commit
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787
788     /**
789      * Gets the number of cached records.
790      * <p>
791      * <em>If using paging, this may not be the total size of the dataset. If the data object
792      * used by the Reader contains the dataset size, then the getTotalCount() function returns
793      * the data set size</em>
794      */
795     getCount : function(){
796         return this.data.length || 0;
797     },
798
799     /**
800      * Gets the total number of records in the dataset as returned by the server.
801      * <p>
802      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
803      * the dataset size</em>
804      */
805     getTotalCount : function(){
806         return this.totalLength || 0;
807     },
808
809     /**
810      * Returns the sort state of the Store as an object with two properties:
811      * <pre><code>
812  field {String} The name of the field by which the Records are sorted
813  direction {String} The sort order, "ASC" or "DESC"
814      * </code></pre>
815      */
816     getSortState : function(){
817         return this.sortInfo;
818     },
819
820     // private
821     applySort : function(){
822         if(this.sortInfo && !this.remoteSort){
823             var s = this.sortInfo, f = s.field;
824             var st = this.fields.get(f).sortType;
825             var fn = function(r1, r2){
826                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
827                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
828             };
829             this.data.sort(s.direction, fn);
830             if(this.snapshot && this.snapshot != this.data){
831                 this.snapshot.sort(s.direction, fn);
832             }
833         }
834     },
835
836     /**
837      * Sets the default sort column and order to be used by the next load operation.
838      * @param {String} fieldName The name of the field to sort by.
839      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
840      */
841     setDefaultSort : function(field, dir){
842         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
843     },
844
845     /**
846      * Sort the Records.
847      * If remote sorting is used, the sort is performed on the server, and the cache is
848      * reloaded. If local sorting is used, the cache is sorted internally.
849      * @param {String} fieldName The name of the field to sort by.
850      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
851      */
852     sort : function(fieldName, dir){
853         var f = this.fields.get(fieldName);
854         if(!dir){
855             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
856             
857             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
858                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
859             }else{
860                 dir = f.sortDir;
861             }
862         }
863         this.sortToggle[f.name] = dir;
864         this.sortInfo = {field: f.name, direction: dir};
865         if(!this.remoteSort){
866             this.applySort();
867             this.fireEvent("datachanged", this);
868         }else{
869             this.load(this.lastOptions);
870         }
871     },
872
873     /**
874      * Calls the specified function for each of the Records in the cache.
875      * @param {Function} fn The function to call. The Record is passed as the first parameter.
876      * Returning <em>false</em> aborts and exits the iteration.
877      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
878      */
879     each : function(fn, scope){
880         this.data.each(fn, scope);
881     },
882
883     /**
884      * Gets all records modified since the last commit.  Modified records are persisted across load operations
885      * (e.g., during paging).
886      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
887      */
888     getModifiedRecords : function(){
889         return this.modified;
890     },
891
892     // private
893     createFilterFn : function(property, value, anyMatch){
894         if(!value.exec){ // not a regex
895             value = String(value);
896             if(value.length == 0){
897                 return false;
898             }
899             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
900         }
901         return function(r){
902             return value.test(r.data[property]);
903         };
904     },
905
906     /**
907      * Sums the value of <i>property</i> for each record between start and end and returns the result.
908      * @param {String} property A field on your records
909      * @param {Number} start The record index to start at (defaults to 0)
910      * @param {Number} end The last record index to include (defaults to length - 1)
911      * @return {Number} The sum
912      */
913     sum : function(property, start, end){
914         var rs = this.data.items, v = 0;
915         start = start || 0;
916         end = (end || end === 0) ? end : rs.length-1;
917
918         for(var i = start; i <= end; i++){
919             v += (rs[i].data[property] || 0);
920         }
921         return v;
922     },
923
924     /**
925      * Filter the records by a specified property.
926      * @param {String} field A field on your records
927      * @param {String/RegExp} value Either a string that the field
928      * should start with or a RegExp to test against the field
929      * @param {Boolean} anyMatch True to match any part not just the beginning
930      */
931     filter : function(property, value, anyMatch){
932         var fn = this.createFilterFn(property, value, anyMatch);
933         return fn ? this.filterBy(fn) : this.clearFilter();
934     },
935
936     /**
937      * Filter by a function. The specified function will be called with each
938      * record in this data source. If the function returns true the record is included,
939      * otherwise it is filtered.
940      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
941      * @param {Object} scope (optional) The scope of the function (defaults to this)
942      */
943     filterBy : function(fn, scope){
944         this.snapshot = this.snapshot || this.data;
945         this.data = this.queryBy(fn, scope||this);
946         this.fireEvent("datachanged", this);
947     },
948
949     /**
950      * Query the records by a specified property.
951      * @param {String} field A field on your records
952      * @param {String/RegExp} value Either a string that the field
953      * should start with or a RegExp to test against the field
954      * @param {Boolean} anyMatch True to match any part not just the beginning
955      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
956      */
957     query : function(property, value, anyMatch){
958         var fn = this.createFilterFn(property, value, anyMatch);
959         return fn ? this.queryBy(fn) : this.data.clone();
960     },
961
962     /**
963      * Query by a function. The specified function will be called with each
964      * record in this data source. If the function returns true the record is included
965      * in the results.
966      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
967      * @param {Object} scope (optional) The scope of the function (defaults to this)
968       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
969      **/
970     queryBy : function(fn, scope){
971         var data = this.snapshot || this.data;
972         return data.filterBy(fn, scope||this);
973     },
974
975     /**
976      * Collects unique values for a particular dataIndex from this store.
977      * @param {String} dataIndex The property to collect
978      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
979      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
980      * @return {Array} An array of the unique values
981      **/
982     collect : function(dataIndex, allowNull, bypassFilter){
983         var d = (bypassFilter === true && this.snapshot) ?
984                 this.snapshot.items : this.data.items;
985         var v, sv, r = [], l = {};
986         for(var i = 0, len = d.length; i < len; i++){
987             v = d[i].data[dataIndex];
988             sv = String(v);
989             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
990                 l[sv] = true;
991                 r[r.length] = v;
992             }
993         }
994         return r;
995     },
996
997     /**
998      * Revert to a view of the Record cache with no filtering applied.
999      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1000      */
1001     clearFilter : function(suppressEvent){
1002         if(this.snapshot && this.snapshot != this.data){
1003             this.data = this.snapshot;
1004             delete this.snapshot;
1005             if(suppressEvent !== true){
1006                 this.fireEvent("datachanged", this);
1007             }
1008         }
1009     },
1010
1011     // private
1012     afterEdit : function(record){
1013         if(this.modified.indexOf(record) == -1){
1014             this.modified.push(record);
1015         }
1016         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1017     },
1018     
1019     // private
1020     afterReject : function(record){
1021         this.modified.remove(record);
1022         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1023     },
1024
1025     // private
1026     afterCommit : function(record){
1027         this.modified.remove(record);
1028         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1029     },
1030
1031     /**
1032      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1033      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1034      */
1035     commitChanges : function(){
1036         var m = this.modified.slice(0);
1037         this.modified = [];
1038         for(var i = 0, len = m.length; i < len; i++){
1039             m[i].commit();
1040         }
1041     },
1042
1043     /**
1044      * Cancel outstanding changes on all changed records.
1045      */
1046     rejectChanges : function(){
1047         var m = this.modified.slice(0);
1048         this.modified = [];
1049         for(var i = 0, len = m.length; i < len; i++){
1050             m[i].reject();
1051         }
1052     },
1053
1054     onMetaChange : function(meta, rtype, o){
1055         this.recordType = rtype;
1056         this.fields = rtype.prototype.fields;
1057         delete this.snapshot;
1058         this.sortInfo = meta.sortInfo || this.sortInfo;
1059         this.modified = [];
1060         this.fireEvent('metachange', this, this.reader.meta);
1061     },
1062     
1063     moveIndex : function(data, type)
1064     {
1065         var index = this.indexOf(data);
1066         
1067         var newIndex = index + type;
1068         
1069         this.remove(data);
1070         
1071         this.insert(newIndex, data);
1072         
1073     }
1074 });/*
1075  * Based on:
1076  * Ext JS Library 1.1.1
1077  * Copyright(c) 2006-2007, Ext JS, LLC.
1078  *
1079  * Originally Released Under LGPL - original licence link has changed is not relivant.
1080  *
1081  * Fork - LGPL
1082  * <script type="text/javascript">
1083  */
1084
1085 /**
1086  * @class Roo.data.SimpleStore
1087  * @extends Roo.data.Store
1088  * Small helper class to make creating Stores from Array data easier.
1089  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1090  * @cfg {Array} fields An array of field definition objects, or field name strings.
1091  * @cfg {Array} data The multi-dimensional array of data
1092  * @constructor
1093  * @param {Object} config
1094  */
1095 Roo.data.SimpleStore = function(config){
1096     Roo.data.SimpleStore.superclass.constructor.call(this, {
1097         isLocal : true,
1098         reader: new Roo.data.ArrayReader({
1099                 id: config.id
1100             },
1101             Roo.data.Record.create(config.fields)
1102         ),
1103         proxy : new Roo.data.MemoryProxy(config.data)
1104     });
1105     this.load();
1106 };
1107 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1108  * Based on:
1109  * Ext JS Library 1.1.1
1110  * Copyright(c) 2006-2007, Ext JS, LLC.
1111  *
1112  * Originally Released Under LGPL - original licence link has changed is not relivant.
1113  *
1114  * Fork - LGPL
1115  * <script type="text/javascript">
1116  */
1117
1118 /**
1119 /**
1120  * @extends Roo.data.Store
1121  * @class Roo.data.JsonStore
1122  * Small helper class to make creating Stores for JSON data easier. <br/>
1123 <pre><code>
1124 var store = new Roo.data.JsonStore({
1125     url: 'get-images.php',
1126     root: 'images',
1127     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1128 });
1129 </code></pre>
1130  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1131  * JsonReader and HttpProxy (unless inline data is provided).</b>
1132  * @cfg {Array} fields An array of field definition objects, or field name strings.
1133  * @constructor
1134  * @param {Object} config
1135  */
1136 Roo.data.JsonStore = function(c){
1137     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1138         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1139         reader: new Roo.data.JsonReader(c, c.fields)
1140     }));
1141 };
1142 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1143  * Based on:
1144  * Ext JS Library 1.1.1
1145  * Copyright(c) 2006-2007, Ext JS, LLC.
1146  *
1147  * Originally Released Under LGPL - original licence link has changed is not relivant.
1148  *
1149  * Fork - LGPL
1150  * <script type="text/javascript">
1151  */
1152
1153  
1154 Roo.data.Field = function(config){
1155     if(typeof config == "string"){
1156         config = {name: config};
1157     }
1158     Roo.apply(this, config);
1159     
1160     if(!this.type){
1161         this.type = "auto";
1162     }
1163     
1164     var st = Roo.data.SortTypes;
1165     // named sortTypes are supported, here we look them up
1166     if(typeof this.sortType == "string"){
1167         this.sortType = st[this.sortType];
1168     }
1169     
1170     // set default sortType for strings and dates
1171     if(!this.sortType){
1172         switch(this.type){
1173             case "string":
1174                 this.sortType = st.asUCString;
1175                 break;
1176             case "date":
1177                 this.sortType = st.asDate;
1178                 break;
1179             default:
1180                 this.sortType = st.none;
1181         }
1182     }
1183
1184     // define once
1185     var stripRe = /[\$,%]/g;
1186
1187     // prebuilt conversion function for this field, instead of
1188     // switching every time we're reading a value
1189     if(!this.convert){
1190         var cv, dateFormat = this.dateFormat;
1191         switch(this.type){
1192             case "":
1193             case "auto":
1194             case undefined:
1195                 cv = function(v){ return v; };
1196                 break;
1197             case "string":
1198                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1199                 break;
1200             case "int":
1201                 cv = function(v){
1202                     return v !== undefined && v !== null && v !== '' ?
1203                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1204                     };
1205                 break;
1206             case "float":
1207                 cv = function(v){
1208                     return v !== undefined && v !== null && v !== '' ?
1209                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1210                     };
1211                 break;
1212             case "bool":
1213             case "boolean":
1214                 cv = function(v){ return v === true || v === "true" || v == 1; };
1215                 break;
1216             case "date":
1217                 cv = function(v){
1218                     if(!v){
1219                         return '';
1220                     }
1221                     if(v instanceof Date){
1222                         return v;
1223                     }
1224                     if(dateFormat){
1225                         if(dateFormat == "timestamp"){
1226                             return new Date(v*1000);
1227                         }
1228                         return Date.parseDate(v, dateFormat);
1229                     }
1230                     var parsed = Date.parse(v);
1231                     return parsed ? new Date(parsed) : null;
1232                 };
1233              break;
1234             
1235         }
1236         this.convert = cv;
1237     }
1238 };
1239
1240 Roo.data.Field.prototype = {
1241     dateFormat: null,
1242     defaultValue: "",
1243     mapping: null,
1244     sortType : null,
1245     sortDir : "ASC"
1246 };/*
1247  * Based on:
1248  * Ext JS Library 1.1.1
1249  * Copyright(c) 2006-2007, Ext JS, LLC.
1250  *
1251  * Originally Released Under LGPL - original licence link has changed is not relivant.
1252  *
1253  * Fork - LGPL
1254  * <script type="text/javascript">
1255  */
1256  
1257 // Base class for reading structured data from a data source.  This class is intended to be
1258 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1259
1260 /**
1261  * @class Roo.data.DataReader
1262  * Base class for reading structured data from a data source.  This class is intended to be
1263  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1264  */
1265
1266 Roo.data.DataReader = function(meta, recordType){
1267     
1268     this.meta = meta;
1269     
1270     this.recordType = recordType instanceof Array ? 
1271         Roo.data.Record.create(recordType) : recordType;
1272 };
1273
1274 Roo.data.DataReader.prototype = {
1275      /**
1276      * Create an empty record
1277      * @param {Object} data (optional) - overlay some values
1278      * @return {Roo.data.Record} record created.
1279      */
1280     newRow :  function(d) {
1281         var da =  {};
1282         this.recordType.prototype.fields.each(function(c) {
1283             switch( c.type) {
1284                 case 'int' : da[c.name] = 0; break;
1285                 case 'date' : da[c.name] = new Date(); break;
1286                 case 'float' : da[c.name] = 0.0; break;
1287                 case 'boolean' : da[c.name] = false; break;
1288                 default : da[c.name] = ""; break;
1289             }
1290             
1291         });
1292         return new this.recordType(Roo.apply(da, d));
1293     }
1294     
1295 };/*
1296  * Based on:
1297  * Ext JS Library 1.1.1
1298  * Copyright(c) 2006-2007, Ext JS, LLC.
1299  *
1300  * Originally Released Under LGPL - original licence link has changed is not relivant.
1301  *
1302  * Fork - LGPL
1303  * <script type="text/javascript">
1304  */
1305
1306 /**
1307  * @class Roo.data.DataProxy
1308  * @extends Roo.data.Observable
1309  * This class is an abstract base class for implementations which provide retrieval of
1310  * unformatted data objects.<br>
1311  * <p>
1312  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1313  * (of the appropriate type which knows how to parse the data object) to provide a block of
1314  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1315  * <p>
1316  * Custom implementations must implement the load method as described in
1317  * {@link Roo.data.HttpProxy#load}.
1318  */
1319 Roo.data.DataProxy = function(){
1320     this.addEvents({
1321         /**
1322          * @event beforeload
1323          * Fires before a network request is made to retrieve a data object.
1324          * @param {Object} This DataProxy object.
1325          * @param {Object} params The params parameter to the load function.
1326          */
1327         beforeload : true,
1328         /**
1329          * @event load
1330          * Fires before the load method's callback is called.
1331          * @param {Object} This DataProxy object.
1332          * @param {Object} o The data object.
1333          * @param {Object} arg The callback argument object passed to the load function.
1334          */
1335         load : true,
1336         /**
1337          * @event loadexception
1338          * Fires if an Exception occurs during data retrieval.
1339          * @param {Object} This DataProxy object.
1340          * @param {Object} o The data object.
1341          * @param {Object} arg The callback argument object passed to the load function.
1342          * @param {Object} e The Exception.
1343          */
1344         loadexception : true
1345     });
1346     Roo.data.DataProxy.superclass.constructor.call(this);
1347 };
1348
1349 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1350
1351     /**
1352      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1353      */
1354 /*
1355  * Based on:
1356  * Ext JS Library 1.1.1
1357  * Copyright(c) 2006-2007, Ext JS, LLC.
1358  *
1359  * Originally Released Under LGPL - original licence link has changed is not relivant.
1360  *
1361  * Fork - LGPL
1362  * <script type="text/javascript">
1363  */
1364 /**
1365  * @class Roo.data.MemoryProxy
1366  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1367  * to the Reader when its load method is called.
1368  * @constructor
1369  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1370  */
1371 Roo.data.MemoryProxy = function(data){
1372     if (data.data) {
1373         data = data.data;
1374     }
1375     Roo.data.MemoryProxy.superclass.constructor.call(this);
1376     this.data = data;
1377 };
1378
1379 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1380     
1381     /**
1382      * Load data from the requested source (in this case an in-memory
1383      * data object passed to the constructor), read the data object into
1384      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1385      * process that block using the passed callback.
1386      * @param {Object} params This parameter is not used by the MemoryProxy class.
1387      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1388      * object into a block of Roo.data.Records.
1389      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1390      * The function must be passed <ul>
1391      * <li>The Record block object</li>
1392      * <li>The "arg" argument from the load function</li>
1393      * <li>A boolean success indicator</li>
1394      * </ul>
1395      * @param {Object} scope The scope in which to call the callback
1396      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1397      */
1398     load : function(params, reader, callback, scope, arg){
1399         params = params || {};
1400         var result;
1401         try {
1402             result = reader.readRecords(this.data);
1403         }catch(e){
1404             this.fireEvent("loadexception", this, arg, null, e);
1405             callback.call(scope, null, arg, false);
1406             return;
1407         }
1408         callback.call(scope, result, arg, true);
1409     },
1410     
1411     // private
1412     update : function(params, records){
1413         
1414     }
1415 });/*
1416  * Based on:
1417  * Ext JS Library 1.1.1
1418  * Copyright(c) 2006-2007, Ext JS, LLC.
1419  *
1420  * Originally Released Under LGPL - original licence link has changed is not relivant.
1421  *
1422  * Fork - LGPL
1423  * <script type="text/javascript">
1424  */
1425 /**
1426  * @class Roo.data.HttpProxy
1427  * @extends Roo.data.DataProxy
1428  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1429  * configured to reference a certain URL.<br><br>
1430  * <p>
1431  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1432  * from which the running page was served.<br><br>
1433  * <p>
1434  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1435  * <p>
1436  * Be aware that to enable the browser to parse an XML document, the server must set
1437  * the Content-Type header in the HTTP response to "text/xml".
1438  * @constructor
1439  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1440  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1441  * will be used to make the request.
1442  */
1443 Roo.data.HttpProxy = function(conn){
1444     Roo.data.HttpProxy.superclass.constructor.call(this);
1445     // is conn a conn config or a real conn?
1446     this.conn = conn;
1447     this.useAjax = !conn || !conn.events;
1448   
1449 };
1450
1451 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1452     // thse are take from connection...
1453     
1454     /**
1455      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1456      */
1457     /**
1458      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1459      * extra parameters to each request made by this object. (defaults to undefined)
1460      */
1461     /**
1462      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1463      *  to each request made by this object. (defaults to undefined)
1464      */
1465     /**
1466      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
1467      */
1468     /**
1469      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1470      */
1471      /**
1472      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1473      * @type Boolean
1474      */
1475   
1476
1477     /**
1478      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1479      * @type Boolean
1480      */
1481     /**
1482      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1483      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1484      * a finer-grained basis than the DataProxy events.
1485      */
1486     getConnection : function(){
1487         return this.useAjax ? Roo.Ajax : this.conn;
1488     },
1489
1490     /**
1491      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1492      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1493      * process that block using the passed callback.
1494      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1495      * for the request to the remote server.
1496      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1497      * object into a block of Roo.data.Records.
1498      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1499      * The function must be passed <ul>
1500      * <li>The Record block object</li>
1501      * <li>The "arg" argument from the load function</li>
1502      * <li>A boolean success indicator</li>
1503      * </ul>
1504      * @param {Object} scope The scope in which to call the callback
1505      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1506      */
1507     load : function(params, reader, callback, scope, arg){
1508         if(this.fireEvent("beforeload", this, params) !== false){
1509             var  o = {
1510                 params : params || {},
1511                 request: {
1512                     callback : callback,
1513                     scope : scope,
1514                     arg : arg
1515                 },
1516                 reader: reader,
1517                 callback : this.loadResponse,
1518                 scope: this
1519             };
1520             if(this.useAjax){
1521                 Roo.applyIf(o, this.conn);
1522                 if(this.activeRequest){
1523                     Roo.Ajax.abort(this.activeRequest);
1524                 }
1525                 this.activeRequest = Roo.Ajax.request(o);
1526             }else{
1527                 this.conn.request(o);
1528             }
1529         }else{
1530             callback.call(scope||this, null, arg, false);
1531         }
1532     },
1533
1534     // private
1535     loadResponse : function(o, success, response){
1536         delete this.activeRequest;
1537         if(!success){
1538             this.fireEvent("loadexception", this, o, response);
1539             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1540             return;
1541         }
1542         var result;
1543         try {
1544             result = o.reader.read(response);
1545         }catch(e){
1546             this.fireEvent("loadexception", this, o, response, e);
1547             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1548             return;
1549         }
1550         
1551         this.fireEvent("load", this, o, o.request.arg);
1552         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1553     },
1554
1555     // private
1556     update : function(dataSet){
1557
1558     },
1559
1560     // private
1561     updateResponse : function(dataSet){
1562
1563     }
1564 });/*
1565  * Based on:
1566  * Ext JS Library 1.1.1
1567  * Copyright(c) 2006-2007, Ext JS, LLC.
1568  *
1569  * Originally Released Under LGPL - original licence link has changed is not relivant.
1570  *
1571  * Fork - LGPL
1572  * <script type="text/javascript">
1573  */
1574
1575 /**
1576  * @class Roo.data.ScriptTagProxy
1577  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1578  * other than the originating domain of the running page.<br><br>
1579  * <p>
1580  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
1581  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1582  * <p>
1583  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1584  * source code that is used as the source inside a &lt;script> tag.<br><br>
1585  * <p>
1586  * In order for the browser to process the returned data, the server must wrap the data object
1587  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1588  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1589  * depending on whether the callback name was passed:
1590  * <p>
1591  * <pre><code>
1592 boolean scriptTag = false;
1593 String cb = request.getParameter("callback");
1594 if (cb != null) {
1595     scriptTag = true;
1596     response.setContentType("text/javascript");
1597 } else {
1598     response.setContentType("application/x-json");
1599 }
1600 Writer out = response.getWriter();
1601 if (scriptTag) {
1602     out.write(cb + "(");
1603 }
1604 out.print(dataBlock.toJsonString());
1605 if (scriptTag) {
1606     out.write(");");
1607 }
1608 </pre></code>
1609  *
1610  * @constructor
1611  * @param {Object} config A configuration object.
1612  */
1613 Roo.data.ScriptTagProxy = function(config){
1614     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1615     Roo.apply(this, config);
1616     this.head = document.getElementsByTagName("head")[0];
1617 };
1618
1619 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1620
1621 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1622     /**
1623      * @cfg {String} url The URL from which to request the data object.
1624      */
1625     /**
1626      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1627      */
1628     timeout : 30000,
1629     /**
1630      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1631      * the server the name of the callback function set up by the load call to process the returned data object.
1632      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1633      * javascript output which calls this named function passing the data object as its only parameter.
1634      */
1635     callbackParam : "callback",
1636     /**
1637      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1638      * name to the request.
1639      */
1640     nocache : true,
1641
1642     /**
1643      * Load data from the configured URL, read the data object into
1644      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1645      * process that block using the passed callback.
1646      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1647      * for the request to the remote server.
1648      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1649      * object into a block of Roo.data.Records.
1650      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1651      * The function must be passed <ul>
1652      * <li>The Record block object</li>
1653      * <li>The "arg" argument from the load function</li>
1654      * <li>A boolean success indicator</li>
1655      * </ul>
1656      * @param {Object} scope The scope in which to call the callback
1657      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1658      */
1659     load : function(params, reader, callback, scope, arg){
1660         if(this.fireEvent("beforeload", this, params) !== false){
1661
1662             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1663
1664             var url = this.url;
1665             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1666             if(this.nocache){
1667                 url += "&_dc=" + (new Date().getTime());
1668             }
1669             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1670             var trans = {
1671                 id : transId,
1672                 cb : "stcCallback"+transId,
1673                 scriptId : "stcScript"+transId,
1674                 params : params,
1675                 arg : arg,
1676                 url : url,
1677                 callback : callback,
1678                 scope : scope,
1679                 reader : reader
1680             };
1681             var conn = this;
1682
1683             window[trans.cb] = function(o){
1684                 conn.handleResponse(o, trans);
1685             };
1686
1687             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1688
1689             if(this.autoAbort !== false){
1690                 this.abort();
1691             }
1692
1693             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1694
1695             var script = document.createElement("script");
1696             script.setAttribute("src", url);
1697             script.setAttribute("type", "text/javascript");
1698             script.setAttribute("id", trans.scriptId);
1699             this.head.appendChild(script);
1700
1701             this.trans = trans;
1702         }else{
1703             callback.call(scope||this, null, arg, false);
1704         }
1705     },
1706
1707     // private
1708     isLoading : function(){
1709         return this.trans ? true : false;
1710     },
1711
1712     /**
1713      * Abort the current server request.
1714      */
1715     abort : function(){
1716         if(this.isLoading()){
1717             this.destroyTrans(this.trans);
1718         }
1719     },
1720
1721     // private
1722     destroyTrans : function(trans, isLoaded){
1723         this.head.removeChild(document.getElementById(trans.scriptId));
1724         clearTimeout(trans.timeoutId);
1725         if(isLoaded){
1726             window[trans.cb] = undefined;
1727             try{
1728                 delete window[trans.cb];
1729             }catch(e){}
1730         }else{
1731             // if hasn't been loaded, wait for load to remove it to prevent script error
1732             window[trans.cb] = function(){
1733                 window[trans.cb] = undefined;
1734                 try{
1735                     delete window[trans.cb];
1736                 }catch(e){}
1737             };
1738         }
1739     },
1740
1741     // private
1742     handleResponse : function(o, trans){
1743         this.trans = false;
1744         this.destroyTrans(trans, true);
1745         var result;
1746         try {
1747             result = trans.reader.readRecords(o);
1748         }catch(e){
1749             this.fireEvent("loadexception", this, o, trans.arg, e);
1750             trans.callback.call(trans.scope||window, null, trans.arg, false);
1751             return;
1752         }
1753         this.fireEvent("load", this, o, trans.arg);
1754         trans.callback.call(trans.scope||window, result, trans.arg, true);
1755     },
1756
1757     // private
1758     handleFailure : function(trans){
1759         this.trans = false;
1760         this.destroyTrans(trans, false);
1761         this.fireEvent("loadexception", this, null, trans.arg);
1762         trans.callback.call(trans.scope||window, null, trans.arg, false);
1763     }
1764 });/*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 /**
1776  * @class Roo.data.JsonReader
1777  * @extends Roo.data.DataReader
1778  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1779  * based on mappings in a provided Roo.data.Record constructor.
1780  * 
1781  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1782  * in the reply previously. 
1783  * 
1784  * <p>
1785  * Example code:
1786  * <pre><code>
1787 var RecordDef = Roo.data.Record.create([
1788     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1789     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1790 ]);
1791 var myReader = new Roo.data.JsonReader({
1792     totalProperty: "results",    // The property which contains the total dataset size (optional)
1793     root: "rows",                // The property which contains an Array of row objects
1794     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1795 }, RecordDef);
1796 </code></pre>
1797  * <p>
1798  * This would consume a JSON file like this:
1799  * <pre><code>
1800 { 'results': 2, 'rows': [
1801     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1802     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1803 }
1804 </code></pre>
1805  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1806  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1807  * paged from the remote server.
1808  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1809  * @cfg {String} root name of the property which contains the Array of row objects.
1810  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1811  * @cfg {Array} fields Array of field definition objects
1812  * @constructor
1813  * Create a new JsonReader
1814  * @param {Object} meta Metadata configuration options
1815  * @param {Object} recordType Either an Array of field definition objects,
1816  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1817  */
1818 Roo.data.JsonReader = function(meta, recordType){
1819     
1820     meta = meta || {};
1821     // set some defaults:
1822     Roo.applyIf(meta, {
1823         totalProperty: 'total',
1824         successProperty : 'success',
1825         root : 'data',
1826         id : 'id'
1827     });
1828     
1829     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1830 };
1831 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1832     
1833     /**
1834      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1835      * Used by Store query builder to append _requestMeta to params.
1836      * 
1837      */
1838     metaFromRemote : false,
1839     /**
1840      * This method is only used by a DataProxy which has retrieved data from a remote server.
1841      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1842      * @return {Object} data A data block which is used by an Roo.data.Store object as
1843      * a cache of Roo.data.Records.
1844      */
1845     read : function(response){
1846         var json = response.responseText;
1847        
1848         var o = /* eval:var:o */ eval("("+json+")");
1849         if(!o) {
1850             throw {message: "JsonReader.read: Json object not found"};
1851         }
1852         
1853         if(o.metaData){
1854             
1855             delete this.ef;
1856             this.metaFromRemote = true;
1857             this.meta = o.metaData;
1858             this.recordType = Roo.data.Record.create(o.metaData.fields);
1859             this.onMetaChange(this.meta, this.recordType, o);
1860         }
1861         return this.readRecords(o);
1862     },
1863
1864     // private function a store will implement
1865     onMetaChange : function(meta, recordType, o){
1866
1867     },
1868
1869     /**
1870          * @ignore
1871          */
1872     simpleAccess: function(obj, subsc) {
1873         return obj[subsc];
1874     },
1875
1876         /**
1877          * @ignore
1878          */
1879     getJsonAccessor: function(){
1880         var re = /[\[\.]/;
1881         return function(expr) {
1882             try {
1883                 return(re.test(expr))
1884                     ? new Function("obj", "return obj." + expr)
1885                     : function(obj){
1886                         return obj[expr];
1887                     };
1888             } catch(e){}
1889             return Roo.emptyFn;
1890         };
1891     }(),
1892
1893     /**
1894      * Create a data block containing Roo.data.Records from an XML document.
1895      * @param {Object} o An object which contains an Array of row objects in the property specified
1896      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1897      * which contains the total size of the dataset.
1898      * @return {Object} data A data block which is used by an Roo.data.Store object as
1899      * a cache of Roo.data.Records.
1900      */
1901     readRecords : function(o){
1902         /**
1903          * After any data loads, the raw JSON data is available for further custom processing.
1904          * @type Object
1905          */
1906         this.o = o;
1907         var s = this.meta, Record = this.recordType,
1908             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1909
1910 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1911         if (!this.ef) {
1912             if(s.totalProperty) {
1913                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1914                 }
1915                 if(s.successProperty) {
1916                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1917                 }
1918                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1919                 if (s.id) {
1920                         var g = this.getJsonAccessor(s.id);
1921                         this.getId = function(rec) {
1922                                 var r = g(rec);  
1923                                 return (r === undefined || r === "") ? null : r;
1924                         };
1925                 } else {
1926                         this.getId = function(){return null;};
1927                 }
1928             this.ef = [];
1929             for(var jj = 0; jj < fl; jj++){
1930                 f = fi[jj];
1931                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1932                 this.ef[jj] = this.getJsonAccessor(map);
1933             }
1934         }
1935
1936         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1937         if(s.totalProperty){
1938             var vt = parseInt(this.getTotal(o), 10);
1939             if(!isNaN(vt)){
1940                 totalRecords = vt;
1941             }
1942         }
1943         if(s.successProperty){
1944             var vs = this.getSuccess(o);
1945             if(vs === false || vs === 'false'){
1946                 success = false;
1947             }
1948         }
1949         var records = [];
1950         for(var i = 0; i < c; i++){
1951                 var n = root[i];
1952             var values = {};
1953             var id = this.getId(n);
1954             for(var j = 0; j < fl; j++){
1955                 f = fi[j];
1956             var v = this.ef[j](n);
1957             if (!f.convert) {
1958                 Roo.log('missing convert for ' + f.name);
1959                 Roo.log(f);
1960                 continue;
1961             }
1962             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1963             }
1964             var record = new Record(values, id);
1965             record.json = n;
1966             records[i] = record;
1967         }
1968         return {
1969             raw : o,
1970             success : success,
1971             records : records,
1972             totalRecords : totalRecords
1973         };
1974     }
1975 });/*
1976  * Based on:
1977  * Ext JS Library 1.1.1
1978  * Copyright(c) 2006-2007, Ext JS, LLC.
1979  *
1980  * Originally Released Under LGPL - original licence link has changed is not relivant.
1981  *
1982  * Fork - LGPL
1983  * <script type="text/javascript">
1984  */
1985
1986 /**
1987  * @class Roo.data.XmlReader
1988  * @extends Roo.data.DataReader
1989  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
1990  * based on mappings in a provided Roo.data.Record constructor.<br><br>
1991  * <p>
1992  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
1993  * header in the HTTP response must be set to "text/xml".</em>
1994  * <p>
1995  * Example code:
1996  * <pre><code>
1997 var RecordDef = Roo.data.Record.create([
1998    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1999    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2000 ]);
2001 var myReader = new Roo.data.XmlReader({
2002    totalRecords: "results", // The element which contains the total dataset size (optional)
2003    record: "row",           // The repeated element which contains row information
2004    id: "id"                 // The element within the row that provides an ID for the record (optional)
2005 }, RecordDef);
2006 </code></pre>
2007  * <p>
2008  * This would consume an XML file like this:
2009  * <pre><code>
2010 &lt;?xml?>
2011 &lt;dataset>
2012  &lt;results>2&lt;/results>
2013  &lt;row>
2014    &lt;id>1&lt;/id>
2015    &lt;name>Bill&lt;/name>
2016    &lt;occupation>Gardener&lt;/occupation>
2017  &lt;/row>
2018  &lt;row>
2019    &lt;id>2&lt;/id>
2020    &lt;name>Ben&lt;/name>
2021    &lt;occupation>Horticulturalist&lt;/occupation>
2022  &lt;/row>
2023 &lt;/dataset>
2024 </code></pre>
2025  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2026  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2027  * paged from the remote server.
2028  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2029  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2030  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2031  * a record identifier value.
2032  * @constructor
2033  * Create a new XmlReader
2034  * @param {Object} meta Metadata configuration options
2035  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2036  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2037  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2038  */
2039 Roo.data.XmlReader = function(meta, recordType){
2040     meta = meta || {};
2041     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2042 };
2043 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2044     /**
2045      * This method is only used by a DataProxy which has retrieved data from a remote server.
2046          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2047          * to contain a method called 'responseXML' that returns an XML document object.
2048      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2049      * a cache of Roo.data.Records.
2050      */
2051     read : function(response){
2052         var doc = response.responseXML;
2053         if(!doc) {
2054             throw {message: "XmlReader.read: XML Document not available"};
2055         }
2056         return this.readRecords(doc);
2057     },
2058
2059     /**
2060      * Create a data block containing Roo.data.Records from an XML document.
2061          * @param {Object} doc A parsed XML document.
2062      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2063      * a cache of Roo.data.Records.
2064      */
2065     readRecords : function(doc){
2066         /**
2067          * After any data loads/reads, the raw XML Document is available for further custom processing.
2068          * @type XMLDocument
2069          */
2070         this.xmlData = doc;
2071         var root = doc.documentElement || doc;
2072         var q = Roo.DomQuery;
2073         var recordType = this.recordType, fields = recordType.prototype.fields;
2074         var sid = this.meta.id;
2075         var totalRecords = 0, success = true;
2076         if(this.meta.totalRecords){
2077             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2078         }
2079         
2080         if(this.meta.success){
2081             var sv = q.selectValue(this.meta.success, root, true);
2082             success = sv !== false && sv !== 'false';
2083         }
2084         var records = [];
2085         var ns = q.select(this.meta.record, root);
2086         for(var i = 0, len = ns.length; i < len; i++) {
2087                 var n = ns[i];
2088                 var values = {};
2089                 var id = sid ? q.selectValue(sid, n) : undefined;
2090                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2091                     var f = fields.items[j];
2092                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2093                     v = f.convert(v);
2094                     values[f.name] = v;
2095                 }
2096                 var record = new recordType(values, id);
2097                 record.node = n;
2098                 records[records.length] = record;
2099             }
2100
2101             return {
2102                 success : success,
2103                 records : records,
2104                 totalRecords : totalRecords || records.length
2105             };
2106     }
2107 });/*
2108  * Based on:
2109  * Ext JS Library 1.1.1
2110  * Copyright(c) 2006-2007, Ext JS, LLC.
2111  *
2112  * Originally Released Under LGPL - original licence link has changed is not relivant.
2113  *
2114  * Fork - LGPL
2115  * <script type="text/javascript">
2116  */
2117
2118 /**
2119  * @class Roo.data.ArrayReader
2120  * @extends Roo.data.DataReader
2121  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2122  * Each element of that Array represents a row of data fields. The
2123  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2124  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2125  * <p>
2126  * Example code:.
2127  * <pre><code>
2128 var RecordDef = Roo.data.Record.create([
2129     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2130     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2131 ]);
2132 var myReader = new Roo.data.ArrayReader({
2133     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2134 }, RecordDef);
2135 </code></pre>
2136  * <p>
2137  * This would consume an Array like this:
2138  * <pre><code>
2139 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2140   </code></pre>
2141  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
2142  * @constructor
2143  * Create a new JsonReader
2144  * @param {Object} meta Metadata configuration options.
2145  * @param {Object} recordType Either an Array of field definition objects
2146  * as specified to {@link Roo.data.Record#create},
2147  * or an {@link Roo.data.Record} object
2148  * created using {@link Roo.data.Record#create}.
2149  */
2150 Roo.data.ArrayReader = function(meta, recordType){
2151     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
2152 };
2153
2154 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2155     /**
2156      * Create a data block containing Roo.data.Records from an XML document.
2157      * @param {Object} o An Array of row objects which represents the dataset.
2158      * @return {Object} data A data block which is used by an Roo.data.Store object as
2159      * a cache of Roo.data.Records.
2160      */
2161     readRecords : function(o){
2162         var sid = this.meta ? this.meta.id : null;
2163         var recordType = this.recordType, fields = recordType.prototype.fields;
2164         var records = [];
2165         var root = o;
2166             for(var i = 0; i < root.length; i++){
2167                     var n = root[i];
2168                 var values = {};
2169                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2170                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2171                 var f = fields.items[j];
2172                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2173                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2174                 v = f.convert(v);
2175                 values[f.name] = v;
2176             }
2177                 var record = new recordType(values, id);
2178                 record.json = n;
2179                 records[records.length] = record;
2180             }
2181             return {
2182                 records : records,
2183                 totalRecords : records.length
2184             };
2185     }
2186 });/*
2187  * Based on:
2188  * Ext JS Library 1.1.1
2189  * Copyright(c) 2006-2007, Ext JS, LLC.
2190  *
2191  * Originally Released Under LGPL - original licence link has changed is not relivant.
2192  *
2193  * Fork - LGPL
2194  * <script type="text/javascript">
2195  */
2196
2197
2198 /**
2199  * @class Roo.data.Tree
2200  * @extends Roo.util.Observable
2201  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2202  * in the tree have most standard DOM functionality.
2203  * @constructor
2204  * @param {Node} root (optional) The root node
2205  */
2206 Roo.data.Tree = function(root){
2207    this.nodeHash = {};
2208    /**
2209     * The root node for this tree
2210     * @type Node
2211     */
2212    this.root = null;
2213    if(root){
2214        this.setRootNode(root);
2215    }
2216    this.addEvents({
2217        /**
2218         * @event append
2219         * Fires when a new child node is appended to a node in this tree.
2220         * @param {Tree} tree The owner tree
2221         * @param {Node} parent The parent node
2222         * @param {Node} node The newly appended node
2223         * @param {Number} index The index of the newly appended node
2224         */
2225        "append" : true,
2226        /**
2227         * @event remove
2228         * Fires when a child node is removed from a node in this tree.
2229         * @param {Tree} tree The owner tree
2230         * @param {Node} parent The parent node
2231         * @param {Node} node The child node removed
2232         */
2233        "remove" : true,
2234        /**
2235         * @event move
2236         * Fires when a node is moved to a new location in the tree
2237         * @param {Tree} tree The owner tree
2238         * @param {Node} node The node moved
2239         * @param {Node} oldParent The old parent of this node
2240         * @param {Node} newParent The new parent of this node
2241         * @param {Number} index The index it was moved to
2242         */
2243        "move" : true,
2244        /**
2245         * @event insert
2246         * Fires when a new child node is inserted in a node in this tree.
2247         * @param {Tree} tree The owner tree
2248         * @param {Node} parent The parent node
2249         * @param {Node} node The child node inserted
2250         * @param {Node} refNode The child node the node was inserted before
2251         */
2252        "insert" : true,
2253        /**
2254         * @event beforeappend
2255         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2256         * @param {Tree} tree The owner tree
2257         * @param {Node} parent The parent node
2258         * @param {Node} node The child node to be appended
2259         */
2260        "beforeappend" : true,
2261        /**
2262         * @event beforeremove
2263         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2264         * @param {Tree} tree The owner tree
2265         * @param {Node} parent The parent node
2266         * @param {Node} node The child node to be removed
2267         */
2268        "beforeremove" : true,
2269        /**
2270         * @event beforemove
2271         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2272         * @param {Tree} tree The owner tree
2273         * @param {Node} node The node being moved
2274         * @param {Node} oldParent The parent of the node
2275         * @param {Node} newParent The new parent the node is moving to
2276         * @param {Number} index The index it is being moved to
2277         */
2278        "beforemove" : true,
2279        /**
2280         * @event beforeinsert
2281         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2282         * @param {Tree} tree The owner tree
2283         * @param {Node} parent The parent node
2284         * @param {Node} node The child node to be inserted
2285         * @param {Node} refNode The child node the node is being inserted before
2286         */
2287        "beforeinsert" : true
2288    });
2289
2290     Roo.data.Tree.superclass.constructor.call(this);
2291 };
2292
2293 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2294     pathSeparator: "/",
2295
2296     proxyNodeEvent : function(){
2297         return this.fireEvent.apply(this, arguments);
2298     },
2299
2300     /**
2301      * Returns the root node for this tree.
2302      * @return {Node}
2303      */
2304     getRootNode : function(){
2305         return this.root;
2306     },
2307
2308     /**
2309      * Sets the root node for this tree.
2310      * @param {Node} node
2311      * @return {Node}
2312      */
2313     setRootNode : function(node){
2314         this.root = node;
2315         node.ownerTree = this;
2316         node.isRoot = true;
2317         this.registerNode(node);
2318         return node;
2319     },
2320
2321     /**
2322      * Gets a node in this tree by its id.
2323      * @param {String} id
2324      * @return {Node}
2325      */
2326     getNodeById : function(id){
2327         return this.nodeHash[id];
2328     },
2329
2330     registerNode : function(node){
2331         this.nodeHash[node.id] = node;
2332     },
2333
2334     unregisterNode : function(node){
2335         delete this.nodeHash[node.id];
2336     },
2337
2338     toString : function(){
2339         return "[Tree"+(this.id?" "+this.id:"")+"]";
2340     }
2341 });
2342
2343 /**
2344  * @class Roo.data.Node
2345  * @extends Roo.util.Observable
2346  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2347  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2348  * @constructor
2349  * @param {Object} attributes The attributes/config for the node
2350  */
2351 Roo.data.Node = function(attributes){
2352     /**
2353      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2354      * @type {Object}
2355      */
2356     this.attributes = attributes || {};
2357     this.leaf = this.attributes.leaf;
2358     /**
2359      * The node id. @type String
2360      */
2361     this.id = this.attributes.id;
2362     if(!this.id){
2363         this.id = Roo.id(null, "ynode-");
2364         this.attributes.id = this.id;
2365     }
2366      
2367     
2368     /**
2369      * All child nodes of this node. @type Array
2370      */
2371     this.childNodes = [];
2372     if(!this.childNodes.indexOf){ // indexOf is a must
2373         this.childNodes.indexOf = function(o){
2374             for(var i = 0, len = this.length; i < len; i++){
2375                 if(this[i] == o) {
2376                     return i;
2377                 }
2378             }
2379             return -1;
2380         };
2381     }
2382     /**
2383      * The parent node for this node. @type Node
2384      */
2385     this.parentNode = null;
2386     /**
2387      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2388      */
2389     this.firstChild = null;
2390     /**
2391      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2392      */
2393     this.lastChild = null;
2394     /**
2395      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2396      */
2397     this.previousSibling = null;
2398     /**
2399      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2400      */
2401     this.nextSibling = null;
2402
2403     this.addEvents({
2404        /**
2405         * @event append
2406         * Fires when a new child node is appended
2407         * @param {Tree} tree The owner tree
2408         * @param {Node} this This node
2409         * @param {Node} node The newly appended node
2410         * @param {Number} index The index of the newly appended node
2411         */
2412        "append" : true,
2413        /**
2414         * @event remove
2415         * Fires when a child node is removed
2416         * @param {Tree} tree The owner tree
2417         * @param {Node} this This node
2418         * @param {Node} node The removed node
2419         */
2420        "remove" : true,
2421        /**
2422         * @event move
2423         * Fires when this node is moved to a new location in the tree
2424         * @param {Tree} tree The owner tree
2425         * @param {Node} this This node
2426         * @param {Node} oldParent The old parent of this node
2427         * @param {Node} newParent The new parent of this node
2428         * @param {Number} index The index it was moved to
2429         */
2430        "move" : true,
2431        /**
2432         * @event insert
2433         * Fires when a new child node is inserted.
2434         * @param {Tree} tree The owner tree
2435         * @param {Node} this This node
2436         * @param {Node} node The child node inserted
2437         * @param {Node} refNode The child node the node was inserted before
2438         */
2439        "insert" : true,
2440        /**
2441         * @event beforeappend
2442         * Fires before a new child is appended, return false to cancel the append.
2443         * @param {Tree} tree The owner tree
2444         * @param {Node} this This node
2445         * @param {Node} node The child node to be appended
2446         */
2447        "beforeappend" : true,
2448        /**
2449         * @event beforeremove
2450         * Fires before a child is removed, return false to cancel the remove.
2451         * @param {Tree} tree The owner tree
2452         * @param {Node} this This node
2453         * @param {Node} node The child node to be removed
2454         */
2455        "beforeremove" : true,
2456        /**
2457         * @event beforemove
2458         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2459         * @param {Tree} tree The owner tree
2460         * @param {Node} this This node
2461         * @param {Node} oldParent The parent of this node
2462         * @param {Node} newParent The new parent this node is moving to
2463         * @param {Number} index The index it is being moved to
2464         */
2465        "beforemove" : true,
2466        /**
2467         * @event beforeinsert
2468         * Fires before a new child is inserted, return false to cancel the insert.
2469         * @param {Tree} tree The owner tree
2470         * @param {Node} this This node
2471         * @param {Node} node The child node to be inserted
2472         * @param {Node} refNode The child node the node is being inserted before
2473         */
2474        "beforeinsert" : true
2475    });
2476     this.listeners = this.attributes.listeners;
2477     Roo.data.Node.superclass.constructor.call(this);
2478 };
2479
2480 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2481     fireEvent : function(evtName){
2482         // first do standard event for this node
2483         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2484             return false;
2485         }
2486         // then bubble it up to the tree if the event wasn't cancelled
2487         var ot = this.getOwnerTree();
2488         if(ot){
2489             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2490                 return false;
2491             }
2492         }
2493         return true;
2494     },
2495
2496     /**
2497      * Returns true if this node is a leaf
2498      * @return {Boolean}
2499      */
2500     isLeaf : function(){
2501         return this.leaf === true;
2502     },
2503
2504     // private
2505     setFirstChild : function(node){
2506         this.firstChild = node;
2507     },
2508
2509     //private
2510     setLastChild : function(node){
2511         this.lastChild = node;
2512     },
2513
2514
2515     /**
2516      * Returns true if this node is the last child of its parent
2517      * @return {Boolean}
2518      */
2519     isLast : function(){
2520        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2521     },
2522
2523     /**
2524      * Returns true if this node is the first child of its parent
2525      * @return {Boolean}
2526      */
2527     isFirst : function(){
2528        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2529     },
2530
2531     hasChildNodes : function(){
2532         return !this.isLeaf() && this.childNodes.length > 0;
2533     },
2534
2535     /**
2536      * Insert node(s) as the last child node of this node.
2537      * @param {Node/Array} node The node or Array of nodes to append
2538      * @return {Node} The appended node if single append, or null if an array was passed
2539      */
2540     appendChild : function(node){
2541         var multi = false;
2542         if(node instanceof Array){
2543             multi = node;
2544         }else if(arguments.length > 1){
2545             multi = arguments;
2546         }
2547         // if passed an array or multiple args do them one by one
2548         if(multi){
2549             for(var i = 0, len = multi.length; i < len; i++) {
2550                 this.appendChild(multi[i]);
2551             }
2552         }else{
2553             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2554                 return false;
2555             }
2556             var index = this.childNodes.length;
2557             var oldParent = node.parentNode;
2558             // it's a move, make sure we move it cleanly
2559             if(oldParent){
2560                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2561                     return false;
2562                 }
2563                 oldParent.removeChild(node);
2564             }
2565             index = this.childNodes.length;
2566             if(index == 0){
2567                 this.setFirstChild(node);
2568             }
2569             this.childNodes.push(node);
2570             node.parentNode = this;
2571             var ps = this.childNodes[index-1];
2572             if(ps){
2573                 node.previousSibling = ps;
2574                 ps.nextSibling = node;
2575             }else{
2576                 node.previousSibling = null;
2577             }
2578             node.nextSibling = null;
2579             this.setLastChild(node);
2580             node.setOwnerTree(this.getOwnerTree());
2581             this.fireEvent("append", this.ownerTree, this, node, index);
2582             if(oldParent){
2583                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2584             }
2585             return node;
2586         }
2587     },
2588
2589     /**
2590      * Removes a child node from this node.
2591      * @param {Node} node The node to remove
2592      * @return {Node} The removed node
2593      */
2594     removeChild : function(node){
2595         var index = this.childNodes.indexOf(node);
2596         if(index == -1){
2597             return false;
2598         }
2599         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2600             return false;
2601         }
2602
2603         // remove it from childNodes collection
2604         this.childNodes.splice(index, 1);
2605
2606         // update siblings
2607         if(node.previousSibling){
2608             node.previousSibling.nextSibling = node.nextSibling;
2609         }
2610         if(node.nextSibling){
2611             node.nextSibling.previousSibling = node.previousSibling;
2612         }
2613
2614         // update child refs
2615         if(this.firstChild == node){
2616             this.setFirstChild(node.nextSibling);
2617         }
2618         if(this.lastChild == node){
2619             this.setLastChild(node.previousSibling);
2620         }
2621
2622         node.setOwnerTree(null);
2623         // clear any references from the node
2624         node.parentNode = null;
2625         node.previousSibling = null;
2626         node.nextSibling = null;
2627         this.fireEvent("remove", this.ownerTree, this, node);
2628         return node;
2629     },
2630
2631     /**
2632      * Inserts the first node before the second node in this nodes childNodes collection.
2633      * @param {Node} node The node to insert
2634      * @param {Node} refNode The node to insert before (if null the node is appended)
2635      * @return {Node} The inserted node
2636      */
2637     insertBefore : function(node, refNode){
2638         if(!refNode){ // like standard Dom, refNode can be null for append
2639             return this.appendChild(node);
2640         }
2641         // nothing to do
2642         if(node == refNode){
2643             return false;
2644         }
2645
2646         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2647             return false;
2648         }
2649         var index = this.childNodes.indexOf(refNode);
2650         var oldParent = node.parentNode;
2651         var refIndex = index;
2652
2653         // when moving internally, indexes will change after remove
2654         if(oldParent == this && this.childNodes.indexOf(node) < index){
2655             refIndex--;
2656         }
2657
2658         // it's a move, make sure we move it cleanly
2659         if(oldParent){
2660             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2661                 return false;
2662             }
2663             oldParent.removeChild(node);
2664         }
2665         if(refIndex == 0){
2666             this.setFirstChild(node);
2667         }
2668         this.childNodes.splice(refIndex, 0, node);
2669         node.parentNode = this;
2670         var ps = this.childNodes[refIndex-1];
2671         if(ps){
2672             node.previousSibling = ps;
2673             ps.nextSibling = node;
2674         }else{
2675             node.previousSibling = null;
2676         }
2677         node.nextSibling = refNode;
2678         refNode.previousSibling = node;
2679         node.setOwnerTree(this.getOwnerTree());
2680         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2681         if(oldParent){
2682             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2683         }
2684         return node;
2685     },
2686
2687     /**
2688      * Returns the child node at the specified index.
2689      * @param {Number} index
2690      * @return {Node}
2691      */
2692     item : function(index){
2693         return this.childNodes[index];
2694     },
2695
2696     /**
2697      * Replaces one child node in this node with another.
2698      * @param {Node} newChild The replacement node
2699      * @param {Node} oldChild The node to replace
2700      * @return {Node} The replaced node
2701      */
2702     replaceChild : function(newChild, oldChild){
2703         this.insertBefore(newChild, oldChild);
2704         this.removeChild(oldChild);
2705         return oldChild;
2706     },
2707
2708     /**
2709      * Returns the index of a child node
2710      * @param {Node} node
2711      * @return {Number} The index of the node or -1 if it was not found
2712      */
2713     indexOf : function(child){
2714         return this.childNodes.indexOf(child);
2715     },
2716
2717     /**
2718      * Returns the tree this node is in.
2719      * @return {Tree}
2720      */
2721     getOwnerTree : function(){
2722         // if it doesn't have one, look for one
2723         if(!this.ownerTree){
2724             var p = this;
2725             while(p){
2726                 if(p.ownerTree){
2727                     this.ownerTree = p.ownerTree;
2728                     break;
2729                 }
2730                 p = p.parentNode;
2731             }
2732         }
2733         return this.ownerTree;
2734     },
2735
2736     /**
2737      * Returns depth of this node (the root node has a depth of 0)
2738      * @return {Number}
2739      */
2740     getDepth : function(){
2741         var depth = 0;
2742         var p = this;
2743         while(p.parentNode){
2744             ++depth;
2745             p = p.parentNode;
2746         }
2747         return depth;
2748     },
2749
2750     // private
2751     setOwnerTree : function(tree){
2752         // if it's move, we need to update everyone
2753         if(tree != this.ownerTree){
2754             if(this.ownerTree){
2755                 this.ownerTree.unregisterNode(this);
2756             }
2757             this.ownerTree = tree;
2758             var cs = this.childNodes;
2759             for(var i = 0, len = cs.length; i < len; i++) {
2760                 cs[i].setOwnerTree(tree);
2761             }
2762             if(tree){
2763                 tree.registerNode(this);
2764             }
2765         }
2766     },
2767
2768     /**
2769      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2770      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2771      * @return {String} The path
2772      */
2773     getPath : function(attr){
2774         attr = attr || "id";
2775         var p = this.parentNode;
2776         var b = [this.attributes[attr]];
2777         while(p){
2778             b.unshift(p.attributes[attr]);
2779             p = p.parentNode;
2780         }
2781         var sep = this.getOwnerTree().pathSeparator;
2782         return sep + b.join(sep);
2783     },
2784
2785     /**
2786      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2787      * function call will be the scope provided or the current node. The arguments to the function
2788      * will be the args provided or the current node. If the function returns false at any point,
2789      * the bubble is stopped.
2790      * @param {Function} fn The function to call
2791      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2792      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2793      */
2794     bubble : function(fn, scope, args){
2795         var p = this;
2796         while(p){
2797             if(fn.call(scope || p, args || p) === false){
2798                 break;
2799             }
2800             p = p.parentNode;
2801         }
2802     },
2803
2804     /**
2805      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2806      * function call will be the scope provided or the current node. The arguments to the function
2807      * will be the args provided or the current node. If the function returns false at any point,
2808      * the cascade is stopped on that branch.
2809      * @param {Function} fn The function to call
2810      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2811      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2812      */
2813     cascade : function(fn, scope, args){
2814         if(fn.call(scope || this, args || this) !== false){
2815             var cs = this.childNodes;
2816             for(var i = 0, len = cs.length; i < len; i++) {
2817                 cs[i].cascade(fn, scope, args);
2818             }
2819         }
2820     },
2821
2822     /**
2823      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2824      * function call will be the scope provided or the current node. The arguments to the function
2825      * will be the args provided or the current node. If the function returns false at any point,
2826      * the iteration stops.
2827      * @param {Function} fn The function to call
2828      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2829      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2830      */
2831     eachChild : function(fn, scope, args){
2832         var cs = this.childNodes;
2833         for(var i = 0, len = cs.length; i < len; i++) {
2834                 if(fn.call(scope || this, args || cs[i]) === false){
2835                     break;
2836                 }
2837         }
2838     },
2839
2840     /**
2841      * Finds the first child that has the attribute with the specified value.
2842      * @param {String} attribute The attribute name
2843      * @param {Mixed} value The value to search for
2844      * @return {Node} The found child or null if none was found
2845      */
2846     findChild : function(attribute, value){
2847         var cs = this.childNodes;
2848         for(var i = 0, len = cs.length; i < len; i++) {
2849                 if(cs[i].attributes[attribute] == value){
2850                     return cs[i];
2851                 }
2852         }
2853         return null;
2854     },
2855
2856     /**
2857      * Finds the first child by a custom function. The child matches if the function passed
2858      * returns true.
2859      * @param {Function} fn
2860      * @param {Object} scope (optional)
2861      * @return {Node} The found child or null if none was found
2862      */
2863     findChildBy : function(fn, scope){
2864         var cs = this.childNodes;
2865         for(var i = 0, len = cs.length; i < len; i++) {
2866                 if(fn.call(scope||cs[i], cs[i]) === true){
2867                     return cs[i];
2868                 }
2869         }
2870         return null;
2871     },
2872
2873     /**
2874      * Sorts this nodes children using the supplied sort function
2875      * @param {Function} fn
2876      * @param {Object} scope (optional)
2877      */
2878     sort : function(fn, scope){
2879         var cs = this.childNodes;
2880         var len = cs.length;
2881         if(len > 0){
2882             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2883             cs.sort(sortFn);
2884             for(var i = 0; i < len; i++){
2885                 var n = cs[i];
2886                 n.previousSibling = cs[i-1];
2887                 n.nextSibling = cs[i+1];
2888                 if(i == 0){
2889                     this.setFirstChild(n);
2890                 }
2891                 if(i == len-1){
2892                     this.setLastChild(n);
2893                 }
2894             }
2895         }
2896     },
2897
2898     /**
2899      * Returns true if this node is an ancestor (at any point) of the passed node.
2900      * @param {Node} node
2901      * @return {Boolean}
2902      */
2903     contains : function(node){
2904         return node.isAncestor(this);
2905     },
2906
2907     /**
2908      * Returns true if the passed node is an ancestor (at any point) of this node.
2909      * @param {Node} node
2910      * @return {Boolean}
2911      */
2912     isAncestor : function(node){
2913         var p = this.parentNode;
2914         while(p){
2915             if(p == node){
2916                 return true;
2917             }
2918             p = p.parentNode;
2919         }
2920         return false;
2921     },
2922
2923     toString : function(){
2924         return "[Node"+(this.id?" "+this.id:"")+"]";
2925     }
2926 });/*
2927  * Based on:
2928  * Ext JS Library 1.1.1
2929  * Copyright(c) 2006-2007, Ext JS, LLC.
2930  *
2931  * Originally Released Under LGPL - original licence link has changed is not relivant.
2932  *
2933  * Fork - LGPL
2934  * <script type="text/javascript">
2935  */
2936  (function(){ 
2937 /**
2938  * @class Roo.Layer
2939  * @extends Roo.Element
2940  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2941  * automatic maintaining of shadow/shim positions.
2942  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2943  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2944  * you can pass a string with a CSS class name. False turns off the shadow.
2945  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2946  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2947  * @cfg {String} cls CSS class to add to the element
2948  * @cfg {Number} zindex Starting z-index (defaults to 11000)
2949  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2950  * @constructor
2951  * @param {Object} config An object with config options.
2952  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2953  */
2954
2955 Roo.Layer = function(config, existingEl){
2956     config = config || {};
2957     var dh = Roo.DomHelper;
2958     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2959     if(existingEl){
2960         this.dom = Roo.getDom(existingEl);
2961     }
2962     if(!this.dom){
2963         var o = config.dh || {tag: "div", cls: "x-layer"};
2964         this.dom = dh.append(pel, o);
2965     }
2966     if(config.cls){
2967         this.addClass(config.cls);
2968     }
2969     this.constrain = config.constrain !== false;
2970     this.visibilityMode = Roo.Element.VISIBILITY;
2971     if(config.id){
2972         this.id = this.dom.id = config.id;
2973     }else{
2974         this.id = Roo.id(this.dom);
2975     }
2976     this.zindex = config.zindex || this.getZIndex();
2977     this.position("absolute", this.zindex);
2978     if(config.shadow){
2979         this.shadowOffset = config.shadowOffset || 4;
2980         this.shadow = new Roo.Shadow({
2981             offset : this.shadowOffset,
2982             mode : config.shadow
2983         });
2984     }else{
2985         this.shadowOffset = 0;
2986     }
2987     this.useShim = config.shim !== false && Roo.useShims;
2988     this.useDisplay = config.useDisplay;
2989     this.hide();
2990 };
2991
2992 var supr = Roo.Element.prototype;
2993
2994 // shims are shared among layer to keep from having 100 iframes
2995 var shims = [];
2996
2997 Roo.extend(Roo.Layer, Roo.Element, {
2998
2999     getZIndex : function(){
3000         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3001     },
3002
3003     getShim : function(){
3004         if(!this.useShim){
3005             return null;
3006         }
3007         if(this.shim){
3008             return this.shim;
3009         }
3010         var shim = shims.shift();
3011         if(!shim){
3012             shim = this.createShim();
3013             shim.enableDisplayMode('block');
3014             shim.dom.style.display = 'none';
3015             shim.dom.style.visibility = 'visible';
3016         }
3017         var pn = this.dom.parentNode;
3018         if(shim.dom.parentNode != pn){
3019             pn.insertBefore(shim.dom, this.dom);
3020         }
3021         shim.setStyle('z-index', this.getZIndex()-2);
3022         this.shim = shim;
3023         return shim;
3024     },
3025
3026     hideShim : function(){
3027         if(this.shim){
3028             this.shim.setDisplayed(false);
3029             shims.push(this.shim);
3030             delete this.shim;
3031         }
3032     },
3033
3034     disableShadow : function(){
3035         if(this.shadow){
3036             this.shadowDisabled = true;
3037             this.shadow.hide();
3038             this.lastShadowOffset = this.shadowOffset;
3039             this.shadowOffset = 0;
3040         }
3041     },
3042
3043     enableShadow : function(show){
3044         if(this.shadow){
3045             this.shadowDisabled = false;
3046             this.shadowOffset = this.lastShadowOffset;
3047             delete this.lastShadowOffset;
3048             if(show){
3049                 this.sync(true);
3050             }
3051         }
3052     },
3053
3054     // private
3055     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3056     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3057     sync : function(doShow){
3058         var sw = this.shadow;
3059         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3060             var sh = this.getShim();
3061
3062             var w = this.getWidth(),
3063                 h = this.getHeight();
3064
3065             var l = this.getLeft(true),
3066                 t = this.getTop(true);
3067
3068             if(sw && !this.shadowDisabled){
3069                 if(doShow && !sw.isVisible()){
3070                     sw.show(this);
3071                 }else{
3072                     sw.realign(l, t, w, h);
3073                 }
3074                 if(sh){
3075                     if(doShow){
3076                        sh.show();
3077                     }
3078                     // fit the shim behind the shadow, so it is shimmed too
3079                     var a = sw.adjusts, s = sh.dom.style;
3080                     s.left = (Math.min(l, l+a.l))+"px";
3081                     s.top = (Math.min(t, t+a.t))+"px";
3082                     s.width = (w+a.w)+"px";
3083                     s.height = (h+a.h)+"px";
3084                 }
3085             }else if(sh){
3086                 if(doShow){
3087                    sh.show();
3088                 }
3089                 sh.setSize(w, h);
3090                 sh.setLeftTop(l, t);
3091             }
3092             
3093         }
3094     },
3095
3096     // private
3097     destroy : function(){
3098         this.hideShim();
3099         if(this.shadow){
3100             this.shadow.hide();
3101         }
3102         this.removeAllListeners();
3103         var pn = this.dom.parentNode;
3104         if(pn){
3105             pn.removeChild(this.dom);
3106         }
3107         Roo.Element.uncache(this.id);
3108     },
3109
3110     remove : function(){
3111         this.destroy();
3112     },
3113
3114     // private
3115     beginUpdate : function(){
3116         this.updating = true;
3117     },
3118
3119     // private
3120     endUpdate : function(){
3121         this.updating = false;
3122         this.sync(true);
3123     },
3124
3125     // private
3126     hideUnders : function(negOffset){
3127         if(this.shadow){
3128             this.shadow.hide();
3129         }
3130         this.hideShim();
3131     },
3132
3133     // private
3134     constrainXY : function(){
3135         if(this.constrain){
3136             var vw = Roo.lib.Dom.getViewWidth(),
3137                 vh = Roo.lib.Dom.getViewHeight();
3138             var s = Roo.get(document).getScroll();
3139
3140             var xy = this.getXY();
3141             var x = xy[0], y = xy[1];   
3142             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3143             // only move it if it needs it
3144             var moved = false;
3145             // first validate right/bottom
3146             if((x + w) > vw+s.left){
3147                 x = vw - w - this.shadowOffset;
3148                 moved = true;
3149             }
3150             if((y + h) > vh+s.top){
3151                 y = vh - h - this.shadowOffset;
3152                 moved = true;
3153             }
3154             // then make sure top/left isn't negative
3155             if(x < s.left){
3156                 x = s.left;
3157                 moved = true;
3158             }
3159             if(y < s.top){
3160                 y = s.top;
3161                 moved = true;
3162             }
3163             if(moved){
3164                 if(this.avoidY){
3165                     var ay = this.avoidY;
3166                     if(y <= ay && (y+h) >= ay){
3167                         y = ay-h-5;   
3168                     }
3169                 }
3170                 xy = [x, y];
3171                 this.storeXY(xy);
3172                 supr.setXY.call(this, xy);
3173                 this.sync();
3174             }
3175         }
3176     },
3177
3178     isVisible : function(){
3179         return this.visible;    
3180     },
3181
3182     // private
3183     showAction : function(){
3184         this.visible = true; // track visibility to prevent getStyle calls
3185         if(this.useDisplay === true){
3186             this.setDisplayed("");
3187         }else if(this.lastXY){
3188             supr.setXY.call(this, this.lastXY);
3189         }else if(this.lastLT){
3190             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3191         }
3192     },
3193
3194     // private
3195     hideAction : function(){
3196         this.visible = false;
3197         if(this.useDisplay === true){
3198             this.setDisplayed(false);
3199         }else{
3200             this.setLeftTop(-10000,-10000);
3201         }
3202     },
3203
3204     // overridden Element method
3205     setVisible : function(v, a, d, c, e){
3206         if(v){
3207             this.showAction();
3208         }
3209         if(a && v){
3210             var cb = function(){
3211                 this.sync(true);
3212                 if(c){
3213                     c();
3214                 }
3215             }.createDelegate(this);
3216             supr.setVisible.call(this, true, true, d, cb, e);
3217         }else{
3218             if(!v){
3219                 this.hideUnders(true);
3220             }
3221             var cb = c;
3222             if(a){
3223                 cb = function(){
3224                     this.hideAction();
3225                     if(c){
3226                         c();
3227                     }
3228                 }.createDelegate(this);
3229             }
3230             supr.setVisible.call(this, v, a, d, cb, e);
3231             if(v){
3232                 this.sync(true);
3233             }else if(!a){
3234                 this.hideAction();
3235             }
3236         }
3237     },
3238
3239     storeXY : function(xy){
3240         delete this.lastLT;
3241         this.lastXY = xy;
3242     },
3243
3244     storeLeftTop : function(left, top){
3245         delete this.lastXY;
3246         this.lastLT = [left, top];
3247     },
3248
3249     // private
3250     beforeFx : function(){
3251         this.beforeAction();
3252         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3253     },
3254
3255     // private
3256     afterFx : function(){
3257         Roo.Layer.superclass.afterFx.apply(this, arguments);
3258         this.sync(this.isVisible());
3259     },
3260
3261     // private
3262     beforeAction : function(){
3263         if(!this.updating && this.shadow){
3264             this.shadow.hide();
3265         }
3266     },
3267
3268     // overridden Element method
3269     setLeft : function(left){
3270         this.storeLeftTop(left, this.getTop(true));
3271         supr.setLeft.apply(this, arguments);
3272         this.sync();
3273     },
3274
3275     setTop : function(top){
3276         this.storeLeftTop(this.getLeft(true), top);
3277         supr.setTop.apply(this, arguments);
3278         this.sync();
3279     },
3280
3281     setLeftTop : function(left, top){
3282         this.storeLeftTop(left, top);
3283         supr.setLeftTop.apply(this, arguments);
3284         this.sync();
3285     },
3286
3287     setXY : function(xy, a, d, c, e){
3288         this.fixDisplay();
3289         this.beforeAction();
3290         this.storeXY(xy);
3291         var cb = this.createCB(c);
3292         supr.setXY.call(this, xy, a, d, cb, e);
3293         if(!a){
3294             cb();
3295         }
3296     },
3297
3298     // private
3299     createCB : function(c){
3300         var el = this;
3301         return function(){
3302             el.constrainXY();
3303             el.sync(true);
3304             if(c){
3305                 c();
3306             }
3307         };
3308     },
3309
3310     // overridden Element method
3311     setX : function(x, a, d, c, e){
3312         this.setXY([x, this.getY()], a, d, c, e);
3313     },
3314
3315     // overridden Element method
3316     setY : function(y, a, d, c, e){
3317         this.setXY([this.getX(), y], a, d, c, e);
3318     },
3319
3320     // overridden Element method
3321     setSize : function(w, h, a, d, c, e){
3322         this.beforeAction();
3323         var cb = this.createCB(c);
3324         supr.setSize.call(this, w, h, a, d, cb, e);
3325         if(!a){
3326             cb();
3327         }
3328     },
3329
3330     // overridden Element method
3331     setWidth : function(w, a, d, c, e){
3332         this.beforeAction();
3333         var cb = this.createCB(c);
3334         supr.setWidth.call(this, w, a, d, cb, e);
3335         if(!a){
3336             cb();
3337         }
3338     },
3339
3340     // overridden Element method
3341     setHeight : function(h, a, d, c, e){
3342         this.beforeAction();
3343         var cb = this.createCB(c);
3344         supr.setHeight.call(this, h, a, d, cb, e);
3345         if(!a){
3346             cb();
3347         }
3348     },
3349
3350     // overridden Element method
3351     setBounds : function(x, y, w, h, a, d, c, e){
3352         this.beforeAction();
3353         var cb = this.createCB(c);
3354         if(!a){
3355             this.storeXY([x, y]);
3356             supr.setXY.call(this, [x, y]);
3357             supr.setSize.call(this, w, h, a, d, cb, e);
3358             cb();
3359         }else{
3360             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3361         }
3362         return this;
3363     },
3364     
3365     /**
3366      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3367      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3368      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3369      * @param {Number} zindex The new z-index to set
3370      * @return {this} The Layer
3371      */
3372     setZIndex : function(zindex){
3373         this.zindex = zindex;
3374         this.setStyle("z-index", zindex + 2);
3375         if(this.shadow){
3376             this.shadow.setZIndex(zindex + 1);
3377         }
3378         if(this.shim){
3379             this.shim.setStyle("z-index", zindex);
3380         }
3381     }
3382 });
3383 })();/*
3384  * Based on:
3385  * Ext JS Library 1.1.1
3386  * Copyright(c) 2006-2007, Ext JS, LLC.
3387  *
3388  * Originally Released Under LGPL - original licence link has changed is not relivant.
3389  *
3390  * Fork - LGPL
3391  * <script type="text/javascript">
3392  */
3393
3394
3395 /**
3396  * @class Roo.Shadow
3397  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3398  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3399  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3400  * @constructor
3401  * Create a new Shadow
3402  * @param {Object} config The config object
3403  */
3404 Roo.Shadow = function(config){
3405     Roo.apply(this, config);
3406     if(typeof this.mode != "string"){
3407         this.mode = this.defaultMode;
3408     }
3409     var o = this.offset, a = {h: 0};
3410     var rad = Math.floor(this.offset/2);
3411     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3412         case "drop":
3413             a.w = 0;
3414             a.l = a.t = o;
3415             a.t -= 1;
3416             if(Roo.isIE){
3417                 a.l -= this.offset + rad;
3418                 a.t -= this.offset + rad;
3419                 a.w -= rad;
3420                 a.h -= rad;
3421                 a.t += 1;
3422             }
3423         break;
3424         case "sides":
3425             a.w = (o*2);
3426             a.l = -o;
3427             a.t = o-1;
3428             if(Roo.isIE){
3429                 a.l -= (this.offset - rad);
3430                 a.t -= this.offset + rad;
3431                 a.l += 1;
3432                 a.w -= (this.offset - rad)*2;
3433                 a.w -= rad + 1;
3434                 a.h -= 1;
3435             }
3436         break;
3437         case "frame":
3438             a.w = a.h = (o*2);
3439             a.l = a.t = -o;
3440             a.t += 1;
3441             a.h -= 2;
3442             if(Roo.isIE){
3443                 a.l -= (this.offset - rad);
3444                 a.t -= (this.offset - rad);
3445                 a.l += 1;
3446                 a.w -= (this.offset + rad + 1);
3447                 a.h -= (this.offset + rad);
3448                 a.h += 1;
3449             }
3450         break;
3451     };
3452
3453     this.adjusts = a;
3454 };
3455
3456 Roo.Shadow.prototype = {
3457     /**
3458      * @cfg {String} mode
3459      * The shadow display mode.  Supports the following options:<br />
3460      * sides: Shadow displays on both sides and bottom only<br />
3461      * frame: Shadow displays equally on all four sides<br />
3462      * drop: Traditional bottom-right drop shadow (default)
3463      */
3464     /**
3465      * @cfg {String} offset
3466      * The number of pixels to offset the shadow from the element (defaults to 4)
3467      */
3468     offset: 4,
3469
3470     // private
3471     defaultMode: "drop",
3472
3473     /**
3474      * Displays the shadow under the target element
3475      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3476      */
3477     show : function(target){
3478         target = Roo.get(target);
3479         if(!this.el){
3480             this.el = Roo.Shadow.Pool.pull();
3481             if(this.el.dom.nextSibling != target.dom){
3482                 this.el.insertBefore(target);
3483             }
3484         }
3485         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3486         if(Roo.isIE){
3487             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3488         }
3489         this.realign(
3490             target.getLeft(true),
3491             target.getTop(true),
3492             target.getWidth(),
3493             target.getHeight()
3494         );
3495         this.el.dom.style.display = "block";
3496     },
3497
3498     /**
3499      * Returns true if the shadow is visible, else false
3500      */
3501     isVisible : function(){
3502         return this.el ? true : false;  
3503     },
3504
3505     /**
3506      * Direct alignment when values are already available. Show must be called at least once before
3507      * calling this method to ensure it is initialized.
3508      * @param {Number} left The target element left position
3509      * @param {Number} top The target element top position
3510      * @param {Number} width The target element width
3511      * @param {Number} height The target element height
3512      */
3513     realign : function(l, t, w, h){
3514         if(!this.el){
3515             return;
3516         }
3517         var a = this.adjusts, d = this.el.dom, s = d.style;
3518         var iea = 0;
3519         s.left = (l+a.l)+"px";
3520         s.top = (t+a.t)+"px";
3521         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3522  
3523         if(s.width != sws || s.height != shs){
3524             s.width = sws;
3525             s.height = shs;
3526             if(!Roo.isIE){
3527                 var cn = d.childNodes;
3528                 var sww = Math.max(0, (sw-12))+"px";
3529                 cn[0].childNodes[1].style.width = sww;
3530                 cn[1].childNodes[1].style.width = sww;
3531                 cn[2].childNodes[1].style.width = sww;
3532                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3533             }
3534         }
3535     },
3536
3537     /**
3538      * Hides this shadow
3539      */
3540     hide : function(){
3541         if(this.el){
3542             this.el.dom.style.display = "none";
3543             Roo.Shadow.Pool.push(this.el);
3544             delete this.el;
3545         }
3546     },
3547
3548     /**
3549      * Adjust the z-index of this shadow
3550      * @param {Number} zindex The new z-index
3551      */
3552     setZIndex : function(z){
3553         this.zIndex = z;
3554         if(this.el){
3555             this.el.setStyle("z-index", z);
3556         }
3557     }
3558 };
3559
3560 // Private utility class that manages the internal Shadow cache
3561 Roo.Shadow.Pool = function(){
3562     var p = [];
3563     var markup = Roo.isIE ?
3564                  '<div class="x-ie-shadow"></div>' :
3565                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
3566     return {
3567         pull : function(){
3568             var sh = p.shift();
3569             if(!sh){
3570                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3571                 sh.autoBoxAdjust = false;
3572             }
3573             return sh;
3574         },
3575
3576         push : function(sh){
3577             p.push(sh);
3578         }
3579     };
3580 }();/*
3581  * Based on:
3582  * Ext JS Library 1.1.1
3583  * Copyright(c) 2006-2007, Ext JS, LLC.
3584  *
3585  * Originally Released Under LGPL - original licence link has changed is not relivant.
3586  *
3587  * Fork - LGPL
3588  * <script type="text/javascript">
3589  */
3590
3591
3592 /**
3593  * @class Roo.SplitBar
3594  * @extends Roo.util.Observable
3595  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3596  * <br><br>
3597  * Usage:
3598  * <pre><code>
3599 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3600                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3601 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3602 split.minSize = 100;
3603 split.maxSize = 600;
3604 split.animate = true;
3605 split.on('moved', splitterMoved);
3606 </code></pre>
3607  * @constructor
3608  * Create a new SplitBar
3609  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3610  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3611  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3612  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3613                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3614                         position of the SplitBar).
3615  */
3616 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3617     
3618     /** @private */
3619     this.el = Roo.get(dragElement, true);
3620     this.el.dom.unselectable = "on";
3621     /** @private */
3622     this.resizingEl = Roo.get(resizingElement, true);
3623
3624     /**
3625      * @private
3626      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3627      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3628      * @type Number
3629      */
3630     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3631     
3632     /**
3633      * The minimum size of the resizing element. (Defaults to 0)
3634      * @type Number
3635      */
3636     this.minSize = 0;
3637     
3638     /**
3639      * The maximum size of the resizing element. (Defaults to 2000)
3640      * @type Number
3641      */
3642     this.maxSize = 2000;
3643     
3644     /**
3645      * Whether to animate the transition to the new size
3646      * @type Boolean
3647      */
3648     this.animate = false;
3649     
3650     /**
3651      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3652      * @type Boolean
3653      */
3654     this.useShim = false;
3655     
3656     /** @private */
3657     this.shim = null;
3658     
3659     if(!existingProxy){
3660         /** @private */
3661         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3662     }else{
3663         this.proxy = Roo.get(existingProxy).dom;
3664     }
3665     /** @private */
3666     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3667     
3668     /** @private */
3669     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3670     
3671     /** @private */
3672     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3673     
3674     /** @private */
3675     this.dragSpecs = {};
3676     
3677     /**
3678      * @private The adapter to use to positon and resize elements
3679      */
3680     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3681     this.adapter.init(this);
3682     
3683     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3684         /** @private */
3685         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3686         this.el.addClass("x-splitbar-h");
3687     }else{
3688         /** @private */
3689         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3690         this.el.addClass("x-splitbar-v");
3691     }
3692     
3693     this.addEvents({
3694         /**
3695          * @event resize
3696          * Fires when the splitter is moved (alias for {@link #event-moved})
3697          * @param {Roo.SplitBar} this
3698          * @param {Number} newSize the new width or height
3699          */
3700         "resize" : true,
3701         /**
3702          * @event moved
3703          * Fires when the splitter is moved
3704          * @param {Roo.SplitBar} this
3705          * @param {Number} newSize the new width or height
3706          */
3707         "moved" : true,
3708         /**
3709          * @event beforeresize
3710          * Fires before the splitter is dragged
3711          * @param {Roo.SplitBar} this
3712          */
3713         "beforeresize" : true,
3714
3715         "beforeapply" : true
3716     });
3717
3718     Roo.util.Observable.call(this);
3719 };
3720
3721 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3722     onStartProxyDrag : function(x, y){
3723         this.fireEvent("beforeresize", this);
3724         if(!this.overlay){
3725             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3726             o.unselectable();
3727             o.enableDisplayMode("block");
3728             // all splitbars share the same overlay
3729             Roo.SplitBar.prototype.overlay = o;
3730         }
3731         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3732         this.overlay.show();
3733         Roo.get(this.proxy).setDisplayed("block");
3734         var size = this.adapter.getElementSize(this);
3735         this.activeMinSize = this.getMinimumSize();;
3736         this.activeMaxSize = this.getMaximumSize();;
3737         var c1 = size - this.activeMinSize;
3738         var c2 = Math.max(this.activeMaxSize - size, 0);
3739         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3740             this.dd.resetConstraints();
3741             this.dd.setXConstraint(
3742                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3743                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3744             );
3745             this.dd.setYConstraint(0, 0);
3746         }else{
3747             this.dd.resetConstraints();
3748             this.dd.setXConstraint(0, 0);
3749             this.dd.setYConstraint(
3750                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3751                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3752             );
3753          }
3754         this.dragSpecs.startSize = size;
3755         this.dragSpecs.startPoint = [x, y];
3756         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3757     },
3758     
3759     /** 
3760      * @private Called after the drag operation by the DDProxy
3761      */
3762     onEndProxyDrag : function(e){
3763         Roo.get(this.proxy).setDisplayed(false);
3764         var endPoint = Roo.lib.Event.getXY(e);
3765         if(this.overlay){
3766             this.overlay.hide();
3767         }
3768         var newSize;
3769         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3770             newSize = this.dragSpecs.startSize + 
3771                 (this.placement == Roo.SplitBar.LEFT ?
3772                     endPoint[0] - this.dragSpecs.startPoint[0] :
3773                     this.dragSpecs.startPoint[0] - endPoint[0]
3774                 );
3775         }else{
3776             newSize = this.dragSpecs.startSize + 
3777                 (this.placement == Roo.SplitBar.TOP ?
3778                     endPoint[1] - this.dragSpecs.startPoint[1] :
3779                     this.dragSpecs.startPoint[1] - endPoint[1]
3780                 );
3781         }
3782         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3783         if(newSize != this.dragSpecs.startSize){
3784             if(this.fireEvent('beforeapply', this, newSize) !== false){
3785                 this.adapter.setElementSize(this, newSize);
3786                 this.fireEvent("moved", this, newSize);
3787                 this.fireEvent("resize", this, newSize);
3788             }
3789         }
3790     },
3791     
3792     /**
3793      * Get the adapter this SplitBar uses
3794      * @return The adapter object
3795      */
3796     getAdapter : function(){
3797         return this.adapter;
3798     },
3799     
3800     /**
3801      * Set the adapter this SplitBar uses
3802      * @param {Object} adapter A SplitBar adapter object
3803      */
3804     setAdapter : function(adapter){
3805         this.adapter = adapter;
3806         this.adapter.init(this);
3807     },
3808     
3809     /**
3810      * Gets the minimum size for the resizing element
3811      * @return {Number} The minimum size
3812      */
3813     getMinimumSize : function(){
3814         return this.minSize;
3815     },
3816     
3817     /**
3818      * Sets the minimum size for the resizing element
3819      * @param {Number} minSize The minimum size
3820      */
3821     setMinimumSize : function(minSize){
3822         this.minSize = minSize;
3823     },
3824     
3825     /**
3826      * Gets the maximum size for the resizing element
3827      * @return {Number} The maximum size
3828      */
3829     getMaximumSize : function(){
3830         return this.maxSize;
3831     },
3832     
3833     /**
3834      * Sets the maximum size for the resizing element
3835      * @param {Number} maxSize The maximum size
3836      */
3837     setMaximumSize : function(maxSize){
3838         this.maxSize = maxSize;
3839     },
3840     
3841     /**
3842      * Sets the initialize size for the resizing element
3843      * @param {Number} size The initial size
3844      */
3845     setCurrentSize : function(size){
3846         var oldAnimate = this.animate;
3847         this.animate = false;
3848         this.adapter.setElementSize(this, size);
3849         this.animate = oldAnimate;
3850     },
3851     
3852     /**
3853      * Destroy this splitbar. 
3854      * @param {Boolean} removeEl True to remove the element
3855      */
3856     destroy : function(removeEl){
3857         if(this.shim){
3858             this.shim.remove();
3859         }
3860         this.dd.unreg();
3861         this.proxy.parentNode.removeChild(this.proxy);
3862         if(removeEl){
3863             this.el.remove();
3864         }
3865     }
3866 });
3867
3868 /**
3869  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
3870  */
3871 Roo.SplitBar.createProxy = function(dir){
3872     var proxy = new Roo.Element(document.createElement("div"));
3873     proxy.unselectable();
3874     var cls = 'x-splitbar-proxy';
3875     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3876     document.body.appendChild(proxy.dom);
3877     return proxy.dom;
3878 };
3879
3880 /** 
3881  * @class Roo.SplitBar.BasicLayoutAdapter
3882  * Default Adapter. It assumes the splitter and resizing element are not positioned
3883  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3884  */
3885 Roo.SplitBar.BasicLayoutAdapter = function(){
3886 };
3887
3888 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3889     // do nothing for now
3890     init : function(s){
3891     
3892     },
3893     /**
3894      * Called before drag operations to get the current size of the resizing element. 
3895      * @param {Roo.SplitBar} s The SplitBar using this adapter
3896      */
3897      getElementSize : function(s){
3898         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3899             return s.resizingEl.getWidth();
3900         }else{
3901             return s.resizingEl.getHeight();
3902         }
3903     },
3904     
3905     /**
3906      * Called after drag operations to set the size of the resizing element.
3907      * @param {Roo.SplitBar} s The SplitBar using this adapter
3908      * @param {Number} newSize The new size to set
3909      * @param {Function} onComplete A function to be invoked when resizing is complete
3910      */
3911     setElementSize : function(s, newSize, onComplete){
3912         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3913             if(!s.animate){
3914                 s.resizingEl.setWidth(newSize);
3915                 if(onComplete){
3916                     onComplete(s, newSize);
3917                 }
3918             }else{
3919                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3920             }
3921         }else{
3922             
3923             if(!s.animate){
3924                 s.resizingEl.setHeight(newSize);
3925                 if(onComplete){
3926                     onComplete(s, newSize);
3927                 }
3928             }else{
3929                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3930             }
3931         }
3932     }
3933 };
3934
3935 /** 
3936  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3937  * @extends Roo.SplitBar.BasicLayoutAdapter
3938  * Adapter that  moves the splitter element to align with the resized sizing element. 
3939  * Used with an absolute positioned SplitBar.
3940  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3941  * document.body, make sure you assign an id to the body element.
3942  */
3943 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3944     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3945     this.container = Roo.get(container);
3946 };
3947
3948 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3949     init : function(s){
3950         this.basic.init(s);
3951     },
3952     
3953     getElementSize : function(s){
3954         return this.basic.getElementSize(s);
3955     },
3956     
3957     setElementSize : function(s, newSize, onComplete){
3958         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3959     },
3960     
3961     moveSplitter : function(s){
3962         var yes = Roo.SplitBar;
3963         switch(s.placement){
3964             case yes.LEFT:
3965                 s.el.setX(s.resizingEl.getRight());
3966                 break;
3967             case yes.RIGHT:
3968                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3969                 break;
3970             case yes.TOP:
3971                 s.el.setY(s.resizingEl.getBottom());
3972                 break;
3973             case yes.BOTTOM:
3974                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3975                 break;
3976         }
3977     }
3978 };
3979
3980 /**
3981  * Orientation constant - Create a vertical SplitBar
3982  * @static
3983  * @type Number
3984  */
3985 Roo.SplitBar.VERTICAL = 1;
3986
3987 /**
3988  * Orientation constant - Create a horizontal SplitBar
3989  * @static
3990  * @type Number
3991  */
3992 Roo.SplitBar.HORIZONTAL = 2;
3993
3994 /**
3995  * Placement constant - The resizing element is to the left of the splitter element
3996  * @static
3997  * @type Number
3998  */
3999 Roo.SplitBar.LEFT = 1;
4000
4001 /**
4002  * Placement constant - The resizing element is to the right of the splitter element
4003  * @static
4004  * @type Number
4005  */
4006 Roo.SplitBar.RIGHT = 2;
4007
4008 /**
4009  * Placement constant - The resizing element is positioned above the splitter element
4010  * @static
4011  * @type Number
4012  */
4013 Roo.SplitBar.TOP = 3;
4014
4015 /**
4016  * Placement constant - The resizing element is positioned under splitter element
4017  * @static
4018  * @type Number
4019  */
4020 Roo.SplitBar.BOTTOM = 4;
4021 /*
4022  * Based on:
4023  * Ext JS Library 1.1.1
4024  * Copyright(c) 2006-2007, Ext JS, LLC.
4025  *
4026  * Originally Released Under LGPL - original licence link has changed is not relivant.
4027  *
4028  * Fork - LGPL
4029  * <script type="text/javascript">
4030  */
4031
4032 /**
4033  * @class Roo.View
4034  * @extends Roo.util.Observable
4035  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4036  * This class also supports single and multi selection modes. <br>
4037  * Create a data model bound view:
4038  <pre><code>
4039  var store = new Roo.data.Store(...);
4040
4041  var view = new Roo.View({
4042     el : "my-element",
4043     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4044  
4045     singleSelect: true,
4046     selectedClass: "ydataview-selected",
4047     store: store
4048  });
4049
4050  // listen for node click?
4051  view.on("click", function(vw, index, node, e){
4052  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4053  });
4054
4055  // load XML data
4056  dataModel.load("foobar.xml");
4057  </code></pre>
4058  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4059  * <br><br>
4060  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4061  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4062  * 
4063  * Note: old style constructor is still suported (container, template, config)
4064  * 
4065  * @constructor
4066  * Create a new View
4067  * @param {Object} config The config object
4068  * 
4069  */
4070 Roo.View = function(config, depreciated_tpl, depreciated_config){
4071     
4072     this.parent = false;
4073     
4074     if (typeof(depreciated_tpl) == 'undefined') {
4075         // new way.. - universal constructor.
4076         Roo.apply(this, config);
4077         this.el  = Roo.get(this.el);
4078     } else {
4079         // old format..
4080         this.el  = Roo.get(config);
4081         this.tpl = depreciated_tpl;
4082         Roo.apply(this, depreciated_config);
4083     }
4084     this.wrapEl  = this.el.wrap().wrap();
4085     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4086     
4087     
4088     if(typeof(this.tpl) == "string"){
4089         this.tpl = new Roo.Template(this.tpl);
4090     } else {
4091         // support xtype ctors..
4092         this.tpl = new Roo.factory(this.tpl, Roo);
4093     }
4094     
4095     
4096     this.tpl.compile();
4097     
4098     /** @private */
4099     this.addEvents({
4100         /**
4101          * @event beforeclick
4102          * Fires before a click is processed. Returns false to cancel the default action.
4103          * @param {Roo.View} this
4104          * @param {Number} index The index of the target node
4105          * @param {HTMLElement} node The target node
4106          * @param {Roo.EventObject} e The raw event object
4107          */
4108             "beforeclick" : true,
4109         /**
4110          * @event click
4111          * Fires when a template node is clicked.
4112          * @param {Roo.View} this
4113          * @param {Number} index The index of the target node
4114          * @param {HTMLElement} node The target node
4115          * @param {Roo.EventObject} e The raw event object
4116          */
4117             "click" : true,
4118         /**
4119          * @event dblclick
4120          * Fires when a template node is double clicked.
4121          * @param {Roo.View} this
4122          * @param {Number} index The index of the target node
4123          * @param {HTMLElement} node The target node
4124          * @param {Roo.EventObject} e The raw event object
4125          */
4126             "dblclick" : true,
4127         /**
4128          * @event contextmenu
4129          * Fires when a template node is right clicked.
4130          * @param {Roo.View} this
4131          * @param {Number} index The index of the target node
4132          * @param {HTMLElement} node The target node
4133          * @param {Roo.EventObject} e The raw event object
4134          */
4135             "contextmenu" : true,
4136         /**
4137          * @event selectionchange
4138          * Fires when the selected nodes change.
4139          * @param {Roo.View} this
4140          * @param {Array} selections Array of the selected nodes
4141          */
4142             "selectionchange" : true,
4143     
4144         /**
4145          * @event beforeselect
4146          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4147          * @param {Roo.View} this
4148          * @param {HTMLElement} node The node to be selected
4149          * @param {Array} selections Array of currently selected nodes
4150          */
4151             "beforeselect" : true,
4152         /**
4153          * @event preparedata
4154          * Fires on every row to render, to allow you to change the data.
4155          * @param {Roo.View} this
4156          * @param {Object} data to be rendered (change this)
4157          */
4158           "preparedata" : true
4159           
4160           
4161         });
4162
4163
4164
4165     this.el.on({
4166         "click": this.onClick,
4167         "dblclick": this.onDblClick,
4168         "contextmenu": this.onContextMenu,
4169         scope:this
4170     });
4171
4172     this.selections = [];
4173     this.nodes = [];
4174     this.cmp = new Roo.CompositeElementLite([]);
4175     if(this.store){
4176         this.store = Roo.factory(this.store, Roo.data);
4177         this.setStore(this.store, true);
4178     }
4179     
4180     if ( this.footer && this.footer.xtype) {
4181            
4182          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4183         
4184         this.footer.dataSource = this.store;
4185         this.footer.container = fctr;
4186         this.footer = Roo.factory(this.footer, Roo);
4187         fctr.insertFirst(this.el);
4188         
4189         // this is a bit insane - as the paging toolbar seems to detach the el..
4190 //        dom.parentNode.parentNode.parentNode
4191          // they get detached?
4192     }
4193     
4194     
4195     Roo.View.superclass.constructor.call(this);
4196     
4197     
4198 };
4199
4200 Roo.extend(Roo.View, Roo.util.Observable, {
4201     
4202      /**
4203      * @cfg {Roo.data.Store} store Data store to load data from.
4204      */
4205     store : false,
4206     
4207     /**
4208      * @cfg {String|Roo.Element} el The container element.
4209      */
4210     el : '',
4211     
4212     /**
4213      * @cfg {String|Roo.Template} tpl The template used by this View 
4214      */
4215     tpl : false,
4216     /**
4217      * @cfg {String} dataName the named area of the template to use as the data area
4218      *                          Works with domtemplates roo-name="name"
4219      */
4220     dataName: false,
4221     /**
4222      * @cfg {String} selectedClass The css class to add to selected nodes
4223      */
4224     selectedClass : "x-view-selected",
4225      /**
4226      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4227      */
4228     emptyText : "",
4229     
4230     /**
4231      * @cfg {String} text to display on mask (default Loading)
4232      */
4233     mask : false,
4234     /**
4235      * @cfg {Boolean} multiSelect Allow multiple selection
4236      */
4237     multiSelect : false,
4238     /**
4239      * @cfg {Boolean} singleSelect Allow single selection
4240      */
4241     singleSelect:  false,
4242     
4243     /**
4244      * @cfg {Boolean} toggleSelect - selecting 
4245      */
4246     toggleSelect : false,
4247     
4248     /**
4249      * @cfg {Boolean} tickable - selecting 
4250      */
4251     tickable : false,
4252     
4253     /**
4254      * Returns the element this view is bound to.
4255      * @return {Roo.Element}
4256      */
4257     getEl : function(){
4258         return this.wrapEl;
4259     },
4260     
4261     
4262
4263     /**
4264      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4265      */
4266     refresh : function(){
4267         //Roo.log('refresh');
4268         var t = this.tpl;
4269         
4270         // if we are using something like 'domtemplate', then
4271         // the what gets used is:
4272         // t.applySubtemplate(NAME, data, wrapping data..)
4273         // the outer template then get' applied with
4274         //     the store 'extra data'
4275         // and the body get's added to the
4276         //      roo-name="data" node?
4277         //      <span class='roo-tpl-{name}'></span> ?????
4278         
4279         
4280         
4281         this.clearSelections();
4282         this.el.update("");
4283         var html = [];
4284         var records = this.store.getRange();
4285         if(records.length < 1) {
4286             
4287             // is this valid??  = should it render a template??
4288             
4289             this.el.update(this.emptyText);
4290             return;
4291         }
4292         var el = this.el;
4293         if (this.dataName) {
4294             this.el.update(t.apply(this.store.meta)); //????
4295             el = this.el.child('.roo-tpl-' + this.dataName);
4296         }
4297         
4298         for(var i = 0, len = records.length; i < len; i++){
4299             var data = this.prepareData(records[i].data, i, records[i]);
4300             this.fireEvent("preparedata", this, data, i, records[i]);
4301             
4302             var d = Roo.apply({}, data);
4303             
4304             if(this.tickable){
4305                 Roo.apply(d, {'roo-id' : Roo.id()});
4306                 
4307                 var _this = this;
4308             
4309                 Roo.each(this.parent.item, function(item){
4310                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4311                         return;
4312                     }
4313                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4314                 });
4315             }
4316             
4317             html[html.length] = Roo.util.Format.trim(
4318                 this.dataName ?
4319                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4320                     t.apply(d)
4321             );
4322         }
4323         
4324         
4325         
4326         el.update(html.join(""));
4327         this.nodes = el.dom.childNodes;
4328         this.updateIndexes(0);
4329     },
4330     
4331
4332     /**
4333      * Function to override to reformat the data that is sent to
4334      * the template for each node.
4335      * DEPRICATED - use the preparedata event handler.
4336      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4337      * a JSON object for an UpdateManager bound view).
4338      */
4339     prepareData : function(data, index, record)
4340     {
4341         this.fireEvent("preparedata", this, data, index, record);
4342         return data;
4343     },
4344
4345     onUpdate : function(ds, record){
4346         // Roo.log('on update');   
4347         this.clearSelections();
4348         var index = this.store.indexOf(record);
4349         var n = this.nodes[index];
4350         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4351         n.parentNode.removeChild(n);
4352         this.updateIndexes(index, index);
4353     },
4354
4355     
4356     
4357 // --------- FIXME     
4358     onAdd : function(ds, records, index)
4359     {
4360         //Roo.log(['on Add', ds, records, index] );        
4361         this.clearSelections();
4362         if(this.nodes.length == 0){
4363             this.refresh();
4364             return;
4365         }
4366         var n = this.nodes[index];
4367         for(var i = 0, len = records.length; i < len; i++){
4368             var d = this.prepareData(records[i].data, i, records[i]);
4369             if(n){
4370                 this.tpl.insertBefore(n, d);
4371             }else{
4372                 
4373                 this.tpl.append(this.el, d);
4374             }
4375         }
4376         this.updateIndexes(index);
4377     },
4378
4379     onRemove : function(ds, record, index){
4380        // Roo.log('onRemove');
4381         this.clearSelections();
4382         var el = this.dataName  ?
4383             this.el.child('.roo-tpl-' + this.dataName) :
4384             this.el; 
4385         
4386         el.dom.removeChild(this.nodes[index]);
4387         this.updateIndexes(index);
4388     },
4389
4390     /**
4391      * Refresh an individual node.
4392      * @param {Number} index
4393      */
4394     refreshNode : function(index){
4395         this.onUpdate(this.store, this.store.getAt(index));
4396     },
4397
4398     updateIndexes : function(startIndex, endIndex){
4399         var ns = this.nodes;
4400         startIndex = startIndex || 0;
4401         endIndex = endIndex || ns.length - 1;
4402         for(var i = startIndex; i <= endIndex; i++){
4403             ns[i].nodeIndex = i;
4404         }
4405     },
4406
4407     /**
4408      * Changes the data store this view uses and refresh the view.
4409      * @param {Store} store
4410      */
4411     setStore : function(store, initial){
4412         if(!initial && this.store){
4413             this.store.un("datachanged", this.refresh);
4414             this.store.un("add", this.onAdd);
4415             this.store.un("remove", this.onRemove);
4416             this.store.un("update", this.onUpdate);
4417             this.store.un("clear", this.refresh);
4418             this.store.un("beforeload", this.onBeforeLoad);
4419             this.store.un("load", this.onLoad);
4420             this.store.un("loadexception", this.onLoad);
4421         }
4422         if(store){
4423           
4424             store.on("datachanged", this.refresh, this);
4425             store.on("add", this.onAdd, this);
4426             store.on("remove", this.onRemove, this);
4427             store.on("update", this.onUpdate, this);
4428             store.on("clear", this.refresh, this);
4429             store.on("beforeload", this.onBeforeLoad, this);
4430             store.on("load", this.onLoad, this);
4431             store.on("loadexception", this.onLoad, this);
4432         }
4433         
4434         if(store){
4435             this.refresh();
4436         }
4437     },
4438     /**
4439      * onbeforeLoad - masks the loading area.
4440      *
4441      */
4442     onBeforeLoad : function(store,opts)
4443     {
4444          //Roo.log('onBeforeLoad');   
4445         if (!opts.add) {
4446             this.el.update("");
4447         }
4448         this.el.mask(this.mask ? this.mask : "Loading" ); 
4449     },
4450     onLoad : function ()
4451     {
4452         this.el.unmask();
4453     },
4454     
4455
4456     /**
4457      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4458      * @param {HTMLElement} node
4459      * @return {HTMLElement} The template node
4460      */
4461     findItemFromChild : function(node){
4462         var el = this.dataName  ?
4463             this.el.child('.roo-tpl-' + this.dataName,true) :
4464             this.el.dom; 
4465         
4466         if(!node || node.parentNode == el){
4467                     return node;
4468             }
4469             var p = node.parentNode;
4470             while(p && p != el){
4471             if(p.parentNode == el){
4472                 return p;
4473             }
4474             p = p.parentNode;
4475         }
4476             return null;
4477     },
4478
4479     /** @ignore */
4480     onClick : function(e){
4481         var item = this.findItemFromChild(e.getTarget());
4482         if(item){
4483             var index = this.indexOf(item);
4484             if(this.onItemClick(item, index, e) !== false){
4485                 this.fireEvent("click", this, index, item, e);
4486             }
4487         }else{
4488             this.clearSelections();
4489         }
4490     },
4491
4492     /** @ignore */
4493     onContextMenu : function(e){
4494         var item = this.findItemFromChild(e.getTarget());
4495         if(item){
4496             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4497         }
4498     },
4499
4500     /** @ignore */
4501     onDblClick : function(e){
4502         var item = this.findItemFromChild(e.getTarget());
4503         if(item){
4504             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4505         }
4506     },
4507
4508     onItemClick : function(item, index, e)
4509     {
4510         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4511             return false;
4512         }
4513         if (this.toggleSelect) {
4514             var m = this.isSelected(item) ? 'unselect' : 'select';
4515             //Roo.log(m);
4516             var _t = this;
4517             _t[m](item, true, false);
4518             return true;
4519         }
4520         if(this.multiSelect || this.singleSelect){
4521             if(this.multiSelect && e.shiftKey && this.lastSelection){
4522                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4523             }else{
4524                 this.select(item, this.multiSelect && e.ctrlKey);
4525                 this.lastSelection = item;
4526             }
4527             
4528             if(!this.tickable){
4529                 e.preventDefault();
4530             }
4531             
4532         }
4533         return true;
4534     },
4535
4536     /**
4537      * Get the number of selected nodes.
4538      * @return {Number}
4539      */
4540     getSelectionCount : function(){
4541         return this.selections.length;
4542     },
4543
4544     /**
4545      * Get the currently selected nodes.
4546      * @return {Array} An array of HTMLElements
4547      */
4548     getSelectedNodes : function(){
4549         return this.selections;
4550     },
4551
4552     /**
4553      * Get the indexes of the selected nodes.
4554      * @return {Array}
4555      */
4556     getSelectedIndexes : function(){
4557         var indexes = [], s = this.selections;
4558         for(var i = 0, len = s.length; i < len; i++){
4559             indexes.push(s[i].nodeIndex);
4560         }
4561         return indexes;
4562     },
4563
4564     /**
4565      * Clear all selections
4566      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4567      */
4568     clearSelections : function(suppressEvent){
4569         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4570             this.cmp.elements = this.selections;
4571             this.cmp.removeClass(this.selectedClass);
4572             this.selections = [];
4573             if(!suppressEvent){
4574                 this.fireEvent("selectionchange", this, this.selections);
4575             }
4576         }
4577     },
4578
4579     /**
4580      * Returns true if the passed node is selected
4581      * @param {HTMLElement/Number} node The node or node index
4582      * @return {Boolean}
4583      */
4584     isSelected : function(node){
4585         var s = this.selections;
4586         if(s.length < 1){
4587             return false;
4588         }
4589         node = this.getNode(node);
4590         return s.indexOf(node) !== -1;
4591     },
4592
4593     /**
4594      * Selects nodes.
4595      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4596      * @param {Boolean} keepExisting (optional) true to keep existing selections
4597      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4598      */
4599     select : function(nodeInfo, keepExisting, suppressEvent){
4600         if(nodeInfo instanceof Array){
4601             if(!keepExisting){
4602                 this.clearSelections(true);
4603             }
4604             for(var i = 0, len = nodeInfo.length; i < len; i++){
4605                 this.select(nodeInfo[i], true, true);
4606             }
4607             return;
4608         } 
4609         var node = this.getNode(nodeInfo);
4610         if(!node || this.isSelected(node)){
4611             return; // already selected.
4612         }
4613         if(!keepExisting){
4614             this.clearSelections(true);
4615         }
4616         
4617         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4618             Roo.fly(node).addClass(this.selectedClass);
4619             this.selections.push(node);
4620             if(!suppressEvent){
4621                 this.fireEvent("selectionchange", this, this.selections);
4622             }
4623         }
4624         
4625         
4626     },
4627       /**
4628      * Unselects nodes.
4629      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4630      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4631      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4632      */
4633     unselect : function(nodeInfo, keepExisting, suppressEvent)
4634     {
4635         if(nodeInfo instanceof Array){
4636             Roo.each(this.selections, function(s) {
4637                 this.unselect(s, nodeInfo);
4638             }, this);
4639             return;
4640         }
4641         var node = this.getNode(nodeInfo);
4642         if(!node || !this.isSelected(node)){
4643             //Roo.log("not selected");
4644             return; // not selected.
4645         }
4646         // fireevent???
4647         var ns = [];
4648         Roo.each(this.selections, function(s) {
4649             if (s == node ) {
4650                 Roo.fly(node).removeClass(this.selectedClass);
4651
4652                 return;
4653             }
4654             ns.push(s);
4655         },this);
4656         
4657         this.selections= ns;
4658         this.fireEvent("selectionchange", this, this.selections);
4659     },
4660
4661     /**
4662      * Gets a template node.
4663      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4664      * @return {HTMLElement} The node or null if it wasn't found
4665      */
4666     getNode : function(nodeInfo){
4667         if(typeof nodeInfo == "string"){
4668             return document.getElementById(nodeInfo);
4669         }else if(typeof nodeInfo == "number"){
4670             return this.nodes[nodeInfo];
4671         }
4672         return nodeInfo;
4673     },
4674
4675     /**
4676      * Gets a range template nodes.
4677      * @param {Number} startIndex
4678      * @param {Number} endIndex
4679      * @return {Array} An array of nodes
4680      */
4681     getNodes : function(start, end){
4682         var ns = this.nodes;
4683         start = start || 0;
4684         end = typeof end == "undefined" ? ns.length - 1 : end;
4685         var nodes = [];
4686         if(start <= end){
4687             for(var i = start; i <= end; i++){
4688                 nodes.push(ns[i]);
4689             }
4690         } else{
4691             for(var i = start; i >= end; i--){
4692                 nodes.push(ns[i]);
4693             }
4694         }
4695         return nodes;
4696     },
4697
4698     /**
4699      * Finds the index of the passed node
4700      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4701      * @return {Number} The index of the node or -1
4702      */
4703     indexOf : function(node){
4704         node = this.getNode(node);
4705         if(typeof node.nodeIndex == "number"){
4706             return node.nodeIndex;
4707         }
4708         var ns = this.nodes;
4709         for(var i = 0, len = ns.length; i < len; i++){
4710             if(ns[i] == node){
4711                 return i;
4712             }
4713         }
4714         return -1;
4715     }
4716 });
4717 /*
4718  * Based on:
4719  * Ext JS Library 1.1.1
4720  * Copyright(c) 2006-2007, Ext JS, LLC.
4721  *
4722  * Originally Released Under LGPL - original licence link has changed is not relivant.
4723  *
4724  * Fork - LGPL
4725  * <script type="text/javascript">
4726  */
4727
4728 /**
4729  * @class Roo.JsonView
4730  * @extends Roo.View
4731  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4732 <pre><code>
4733 var view = new Roo.JsonView({
4734     container: "my-element",
4735     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4736     multiSelect: true, 
4737     jsonRoot: "data" 
4738 });
4739
4740 // listen for node click?
4741 view.on("click", function(vw, index, node, e){
4742     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4743 });
4744
4745 // direct load of JSON data
4746 view.load("foobar.php");
4747
4748 // Example from my blog list
4749 var tpl = new Roo.Template(
4750     '&lt;div class="entry"&gt;' +
4751     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4752     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4753     "&lt;/div&gt;&lt;hr /&gt;"
4754 );
4755
4756 var moreView = new Roo.JsonView({
4757     container :  "entry-list", 
4758     template : tpl,
4759     jsonRoot: "posts"
4760 });
4761 moreView.on("beforerender", this.sortEntries, this);
4762 moreView.load({
4763     url: "/blog/get-posts.php",
4764     params: "allposts=true",
4765     text: "Loading Blog Entries..."
4766 });
4767 </code></pre>
4768
4769 * Note: old code is supported with arguments : (container, template, config)
4770
4771
4772  * @constructor
4773  * Create a new JsonView
4774  * 
4775  * @param {Object} config The config object
4776  * 
4777  */
4778 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4779     
4780     
4781     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4782
4783     var um = this.el.getUpdateManager();
4784     um.setRenderer(this);
4785     um.on("update", this.onLoad, this);
4786     um.on("failure", this.onLoadException, this);
4787
4788     /**
4789      * @event beforerender
4790      * Fires before rendering of the downloaded JSON data.
4791      * @param {Roo.JsonView} this
4792      * @param {Object} data The JSON data loaded
4793      */
4794     /**
4795      * @event load
4796      * Fires when data is loaded.
4797      * @param {Roo.JsonView} this
4798      * @param {Object} data The JSON data loaded
4799      * @param {Object} response The raw Connect response object
4800      */
4801     /**
4802      * @event loadexception
4803      * Fires when loading fails.
4804      * @param {Roo.JsonView} this
4805      * @param {Object} response The raw Connect response object
4806      */
4807     this.addEvents({
4808         'beforerender' : true,
4809         'load' : true,
4810         'loadexception' : true
4811     });
4812 };
4813 Roo.extend(Roo.JsonView, Roo.View, {
4814     /**
4815      * @type {String} The root property in the loaded JSON object that contains the data
4816      */
4817     jsonRoot : "",
4818
4819     /**
4820      * Refreshes the view.
4821      */
4822     refresh : function(){
4823         this.clearSelections();
4824         this.el.update("");
4825         var html = [];
4826         var o = this.jsonData;
4827         if(o && o.length > 0){
4828             for(var i = 0, len = o.length; i < len; i++){
4829                 var data = this.prepareData(o[i], i, o);
4830                 html[html.length] = this.tpl.apply(data);
4831             }
4832         }else{
4833             html.push(this.emptyText);
4834         }
4835         this.el.update(html.join(""));
4836         this.nodes = this.el.dom.childNodes;
4837         this.updateIndexes(0);
4838     },
4839
4840     /**
4841      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
4842      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
4843      <pre><code>
4844      view.load({
4845          url: "your-url.php",
4846          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4847          callback: yourFunction,
4848          scope: yourObject, //(optional scope)
4849          discardUrl: false,
4850          nocache: false,
4851          text: "Loading...",
4852          timeout: 30,
4853          scripts: false
4854      });
4855      </code></pre>
4856      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4857      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
4858      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
4859      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4860      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
4861      */
4862     load : function(){
4863         var um = this.el.getUpdateManager();
4864         um.update.apply(um, arguments);
4865     },
4866
4867     // note - render is a standard framework call...
4868     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4869     render : function(el, response){
4870         
4871         this.clearSelections();
4872         this.el.update("");
4873         var o;
4874         try{
4875             if (response != '') {
4876                 o = Roo.util.JSON.decode(response.responseText);
4877                 if(this.jsonRoot){
4878                     
4879                     o = o[this.jsonRoot];
4880                 }
4881             }
4882         } catch(e){
4883         }
4884         /**
4885          * The current JSON data or null
4886          */
4887         this.jsonData = o;
4888         this.beforeRender();
4889         this.refresh();
4890     },
4891
4892 /**
4893  * Get the number of records in the current JSON dataset
4894  * @return {Number}
4895  */
4896     getCount : function(){
4897         return this.jsonData ? this.jsonData.length : 0;
4898     },
4899
4900 /**
4901  * Returns the JSON object for the specified node(s)
4902  * @param {HTMLElement/Array} node The node or an array of nodes
4903  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4904  * you get the JSON object for the node
4905  */
4906     getNodeData : function(node){
4907         if(node instanceof Array){
4908             var data = [];
4909             for(var i = 0, len = node.length; i < len; i++){
4910                 data.push(this.getNodeData(node[i]));
4911             }
4912             return data;
4913         }
4914         return this.jsonData[this.indexOf(node)] || null;
4915     },
4916
4917     beforeRender : function(){
4918         this.snapshot = this.jsonData;
4919         if(this.sortInfo){
4920             this.sort.apply(this, this.sortInfo);
4921         }
4922         this.fireEvent("beforerender", this, this.jsonData);
4923     },
4924
4925     onLoad : function(el, o){
4926         this.fireEvent("load", this, this.jsonData, o);
4927     },
4928
4929     onLoadException : function(el, o){
4930         this.fireEvent("loadexception", this, o);
4931     },
4932
4933 /**
4934  * Filter the data by a specific property.
4935  * @param {String} property A property on your JSON objects
4936  * @param {String/RegExp} value Either string that the property values
4937  * should start with, or a RegExp to test against the property
4938  */
4939     filter : function(property, value){
4940         if(this.jsonData){
4941             var data = [];
4942             var ss = this.snapshot;
4943             if(typeof value == "string"){
4944                 var vlen = value.length;
4945                 if(vlen == 0){
4946                     this.clearFilter();
4947                     return;
4948                 }
4949                 value = value.toLowerCase();
4950                 for(var i = 0, len = ss.length; i < len; i++){
4951                     var o = ss[i];
4952                     if(o[property].substr(0, vlen).toLowerCase() == value){
4953                         data.push(o);
4954                     }
4955                 }
4956             } else if(value.exec){ // regex?
4957                 for(var i = 0, len = ss.length; i < len; i++){
4958                     var o = ss[i];
4959                     if(value.test(o[property])){
4960                         data.push(o);
4961                     }
4962                 }
4963             } else{
4964                 return;
4965             }
4966             this.jsonData = data;
4967             this.refresh();
4968         }
4969     },
4970
4971 /**
4972  * Filter by a function. The passed function will be called with each
4973  * object in the current dataset. If the function returns true the value is kept,
4974  * otherwise it is filtered.
4975  * @param {Function} fn
4976  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4977  */
4978     filterBy : function(fn, scope){
4979         if(this.jsonData){
4980             var data = [];
4981             var ss = this.snapshot;
4982             for(var i = 0, len = ss.length; i < len; i++){
4983                 var o = ss[i];
4984                 if(fn.call(scope || this, o)){
4985                     data.push(o);
4986                 }
4987             }
4988             this.jsonData = data;
4989             this.refresh();
4990         }
4991     },
4992
4993 /**
4994  * Clears the current filter.
4995  */
4996     clearFilter : function(){
4997         if(this.snapshot && this.jsonData != this.snapshot){
4998             this.jsonData = this.snapshot;
4999             this.refresh();
5000         }
5001     },
5002
5003
5004 /**
5005  * Sorts the data for this view and refreshes it.
5006  * @param {String} property A property on your JSON objects to sort on
5007  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5008  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5009  */
5010     sort : function(property, dir, sortType){
5011         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5012         if(this.jsonData){
5013             var p = property;
5014             var dsc = dir && dir.toLowerCase() == "desc";
5015             var f = function(o1, o2){
5016                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5017                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5018                 ;
5019                 if(v1 < v2){
5020                     return dsc ? +1 : -1;
5021                 } else if(v1 > v2){
5022                     return dsc ? -1 : +1;
5023                 } else{
5024                     return 0;
5025                 }
5026             };
5027             this.jsonData.sort(f);
5028             this.refresh();
5029             if(this.jsonData != this.snapshot){
5030                 this.snapshot.sort(f);
5031             }
5032         }
5033     }
5034 });/*
5035  * Based on:
5036  * Ext JS Library 1.1.1
5037  * Copyright(c) 2006-2007, Ext JS, LLC.
5038  *
5039  * Originally Released Under LGPL - original licence link has changed is not relivant.
5040  *
5041  * Fork - LGPL
5042  * <script type="text/javascript">
5043  */
5044  
5045
5046 /**
5047  * @class Roo.ColorPalette
5048  * @extends Roo.Component
5049  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5050  * Here's an example of typical usage:
5051  * <pre><code>
5052 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5053 cp.render('my-div');
5054
5055 cp.on('select', function(palette, selColor){
5056     // do something with selColor
5057 });
5058 </code></pre>
5059  * @constructor
5060  * Create a new ColorPalette
5061  * @param {Object} config The config object
5062  */
5063 Roo.ColorPalette = function(config){
5064     Roo.ColorPalette.superclass.constructor.call(this, config);
5065     this.addEvents({
5066         /**
5067              * @event select
5068              * Fires when a color is selected
5069              * @param {ColorPalette} this
5070              * @param {String} color The 6-digit color hex code (without the # symbol)
5071              */
5072         select: true
5073     });
5074
5075     if(this.handler){
5076         this.on("select", this.handler, this.scope, true);
5077     }
5078 };
5079 Roo.extend(Roo.ColorPalette, Roo.Component, {
5080     /**
5081      * @cfg {String} itemCls
5082      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5083      */
5084     itemCls : "x-color-palette",
5085     /**
5086      * @cfg {String} value
5087      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5088      * the hex codes are case-sensitive.
5089      */
5090     value : null,
5091     clickEvent:'click',
5092     // private
5093     ctype: "Roo.ColorPalette",
5094
5095     /**
5096      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5097      */
5098     allowReselect : false,
5099
5100     /**
5101      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5102      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5103      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5104      * of colors with the width setting until the box is symmetrical.</p>
5105      * <p>You can override individual colors if needed:</p>
5106      * <pre><code>
5107 var cp = new Roo.ColorPalette();
5108 cp.colors[0] = "FF0000";  // change the first box to red
5109 </code></pre>
5110
5111 Or you can provide a custom array of your own for complete control:
5112 <pre><code>
5113 var cp = new Roo.ColorPalette();
5114 cp.colors = ["000000", "993300", "333300"];
5115 </code></pre>
5116      * @type Array
5117      */
5118     colors : [
5119         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5120         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5121         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5122         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5123         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5124     ],
5125
5126     // private
5127     onRender : function(container, position){
5128         var t = new Roo.MasterTemplate(
5129             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5130         );
5131         var c = this.colors;
5132         for(var i = 0, len = c.length; i < len; i++){
5133             t.add([c[i]]);
5134         }
5135         var el = document.createElement("div");
5136         el.className = this.itemCls;
5137         t.overwrite(el);
5138         container.dom.insertBefore(el, position);
5139         this.el = Roo.get(el);
5140         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5141         if(this.clickEvent != 'click'){
5142             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5143         }
5144     },
5145
5146     // private
5147     afterRender : function(){
5148         Roo.ColorPalette.superclass.afterRender.call(this);
5149         if(this.value){
5150             var s = this.value;
5151             this.value = null;
5152             this.select(s);
5153         }
5154     },
5155
5156     // private
5157     handleClick : function(e, t){
5158         e.preventDefault();
5159         if(!this.disabled){
5160             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5161             this.select(c.toUpperCase());
5162         }
5163     },
5164
5165     /**
5166      * Selects the specified color in the palette (fires the select event)
5167      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5168      */
5169     select : function(color){
5170         color = color.replace("#", "");
5171         if(color != this.value || this.allowReselect){
5172             var el = this.el;
5173             if(this.value){
5174                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5175             }
5176             el.child("a.color-"+color).addClass("x-color-palette-sel");
5177             this.value = color;
5178             this.fireEvent("select", this, color);
5179         }
5180     }
5181 });/*
5182  * Based on:
5183  * Ext JS Library 1.1.1
5184  * Copyright(c) 2006-2007, Ext JS, LLC.
5185  *
5186  * Originally Released Under LGPL - original licence link has changed is not relivant.
5187  *
5188  * Fork - LGPL
5189  * <script type="text/javascript">
5190  */
5191  
5192 /**
5193  * @class Roo.DatePicker
5194  * @extends Roo.Component
5195  * Simple date picker class.
5196  * @constructor
5197  * Create a new DatePicker
5198  * @param {Object} config The config object
5199  */
5200 Roo.DatePicker = function(config){
5201     Roo.DatePicker.superclass.constructor.call(this, config);
5202
5203     this.value = config && config.value ?
5204                  config.value.clearTime() : new Date().clearTime();
5205
5206     this.addEvents({
5207         /**
5208              * @event select
5209              * Fires when a date is selected
5210              * @param {DatePicker} this
5211              * @param {Date} date The selected date
5212              */
5213         'select': true,
5214         /**
5215              * @event monthchange
5216              * Fires when the displayed month changes 
5217              * @param {DatePicker} this
5218              * @param {Date} date The selected month
5219              */
5220         'monthchange': true
5221     });
5222
5223     if(this.handler){
5224         this.on("select", this.handler,  this.scope || this);
5225     }
5226     // build the disabledDatesRE
5227     if(!this.disabledDatesRE && this.disabledDates){
5228         var dd = this.disabledDates;
5229         var re = "(?:";
5230         for(var i = 0; i < dd.length; i++){
5231             re += dd[i];
5232             if(i != dd.length-1) {
5233                 re += "|";
5234             }
5235         }
5236         this.disabledDatesRE = new RegExp(re + ")");
5237     }
5238 };
5239
5240 Roo.extend(Roo.DatePicker, Roo.Component, {
5241     /**
5242      * @cfg {String} todayText
5243      * The text to display on the button that selects the current date (defaults to "Today")
5244      */
5245     todayText : "Today",
5246     /**
5247      * @cfg {String} okText
5248      * The text to display on the ok button
5249      */
5250     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5251     /**
5252      * @cfg {String} cancelText
5253      * The text to display on the cancel button
5254      */
5255     cancelText : "Cancel",
5256     /**
5257      * @cfg {String} todayTip
5258      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5259      */
5260     todayTip : "{0} (Spacebar)",
5261     /**
5262      * @cfg {Date} minDate
5263      * Minimum allowable date (JavaScript date object, defaults to null)
5264      */
5265     minDate : null,
5266     /**
5267      * @cfg {Date} maxDate
5268      * Maximum allowable date (JavaScript date object, defaults to null)
5269      */
5270     maxDate : null,
5271     /**
5272      * @cfg {String} minText
5273      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5274      */
5275     minText : "This date is before the minimum date",
5276     /**
5277      * @cfg {String} maxText
5278      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5279      */
5280     maxText : "This date is after the maximum date",
5281     /**
5282      * @cfg {String} format
5283      * The default date format string which can be overriden for localization support.  The format must be
5284      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5285      */
5286     format : "m/d/y",
5287     /**
5288      * @cfg {Array} disabledDays
5289      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5290      */
5291     disabledDays : null,
5292     /**
5293      * @cfg {String} disabledDaysText
5294      * The tooltip to display when the date falls on a disabled day (defaults to "")
5295      */
5296     disabledDaysText : "",
5297     /**
5298      * @cfg {RegExp} disabledDatesRE
5299      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5300      */
5301     disabledDatesRE : null,
5302     /**
5303      * @cfg {String} disabledDatesText
5304      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5305      */
5306     disabledDatesText : "",
5307     /**
5308      * @cfg {Boolean} constrainToViewport
5309      * True to constrain the date picker to the viewport (defaults to true)
5310      */
5311     constrainToViewport : true,
5312     /**
5313      * @cfg {Array} monthNames
5314      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5315      */
5316     monthNames : Date.monthNames,
5317     /**
5318      * @cfg {Array} dayNames
5319      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5320      */
5321     dayNames : Date.dayNames,
5322     /**
5323      * @cfg {String} nextText
5324      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5325      */
5326     nextText: 'Next Month (Control+Right)',
5327     /**
5328      * @cfg {String} prevText
5329      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5330      */
5331     prevText: 'Previous Month (Control+Left)',
5332     /**
5333      * @cfg {String} monthYearText
5334      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5335      */
5336     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5337     /**
5338      * @cfg {Number} startDay
5339      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5340      */
5341     startDay : 0,
5342     /**
5343      * @cfg {Bool} showClear
5344      * Show a clear button (usefull for date form elements that can be blank.)
5345      */
5346     
5347     showClear: false,
5348     
5349     /**
5350      * Sets the value of the date field
5351      * @param {Date} value The date to set
5352      */
5353     setValue : function(value){
5354         var old = this.value;
5355         
5356         if (typeof(value) == 'string') {
5357          
5358             value = Date.parseDate(value, this.format);
5359         }
5360         if (!value) {
5361             value = new Date();
5362         }
5363         
5364         this.value = value.clearTime(true);
5365         if(this.el){
5366             this.update(this.value);
5367         }
5368     },
5369
5370     /**
5371      * Gets the current selected value of the date field
5372      * @return {Date} The selected date
5373      */
5374     getValue : function(){
5375         return this.value;
5376     },
5377
5378     // private
5379     focus : function(){
5380         if(this.el){
5381             this.update(this.activeDate);
5382         }
5383     },
5384
5385     // privateval
5386     onRender : function(container, position){
5387         
5388         var m = [
5389              '<table cellspacing="0">',
5390                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
5391                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5392         var dn = this.dayNames;
5393         for(var i = 0; i < 7; i++){
5394             var d = this.startDay+i;
5395             if(d > 6){
5396                 d = d-7;
5397             }
5398             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5399         }
5400         m[m.length] = "</tr></thead><tbody><tr>";
5401         for(var i = 0; i < 42; i++) {
5402             if(i % 7 == 0 && i != 0){
5403                 m[m.length] = "</tr><tr>";
5404             }
5405             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5406         }
5407         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5408             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5409
5410         var el = document.createElement("div");
5411         el.className = "x-date-picker";
5412         el.innerHTML = m.join("");
5413
5414         container.dom.insertBefore(el, position);
5415
5416         this.el = Roo.get(el);
5417         this.eventEl = Roo.get(el.firstChild);
5418
5419         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5420             handler: this.showPrevMonth,
5421             scope: this,
5422             preventDefault:true,
5423             stopDefault:true
5424         });
5425
5426         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5427             handler: this.showNextMonth,
5428             scope: this,
5429             preventDefault:true,
5430             stopDefault:true
5431         });
5432
5433         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5434
5435         this.monthPicker = this.el.down('div.x-date-mp');
5436         this.monthPicker.enableDisplayMode('block');
5437         
5438         var kn = new Roo.KeyNav(this.eventEl, {
5439             "left" : function(e){
5440                 e.ctrlKey ?
5441                     this.showPrevMonth() :
5442                     this.update(this.activeDate.add("d", -1));
5443             },
5444
5445             "right" : function(e){
5446                 e.ctrlKey ?
5447                     this.showNextMonth() :
5448                     this.update(this.activeDate.add("d", 1));
5449             },
5450
5451             "up" : function(e){
5452                 e.ctrlKey ?
5453                     this.showNextYear() :
5454                     this.update(this.activeDate.add("d", -7));
5455             },
5456
5457             "down" : function(e){
5458                 e.ctrlKey ?
5459                     this.showPrevYear() :
5460                     this.update(this.activeDate.add("d", 7));
5461             },
5462
5463             "pageUp" : function(e){
5464                 this.showNextMonth();
5465             },
5466
5467             "pageDown" : function(e){
5468                 this.showPrevMonth();
5469             },
5470
5471             "enter" : function(e){
5472                 e.stopPropagation();
5473                 return true;
5474             },
5475
5476             scope : this
5477         });
5478
5479         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5480
5481         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5482
5483         this.el.unselectable();
5484         
5485         this.cells = this.el.select("table.x-date-inner tbody td");
5486         this.textNodes = this.el.query("table.x-date-inner tbody span");
5487
5488         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5489             text: "&#160;",
5490             tooltip: this.monthYearText
5491         });
5492
5493         this.mbtn.on('click', this.showMonthPicker, this);
5494         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5495
5496
5497         var today = (new Date()).dateFormat(this.format);
5498         
5499         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5500         if (this.showClear) {
5501             baseTb.add( new Roo.Toolbar.Fill());
5502         }
5503         baseTb.add({
5504             text: String.format(this.todayText, today),
5505             tooltip: String.format(this.todayTip, today),
5506             handler: this.selectToday,
5507             scope: this
5508         });
5509         
5510         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5511             
5512         //});
5513         if (this.showClear) {
5514             
5515             baseTb.add( new Roo.Toolbar.Fill());
5516             baseTb.add({
5517                 text: '&#160;',
5518                 cls: 'x-btn-icon x-btn-clear',
5519                 handler: function() {
5520                     //this.value = '';
5521                     this.fireEvent("select", this, '');
5522                 },
5523                 scope: this
5524             });
5525         }
5526         
5527         
5528         if(Roo.isIE){
5529             this.el.repaint();
5530         }
5531         this.update(this.value);
5532     },
5533
5534     createMonthPicker : function(){
5535         if(!this.monthPicker.dom.firstChild){
5536             var buf = ['<table border="0" cellspacing="0">'];
5537             for(var i = 0; i < 6; i++){
5538                 buf.push(
5539                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5540                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5541                     i == 0 ?
5542                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
5543                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5544                 );
5545             }
5546             buf.push(
5547                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5548                     this.okText,
5549                     '</button><button type="button" class="x-date-mp-cancel">',
5550                     this.cancelText,
5551                     '</button></td></tr>',
5552                 '</table>'
5553             );
5554             this.monthPicker.update(buf.join(''));
5555             this.monthPicker.on('click', this.onMonthClick, this);
5556             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5557
5558             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5559             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5560
5561             this.mpMonths.each(function(m, a, i){
5562                 i += 1;
5563                 if((i%2) == 0){
5564                     m.dom.xmonth = 5 + Math.round(i * .5);
5565                 }else{
5566                     m.dom.xmonth = Math.round((i-1) * .5);
5567                 }
5568             });
5569         }
5570     },
5571
5572     showMonthPicker : function(){
5573         this.createMonthPicker();
5574         var size = this.el.getSize();
5575         this.monthPicker.setSize(size);
5576         this.monthPicker.child('table').setSize(size);
5577
5578         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5579         this.updateMPMonth(this.mpSelMonth);
5580         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5581         this.updateMPYear(this.mpSelYear);
5582
5583         this.monthPicker.slideIn('t', {duration:.2});
5584     },
5585
5586     updateMPYear : function(y){
5587         this.mpyear = y;
5588         var ys = this.mpYears.elements;
5589         for(var i = 1; i <= 10; i++){
5590             var td = ys[i-1], y2;
5591             if((i%2) == 0){
5592                 y2 = y + Math.round(i * .5);
5593                 td.firstChild.innerHTML = y2;
5594                 td.xyear = y2;
5595             }else{
5596                 y2 = y - (5-Math.round(i * .5));
5597                 td.firstChild.innerHTML = y2;
5598                 td.xyear = y2;
5599             }
5600             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5601         }
5602     },
5603
5604     updateMPMonth : function(sm){
5605         this.mpMonths.each(function(m, a, i){
5606             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5607         });
5608     },
5609
5610     selectMPMonth: function(m){
5611         
5612     },
5613
5614     onMonthClick : function(e, t){
5615         e.stopEvent();
5616         var el = new Roo.Element(t), pn;
5617         if(el.is('button.x-date-mp-cancel')){
5618             this.hideMonthPicker();
5619         }
5620         else if(el.is('button.x-date-mp-ok')){
5621             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5622             this.hideMonthPicker();
5623         }
5624         else if(pn = el.up('td.x-date-mp-month', 2)){
5625             this.mpMonths.removeClass('x-date-mp-sel');
5626             pn.addClass('x-date-mp-sel');
5627             this.mpSelMonth = pn.dom.xmonth;
5628         }
5629         else if(pn = el.up('td.x-date-mp-year', 2)){
5630             this.mpYears.removeClass('x-date-mp-sel');
5631             pn.addClass('x-date-mp-sel');
5632             this.mpSelYear = pn.dom.xyear;
5633         }
5634         else if(el.is('a.x-date-mp-prev')){
5635             this.updateMPYear(this.mpyear-10);
5636         }
5637         else if(el.is('a.x-date-mp-next')){
5638             this.updateMPYear(this.mpyear+10);
5639         }
5640     },
5641
5642     onMonthDblClick : function(e, t){
5643         e.stopEvent();
5644         var el = new Roo.Element(t), pn;
5645         if(pn = el.up('td.x-date-mp-month', 2)){
5646             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5647             this.hideMonthPicker();
5648         }
5649         else if(pn = el.up('td.x-date-mp-year', 2)){
5650             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5651             this.hideMonthPicker();
5652         }
5653     },
5654
5655     hideMonthPicker : function(disableAnim){
5656         if(this.monthPicker){
5657             if(disableAnim === true){
5658                 this.monthPicker.hide();
5659             }else{
5660                 this.monthPicker.slideOut('t', {duration:.2});
5661             }
5662         }
5663     },
5664
5665     // private
5666     showPrevMonth : function(e){
5667         this.update(this.activeDate.add("mo", -1));
5668     },
5669
5670     // private
5671     showNextMonth : function(e){
5672         this.update(this.activeDate.add("mo", 1));
5673     },
5674
5675     // private
5676     showPrevYear : function(){
5677         this.update(this.activeDate.add("y", -1));
5678     },
5679
5680     // private
5681     showNextYear : function(){
5682         this.update(this.activeDate.add("y", 1));
5683     },
5684
5685     // private
5686     handleMouseWheel : function(e){
5687         var delta = e.getWheelDelta();
5688         if(delta > 0){
5689             this.showPrevMonth();
5690             e.stopEvent();
5691         } else if(delta < 0){
5692             this.showNextMonth();
5693             e.stopEvent();
5694         }
5695     },
5696
5697     // private
5698     handleDateClick : function(e, t){
5699         e.stopEvent();
5700         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5701             this.setValue(new Date(t.dateValue));
5702             this.fireEvent("select", this, this.value);
5703         }
5704     },
5705
5706     // private
5707     selectToday : function(){
5708         this.setValue(new Date().clearTime());
5709         this.fireEvent("select", this, this.value);
5710     },
5711
5712     // private
5713     update : function(date)
5714     {
5715         var vd = this.activeDate;
5716         this.activeDate = date;
5717         if(vd && this.el){
5718             var t = date.getTime();
5719             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5720                 this.cells.removeClass("x-date-selected");
5721                 this.cells.each(function(c){
5722                    if(c.dom.firstChild.dateValue == t){
5723                        c.addClass("x-date-selected");
5724                        setTimeout(function(){
5725                             try{c.dom.firstChild.focus();}catch(e){}
5726                        }, 50);
5727                        return false;
5728                    }
5729                 });
5730                 return;
5731             }
5732         }
5733         
5734         var days = date.getDaysInMonth();
5735         var firstOfMonth = date.getFirstDateOfMonth();
5736         var startingPos = firstOfMonth.getDay()-this.startDay;
5737
5738         if(startingPos <= this.startDay){
5739             startingPos += 7;
5740         }
5741
5742         var pm = date.add("mo", -1);
5743         var prevStart = pm.getDaysInMonth()-startingPos;
5744
5745         var cells = this.cells.elements;
5746         var textEls = this.textNodes;
5747         days += startingPos;
5748
5749         // convert everything to numbers so it's fast
5750         var day = 86400000;
5751         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5752         var today = new Date().clearTime().getTime();
5753         var sel = date.clearTime().getTime();
5754         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5755         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5756         var ddMatch = this.disabledDatesRE;
5757         var ddText = this.disabledDatesText;
5758         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5759         var ddaysText = this.disabledDaysText;
5760         var format = this.format;
5761
5762         var setCellClass = function(cal, cell){
5763             cell.title = "";
5764             var t = d.getTime();
5765             cell.firstChild.dateValue = t;
5766             if(t == today){
5767                 cell.className += " x-date-today";
5768                 cell.title = cal.todayText;
5769             }
5770             if(t == sel){
5771                 cell.className += " x-date-selected";
5772                 setTimeout(function(){
5773                     try{cell.firstChild.focus();}catch(e){}
5774                 }, 50);
5775             }
5776             // disabling
5777             if(t < min) {
5778                 cell.className = " x-date-disabled";
5779                 cell.title = cal.minText;
5780                 return;
5781             }
5782             if(t > max) {
5783                 cell.className = " x-date-disabled";
5784                 cell.title = cal.maxText;
5785                 return;
5786             }
5787             if(ddays){
5788                 if(ddays.indexOf(d.getDay()) != -1){
5789                     cell.title = ddaysText;
5790                     cell.className = " x-date-disabled";
5791                 }
5792             }
5793             if(ddMatch && format){
5794                 var fvalue = d.dateFormat(format);
5795                 if(ddMatch.test(fvalue)){
5796                     cell.title = ddText.replace("%0", fvalue);
5797                     cell.className = " x-date-disabled";
5798                 }
5799             }
5800         };
5801
5802         var i = 0;
5803         for(; i < startingPos; i++) {
5804             textEls[i].innerHTML = (++prevStart);
5805             d.setDate(d.getDate()+1);
5806             cells[i].className = "x-date-prevday";
5807             setCellClass(this, cells[i]);
5808         }
5809         for(; i < days; i++){
5810             intDay = i - startingPos + 1;
5811             textEls[i].innerHTML = (intDay);
5812             d.setDate(d.getDate()+1);
5813             cells[i].className = "x-date-active";
5814             setCellClass(this, cells[i]);
5815         }
5816         var extraDays = 0;
5817         for(; i < 42; i++) {
5818              textEls[i].innerHTML = (++extraDays);
5819              d.setDate(d.getDate()+1);
5820              cells[i].className = "x-date-nextday";
5821              setCellClass(this, cells[i]);
5822         }
5823
5824         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5825         this.fireEvent('monthchange', this, date);
5826         
5827         if(!this.internalRender){
5828             var main = this.el.dom.firstChild;
5829             var w = main.offsetWidth;
5830             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5831             Roo.fly(main).setWidth(w);
5832             this.internalRender = true;
5833             // opera does not respect the auto grow header center column
5834             // then, after it gets a width opera refuses to recalculate
5835             // without a second pass
5836             if(Roo.isOpera && !this.secondPass){
5837                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5838                 this.secondPass = true;
5839                 this.update.defer(10, this, [date]);
5840             }
5841         }
5842         
5843         
5844     }
5845 });        /*
5846  * Based on:
5847  * Ext JS Library 1.1.1
5848  * Copyright(c) 2006-2007, Ext JS, LLC.
5849  *
5850  * Originally Released Under LGPL - original licence link has changed is not relivant.
5851  *
5852  * Fork - LGPL
5853  * <script type="text/javascript">
5854  */
5855 /**
5856  * @class Roo.TabPanel
5857  * @extends Roo.util.Observable
5858  * A lightweight tab container.
5859  * <br><br>
5860  * Usage:
5861  * <pre><code>
5862 // basic tabs 1, built from existing content
5863 var tabs = new Roo.TabPanel("tabs1");
5864 tabs.addTab("script", "View Script");
5865 tabs.addTab("markup", "View Markup");
5866 tabs.activate("script");
5867
5868 // more advanced tabs, built from javascript
5869 var jtabs = new Roo.TabPanel("jtabs");
5870 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5871
5872 // set up the UpdateManager
5873 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5874 var updater = tab2.getUpdateManager();
5875 updater.setDefaultUrl("ajax1.htm");
5876 tab2.on('activate', updater.refresh, updater, true);
5877
5878 // Use setUrl for Ajax loading
5879 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5880 tab3.setUrl("ajax2.htm", null, true);
5881
5882 // Disabled tab
5883 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5884 tab4.disable();
5885
5886 jtabs.activate("jtabs-1");
5887  * </code></pre>
5888  * @constructor
5889  * Create a new TabPanel.
5890  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5891  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5892  */
5893 Roo.TabPanel = function(container, config){
5894     /**
5895     * The container element for this TabPanel.
5896     * @type Roo.Element
5897     */
5898     this.el = Roo.get(container, true);
5899     if(config){
5900         if(typeof config == "boolean"){
5901             this.tabPosition = config ? "bottom" : "top";
5902         }else{
5903             Roo.apply(this, config);
5904         }
5905     }
5906     if(this.tabPosition == "bottom"){
5907         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5908         this.el.addClass("x-tabs-bottom");
5909     }
5910     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5911     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5912     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5913     if(Roo.isIE){
5914         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5915     }
5916     if(this.tabPosition != "bottom"){
5917         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5918          * @type Roo.Element
5919          */
5920         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5921         this.el.addClass("x-tabs-top");
5922     }
5923     this.items = [];
5924
5925     this.bodyEl.setStyle("position", "relative");
5926
5927     this.active = null;
5928     this.activateDelegate = this.activate.createDelegate(this);
5929
5930     this.addEvents({
5931         /**
5932          * @event tabchange
5933          * Fires when the active tab changes
5934          * @param {Roo.TabPanel} this
5935          * @param {Roo.TabPanelItem} activePanel The new active tab
5936          */
5937         "tabchange": true,
5938         /**
5939          * @event beforetabchange
5940          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5941          * @param {Roo.TabPanel} this
5942          * @param {Object} e Set cancel to true on this object to cancel the tab change
5943          * @param {Roo.TabPanelItem} tab The tab being changed to
5944          */
5945         "beforetabchange" : true
5946     });
5947
5948     Roo.EventManager.onWindowResize(this.onResize, this);
5949     this.cpad = this.el.getPadding("lr");
5950     this.hiddenCount = 0;
5951
5952
5953     // toolbar on the tabbar support...
5954     if (this.toolbar) {
5955         var tcfg = this.toolbar;
5956         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5957         this.toolbar = new Roo.Toolbar(tcfg);
5958         if (Roo.isSafari) {
5959             var tbl = tcfg.container.child('table', true);
5960             tbl.setAttribute('width', '100%');
5961         }
5962         
5963     }
5964    
5965
5966
5967     Roo.TabPanel.superclass.constructor.call(this);
5968 };
5969
5970 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5971     /*
5972      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5973      */
5974     tabPosition : "top",
5975     /*
5976      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5977      */
5978     currentTabWidth : 0,
5979     /*
5980      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5981      */
5982     minTabWidth : 40,
5983     /*
5984      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5985      */
5986     maxTabWidth : 250,
5987     /*
5988      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5989      */
5990     preferredTabWidth : 175,
5991     /*
5992      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5993      */
5994     resizeTabs : false,
5995     /*
5996      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5997      */
5998     monitorResize : true,
5999     /*
6000      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6001      */
6002     toolbar : false,
6003
6004     /**
6005      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6006      * @param {String} id The id of the div to use <b>or create</b>
6007      * @param {String} text The text for the tab
6008      * @param {String} content (optional) Content to put in the TabPanelItem body
6009      * @param {Boolean} closable (optional) True to create a close icon on the tab
6010      * @return {Roo.TabPanelItem} The created TabPanelItem
6011      */
6012     addTab : function(id, text, content, closable){
6013         var item = new Roo.TabPanelItem(this, id, text, closable);
6014         this.addTabItem(item);
6015         if(content){
6016             item.setContent(content);
6017         }
6018         return item;
6019     },
6020
6021     /**
6022      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6023      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6024      * @return {Roo.TabPanelItem}
6025      */
6026     getTab : function(id){
6027         return this.items[id];
6028     },
6029
6030     /**
6031      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6032      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6033      */
6034     hideTab : function(id){
6035         var t = this.items[id];
6036         if(!t.isHidden()){
6037            t.setHidden(true);
6038            this.hiddenCount++;
6039            this.autoSizeTabs();
6040         }
6041     },
6042
6043     /**
6044      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6045      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6046      */
6047     unhideTab : function(id){
6048         var t = this.items[id];
6049         if(t.isHidden()){
6050            t.setHidden(false);
6051            this.hiddenCount--;
6052            this.autoSizeTabs();
6053         }
6054     },
6055
6056     /**
6057      * Adds an existing {@link Roo.TabPanelItem}.
6058      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6059      */
6060     addTabItem : function(item){
6061         this.items[item.id] = item;
6062         this.items.push(item);
6063         if(this.resizeTabs){
6064            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6065            this.autoSizeTabs();
6066         }else{
6067             item.autoSize();
6068         }
6069     },
6070
6071     /**
6072      * Removes a {@link Roo.TabPanelItem}.
6073      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6074      */
6075     removeTab : function(id){
6076         var items = this.items;
6077         var tab = items[id];
6078         if(!tab) { return; }
6079         var index = items.indexOf(tab);
6080         if(this.active == tab && items.length > 1){
6081             var newTab = this.getNextAvailable(index);
6082             if(newTab) {
6083                 newTab.activate();
6084             }
6085         }
6086         this.stripEl.dom.removeChild(tab.pnode.dom);
6087         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6088             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6089         }
6090         items.splice(index, 1);
6091         delete this.items[tab.id];
6092         tab.fireEvent("close", tab);
6093         tab.purgeListeners();
6094         this.autoSizeTabs();
6095     },
6096
6097     getNextAvailable : function(start){
6098         var items = this.items;
6099         var index = start;
6100         // look for a next tab that will slide over to
6101         // replace the one being removed
6102         while(index < items.length){
6103             var item = items[++index];
6104             if(item && !item.isHidden()){
6105                 return item;
6106             }
6107         }
6108         // if one isn't found select the previous tab (on the left)
6109         index = start;
6110         while(index >= 0){
6111             var item = items[--index];
6112             if(item && !item.isHidden()){
6113                 return item;
6114             }
6115         }
6116         return null;
6117     },
6118
6119     /**
6120      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6121      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6122      */
6123     disableTab : function(id){
6124         var tab = this.items[id];
6125         if(tab && this.active != tab){
6126             tab.disable();
6127         }
6128     },
6129
6130     /**
6131      * Enables a {@link Roo.TabPanelItem} that is disabled.
6132      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6133      */
6134     enableTab : function(id){
6135         var tab = this.items[id];
6136         tab.enable();
6137     },
6138
6139     /**
6140      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6141      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6142      * @return {Roo.TabPanelItem} The TabPanelItem.
6143      */
6144     activate : function(id){
6145         var tab = this.items[id];
6146         if(!tab){
6147             return null;
6148         }
6149         if(tab == this.active || tab.disabled){
6150             return tab;
6151         }
6152         var e = {};
6153         this.fireEvent("beforetabchange", this, e, tab);
6154         if(e.cancel !== true && !tab.disabled){
6155             if(this.active){
6156                 this.active.hide();
6157             }
6158             this.active = this.items[id];
6159             this.active.show();
6160             this.fireEvent("tabchange", this, this.active);
6161         }
6162         return tab;
6163     },
6164
6165     /**
6166      * Gets the active {@link Roo.TabPanelItem}.
6167      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6168      */
6169     getActiveTab : function(){
6170         return this.active;
6171     },
6172
6173     /**
6174      * Updates the tab body element to fit the height of the container element
6175      * for overflow scrolling
6176      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6177      */
6178     syncHeight : function(targetHeight){
6179         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6180         var bm = this.bodyEl.getMargins();
6181         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6182         this.bodyEl.setHeight(newHeight);
6183         return newHeight;
6184     },
6185
6186     onResize : function(){
6187         if(this.monitorResize){
6188             this.autoSizeTabs();
6189         }
6190     },
6191
6192     /**
6193      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6194      */
6195     beginUpdate : function(){
6196         this.updating = true;
6197     },
6198
6199     /**
6200      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6201      */
6202     endUpdate : function(){
6203         this.updating = false;
6204         this.autoSizeTabs();
6205     },
6206
6207     /**
6208      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6209      */
6210     autoSizeTabs : function(){
6211         var count = this.items.length;
6212         var vcount = count - this.hiddenCount;
6213         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6214             return;
6215         }
6216         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6217         var availWidth = Math.floor(w / vcount);
6218         var b = this.stripBody;
6219         if(b.getWidth() > w){
6220             var tabs = this.items;
6221             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6222             if(availWidth < this.minTabWidth){
6223                 /*if(!this.sleft){    // incomplete scrolling code
6224                     this.createScrollButtons();
6225                 }
6226                 this.showScroll();
6227                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6228             }
6229         }else{
6230             if(this.currentTabWidth < this.preferredTabWidth){
6231                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6232             }
6233         }
6234     },
6235
6236     /**
6237      * Returns the number of tabs in this TabPanel.
6238      * @return {Number}
6239      */
6240      getCount : function(){
6241          return this.items.length;
6242      },
6243
6244     /**
6245      * Resizes all the tabs to the passed width
6246      * @param {Number} The new width
6247      */
6248     setTabWidth : function(width){
6249         this.currentTabWidth = width;
6250         for(var i = 0, len = this.items.length; i < len; i++) {
6251                 if(!this.items[i].isHidden()) {
6252                 this.items[i].setWidth(width);
6253             }
6254         }
6255     },
6256
6257     /**
6258      * Destroys this TabPanel
6259      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6260      */
6261     destroy : function(removeEl){
6262         Roo.EventManager.removeResizeListener(this.onResize, this);
6263         for(var i = 0, len = this.items.length; i < len; i++){
6264             this.items[i].purgeListeners();
6265         }
6266         if(removeEl === true){
6267             this.el.update("");
6268             this.el.remove();
6269         }
6270     }
6271 });
6272
6273 /**
6274  * @class Roo.TabPanelItem
6275  * @extends Roo.util.Observable
6276  * Represents an individual item (tab plus body) in a TabPanel.
6277  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6278  * @param {String} id The id of this TabPanelItem
6279  * @param {String} text The text for the tab of this TabPanelItem
6280  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6281  */
6282 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6283     /**
6284      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6285      * @type Roo.TabPanel
6286      */
6287     this.tabPanel = tabPanel;
6288     /**
6289      * The id for this TabPanelItem
6290      * @type String
6291      */
6292     this.id = id;
6293     /** @private */
6294     this.disabled = false;
6295     /** @private */
6296     this.text = text;
6297     /** @private */
6298     this.loaded = false;
6299     this.closable = closable;
6300
6301     /**
6302      * The body element for this TabPanelItem.
6303      * @type Roo.Element
6304      */
6305     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6306     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6307     this.bodyEl.setStyle("display", "block");
6308     this.bodyEl.setStyle("zoom", "1");
6309     this.hideAction();
6310
6311     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6312     /** @private */
6313     this.el = Roo.get(els.el, true);
6314     this.inner = Roo.get(els.inner, true);
6315     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6316     this.pnode = Roo.get(els.el.parentNode, true);
6317     this.el.on("mousedown", this.onTabMouseDown, this);
6318     this.el.on("click", this.onTabClick, this);
6319     /** @private */
6320     if(closable){
6321         var c = Roo.get(els.close, true);
6322         c.dom.title = this.closeText;
6323         c.addClassOnOver("close-over");
6324         c.on("click", this.closeClick, this);
6325      }
6326
6327     this.addEvents({
6328          /**
6329          * @event activate
6330          * Fires when this tab becomes the active tab.
6331          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6332          * @param {Roo.TabPanelItem} this
6333          */
6334         "activate": true,
6335         /**
6336          * @event beforeclose
6337          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6338          * @param {Roo.TabPanelItem} this
6339          * @param {Object} e Set cancel to true on this object to cancel the close.
6340          */
6341         "beforeclose": true,
6342         /**
6343          * @event close
6344          * Fires when this tab is closed.
6345          * @param {Roo.TabPanelItem} this
6346          */
6347          "close": true,
6348         /**
6349          * @event deactivate
6350          * Fires when this tab is no longer the active tab.
6351          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6352          * @param {Roo.TabPanelItem} this
6353          */
6354          "deactivate" : true
6355     });
6356     this.hidden = false;
6357
6358     Roo.TabPanelItem.superclass.constructor.call(this);
6359 };
6360
6361 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6362     purgeListeners : function(){
6363        Roo.util.Observable.prototype.purgeListeners.call(this);
6364        this.el.removeAllListeners();
6365     },
6366     /**
6367      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6368      */
6369     show : function(){
6370         this.pnode.addClass("on");
6371         this.showAction();
6372         if(Roo.isOpera){
6373             this.tabPanel.stripWrap.repaint();
6374         }
6375         this.fireEvent("activate", this.tabPanel, this);
6376     },
6377
6378     /**
6379      * Returns true if this tab is the active tab.
6380      * @return {Boolean}
6381      */
6382     isActive : function(){
6383         return this.tabPanel.getActiveTab() == this;
6384     },
6385
6386     /**
6387      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6388      */
6389     hide : function(){
6390         this.pnode.removeClass("on");
6391         this.hideAction();
6392         this.fireEvent("deactivate", this.tabPanel, this);
6393     },
6394
6395     hideAction : function(){
6396         this.bodyEl.hide();
6397         this.bodyEl.setStyle("position", "absolute");
6398         this.bodyEl.setLeft("-20000px");
6399         this.bodyEl.setTop("-20000px");
6400     },
6401
6402     showAction : function(){
6403         this.bodyEl.setStyle("position", "relative");
6404         this.bodyEl.setTop("");
6405         this.bodyEl.setLeft("");
6406         this.bodyEl.show();
6407     },
6408
6409     /**
6410      * Set the tooltip for the tab.
6411      * @param {String} tooltip The tab's tooltip
6412      */
6413     setTooltip : function(text){
6414         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6415             this.textEl.dom.qtip = text;
6416             this.textEl.dom.removeAttribute('title');
6417         }else{
6418             this.textEl.dom.title = text;
6419         }
6420     },
6421
6422     onTabClick : function(e){
6423         e.preventDefault();
6424         this.tabPanel.activate(this.id);
6425     },
6426
6427     onTabMouseDown : function(e){
6428         e.preventDefault();
6429         this.tabPanel.activate(this.id);
6430     },
6431
6432     getWidth : function(){
6433         return this.inner.getWidth();
6434     },
6435
6436     setWidth : function(width){
6437         var iwidth = width - this.pnode.getPadding("lr");
6438         this.inner.setWidth(iwidth);
6439         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6440         this.pnode.setWidth(width);
6441     },
6442
6443     /**
6444      * Show or hide the tab
6445      * @param {Boolean} hidden True to hide or false to show.
6446      */
6447     setHidden : function(hidden){
6448         this.hidden = hidden;
6449         this.pnode.setStyle("display", hidden ? "none" : "");
6450     },
6451
6452     /**
6453      * Returns true if this tab is "hidden"
6454      * @return {Boolean}
6455      */
6456     isHidden : function(){
6457         return this.hidden;
6458     },
6459
6460     /**
6461      * Returns the text for this tab
6462      * @return {String}
6463      */
6464     getText : function(){
6465         return this.text;
6466     },
6467
6468     autoSize : function(){
6469         //this.el.beginMeasure();
6470         this.textEl.setWidth(1);
6471         /*
6472          *  #2804 [new] Tabs in Roojs
6473          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6474          */
6475         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6476         //this.el.endMeasure();
6477     },
6478
6479     /**
6480      * Sets the text for the tab (Note: this also sets the tooltip text)
6481      * @param {String} text The tab's text and tooltip
6482      */
6483     setText : function(text){
6484         this.text = text;
6485         this.textEl.update(text);
6486         this.setTooltip(text);
6487         if(!this.tabPanel.resizeTabs){
6488             this.autoSize();
6489         }
6490     },
6491     /**
6492      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6493      */
6494     activate : function(){
6495         this.tabPanel.activate(this.id);
6496     },
6497
6498     /**
6499      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6500      */
6501     disable : function(){
6502         if(this.tabPanel.active != this){
6503             this.disabled = true;
6504             this.pnode.addClass("disabled");
6505         }
6506     },
6507
6508     /**
6509      * Enables this TabPanelItem if it was previously disabled.
6510      */
6511     enable : function(){
6512         this.disabled = false;
6513         this.pnode.removeClass("disabled");
6514     },
6515
6516     /**
6517      * Sets the content for this TabPanelItem.
6518      * @param {String} content The content
6519      * @param {Boolean} loadScripts true to look for and load scripts
6520      */
6521     setContent : function(content, loadScripts){
6522         this.bodyEl.update(content, loadScripts);
6523     },
6524
6525     /**
6526      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6527      * @return {Roo.UpdateManager} The UpdateManager
6528      */
6529     getUpdateManager : function(){
6530         return this.bodyEl.getUpdateManager();
6531     },
6532
6533     /**
6534      * Set a URL to be used to load the content for this TabPanelItem.
6535      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6536      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
6537      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
6538      * @return {Roo.UpdateManager} The UpdateManager
6539      */
6540     setUrl : function(url, params, loadOnce){
6541         if(this.refreshDelegate){
6542             this.un('activate', this.refreshDelegate);
6543         }
6544         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6545         this.on("activate", this.refreshDelegate);
6546         return this.bodyEl.getUpdateManager();
6547     },
6548
6549     /** @private */
6550     _handleRefresh : function(url, params, loadOnce){
6551         if(!loadOnce || !this.loaded){
6552             var updater = this.bodyEl.getUpdateManager();
6553             updater.update(url, params, this._setLoaded.createDelegate(this));
6554         }
6555     },
6556
6557     /**
6558      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6559      *   Will fail silently if the setUrl method has not been called.
6560      *   This does not activate the panel, just updates its content.
6561      */
6562     refresh : function(){
6563         if(this.refreshDelegate){
6564            this.loaded = false;
6565            this.refreshDelegate();
6566         }
6567     },
6568
6569     /** @private */
6570     _setLoaded : function(){
6571         this.loaded = true;
6572     },
6573
6574     /** @private */
6575     closeClick : function(e){
6576         var o = {};
6577         e.stopEvent();
6578         this.fireEvent("beforeclose", this, o);
6579         if(o.cancel !== true){
6580             this.tabPanel.removeTab(this.id);
6581         }
6582     },
6583     /**
6584      * The text displayed in the tooltip for the close icon.
6585      * @type String
6586      */
6587     closeText : "Close this tab"
6588 });
6589
6590 /** @private */
6591 Roo.TabPanel.prototype.createStrip = function(container){
6592     var strip = document.createElement("div");
6593     strip.className = "x-tabs-wrap";
6594     container.appendChild(strip);
6595     return strip;
6596 };
6597 /** @private */
6598 Roo.TabPanel.prototype.createStripList = function(strip){
6599     // div wrapper for retard IE
6600     // returns the "tr" element.
6601     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6602         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6603         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6604     return strip.firstChild.firstChild.firstChild.firstChild;
6605 };
6606 /** @private */
6607 Roo.TabPanel.prototype.createBody = function(container){
6608     var body = document.createElement("div");
6609     Roo.id(body, "tab-body");
6610     Roo.fly(body).addClass("x-tabs-body");
6611     container.appendChild(body);
6612     return body;
6613 };
6614 /** @private */
6615 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6616     var body = Roo.getDom(id);
6617     if(!body){
6618         body = document.createElement("div");
6619         body.id = id;
6620     }
6621     Roo.fly(body).addClass("x-tabs-item-body");
6622     bodyEl.insertBefore(body, bodyEl.firstChild);
6623     return body;
6624 };
6625 /** @private */
6626 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6627     var td = document.createElement("td");
6628     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6629     //stripEl.appendChild(td);
6630     if(closable){
6631         td.className = "x-tabs-closable";
6632         if(!this.closeTpl){
6633             this.closeTpl = new Roo.Template(
6634                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6635                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6636                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6637             );
6638         }
6639         var el = this.closeTpl.overwrite(td, {"text": text});
6640         var close = el.getElementsByTagName("div")[0];
6641         var inner = el.getElementsByTagName("em")[0];
6642         return {"el": el, "close": close, "inner": inner};
6643     } else {
6644         if(!this.tabTpl){
6645             this.tabTpl = new Roo.Template(
6646                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6647                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6648             );
6649         }
6650         var el = this.tabTpl.overwrite(td, {"text": text});
6651         var inner = el.getElementsByTagName("em")[0];
6652         return {"el": el, "inner": inner};
6653     }
6654 };/*
6655  * Based on:
6656  * Ext JS Library 1.1.1
6657  * Copyright(c) 2006-2007, Ext JS, LLC.
6658  *
6659  * Originally Released Under LGPL - original licence link has changed is not relivant.
6660  *
6661  * Fork - LGPL
6662  * <script type="text/javascript">
6663  */
6664
6665 /**
6666  * @class Roo.Button
6667  * @extends Roo.util.Observable
6668  * Simple Button class
6669  * @cfg {String} text The button text
6670  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6671  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6672  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6673  * @cfg {Object} scope The scope of the handler
6674  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6675  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6676  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6677  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6678  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6679  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6680    applies if enableToggle = true)
6681  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6682  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6683   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6684  * @constructor
6685  * Create a new button
6686  * @param {Object} config The config object
6687  */
6688 Roo.Button = function(renderTo, config)
6689 {
6690     if (!config) {
6691         config = renderTo;
6692         renderTo = config.renderTo || false;
6693     }
6694     
6695     Roo.apply(this, config);
6696     this.addEvents({
6697         /**
6698              * @event click
6699              * Fires when this button is clicked
6700              * @param {Button} this
6701              * @param {EventObject} e The click event
6702              */
6703             "click" : true,
6704         /**
6705              * @event toggle
6706              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6707              * @param {Button} this
6708              * @param {Boolean} pressed
6709              */
6710             "toggle" : true,
6711         /**
6712              * @event mouseover
6713              * Fires when the mouse hovers over the button
6714              * @param {Button} this
6715              * @param {Event} e The event object
6716              */
6717         'mouseover' : true,
6718         /**
6719              * @event mouseout
6720              * Fires when the mouse exits the button
6721              * @param {Button} this
6722              * @param {Event} e The event object
6723              */
6724         'mouseout': true,
6725          /**
6726              * @event render
6727              * Fires when the button is rendered
6728              * @param {Button} this
6729              */
6730         'render': true
6731     });
6732     if(this.menu){
6733         this.menu = Roo.menu.MenuMgr.get(this.menu);
6734     }
6735     // register listeners first!!  - so render can be captured..
6736     Roo.util.Observable.call(this);
6737     if(renderTo){
6738         this.render(renderTo);
6739     }
6740     
6741   
6742 };
6743
6744 Roo.extend(Roo.Button, Roo.util.Observable, {
6745     /**
6746      * 
6747      */
6748     
6749     /**
6750      * Read-only. True if this button is hidden
6751      * @type Boolean
6752      */
6753     hidden : false,
6754     /**
6755      * Read-only. True if this button is disabled
6756      * @type Boolean
6757      */
6758     disabled : false,
6759     /**
6760      * Read-only. True if this button is pressed (only if enableToggle = true)
6761      * @type Boolean
6762      */
6763     pressed : false,
6764
6765     /**
6766      * @cfg {Number} tabIndex 
6767      * The DOM tabIndex for this button (defaults to undefined)
6768      */
6769     tabIndex : undefined,
6770
6771     /**
6772      * @cfg {Boolean} enableToggle
6773      * True to enable pressed/not pressed toggling (defaults to false)
6774      */
6775     enableToggle: false,
6776     /**
6777      * @cfg {Mixed} menu
6778      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6779      */
6780     menu : undefined,
6781     /**
6782      * @cfg {String} menuAlign
6783      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6784      */
6785     menuAlign : "tl-bl?",
6786
6787     /**
6788      * @cfg {String} iconCls
6789      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6790      */
6791     iconCls : undefined,
6792     /**
6793      * @cfg {String} type
6794      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6795      */
6796     type : 'button',
6797
6798     // private
6799     menuClassTarget: 'tr',
6800
6801     /**
6802      * @cfg {String} clickEvent
6803      * The type of event to map to the button's event handler (defaults to 'click')
6804      */
6805     clickEvent : 'click',
6806
6807     /**
6808      * @cfg {Boolean} handleMouseEvents
6809      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6810      */
6811     handleMouseEvents : true,
6812
6813     /**
6814      * @cfg {String} tooltipType
6815      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6816      */
6817     tooltipType : 'qtip',
6818
6819     /**
6820      * @cfg {String} cls
6821      * A CSS class to apply to the button's main element.
6822      */
6823     
6824     /**
6825      * @cfg {Roo.Template} template (Optional)
6826      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6827      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6828      * require code modifications if required elements (e.g. a button) aren't present.
6829      */
6830
6831     // private
6832     render : function(renderTo){
6833         var btn;
6834         if(this.hideParent){
6835             this.parentEl = Roo.get(renderTo);
6836         }
6837         if(!this.dhconfig){
6838             if(!this.template){
6839                 if(!Roo.Button.buttonTemplate){
6840                     // hideous table template
6841                     Roo.Button.buttonTemplate = new Roo.Template(
6842                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6843                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
6844                         "</tr></tbody></table>");
6845                 }
6846                 this.template = Roo.Button.buttonTemplate;
6847             }
6848             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6849             var btnEl = btn.child("button:first");
6850             btnEl.on('focus', this.onFocus, this);
6851             btnEl.on('blur', this.onBlur, this);
6852             if(this.cls){
6853                 btn.addClass(this.cls);
6854             }
6855             if(this.icon){
6856                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6857             }
6858             if(this.iconCls){
6859                 btnEl.addClass(this.iconCls);
6860                 if(!this.cls){
6861                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6862                 }
6863             }
6864             if(this.tabIndex !== undefined){
6865                 btnEl.dom.tabIndex = this.tabIndex;
6866             }
6867             if(this.tooltip){
6868                 if(typeof this.tooltip == 'object'){
6869                     Roo.QuickTips.tips(Roo.apply({
6870                           target: btnEl.id
6871                     }, this.tooltip));
6872                 } else {
6873                     btnEl.dom[this.tooltipType] = this.tooltip;
6874                 }
6875             }
6876         }else{
6877             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6878         }
6879         this.el = btn;
6880         if(this.id){
6881             this.el.dom.id = this.el.id = this.id;
6882         }
6883         if(this.menu){
6884             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6885             this.menu.on("show", this.onMenuShow, this);
6886             this.menu.on("hide", this.onMenuHide, this);
6887         }
6888         btn.addClass("x-btn");
6889         if(Roo.isIE && !Roo.isIE7){
6890             this.autoWidth.defer(1, this);
6891         }else{
6892             this.autoWidth();
6893         }
6894         if(this.handleMouseEvents){
6895             btn.on("mouseover", this.onMouseOver, this);
6896             btn.on("mouseout", this.onMouseOut, this);
6897             btn.on("mousedown", this.onMouseDown, this);
6898         }
6899         btn.on(this.clickEvent, this.onClick, this);
6900         //btn.on("mouseup", this.onMouseUp, this);
6901         if(this.hidden){
6902             this.hide();
6903         }
6904         if(this.disabled){
6905             this.disable();
6906         }
6907         Roo.ButtonToggleMgr.register(this);
6908         if(this.pressed){
6909             this.el.addClass("x-btn-pressed");
6910         }
6911         if(this.repeat){
6912             var repeater = new Roo.util.ClickRepeater(btn,
6913                 typeof this.repeat == "object" ? this.repeat : {}
6914             );
6915             repeater.on("click", this.onClick,  this);
6916         }
6917         
6918         this.fireEvent('render', this);
6919         
6920     },
6921     /**
6922      * Returns the button's underlying element
6923      * @return {Roo.Element} The element
6924      */
6925     getEl : function(){
6926         return this.el;  
6927     },
6928     
6929     /**
6930      * Destroys this Button and removes any listeners.
6931      */
6932     destroy : function(){
6933         Roo.ButtonToggleMgr.unregister(this);
6934         this.el.removeAllListeners();
6935         this.purgeListeners();
6936         this.el.remove();
6937     },
6938
6939     // private
6940     autoWidth : function(){
6941         if(this.el){
6942             this.el.setWidth("auto");
6943             if(Roo.isIE7 && Roo.isStrict){
6944                 var ib = this.el.child('button');
6945                 if(ib && ib.getWidth() > 20){
6946                     ib.clip();
6947                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6948                 }
6949             }
6950             if(this.minWidth){
6951                 if(this.hidden){
6952                     this.el.beginMeasure();
6953                 }
6954                 if(this.el.getWidth() < this.minWidth){
6955                     this.el.setWidth(this.minWidth);
6956                 }
6957                 if(this.hidden){
6958                     this.el.endMeasure();
6959                 }
6960             }
6961         }
6962     },
6963
6964     /**
6965      * Assigns this button's click handler
6966      * @param {Function} handler The function to call when the button is clicked
6967      * @param {Object} scope (optional) Scope for the function passed in
6968      */
6969     setHandler : function(handler, scope){
6970         this.handler = handler;
6971         this.scope = scope;  
6972     },
6973     
6974     /**
6975      * Sets this button's text
6976      * @param {String} text The button text
6977      */
6978     setText : function(text){
6979         this.text = text;
6980         if(this.el){
6981             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6982         }
6983         this.autoWidth();
6984     },
6985     
6986     /**
6987      * Gets the text for this button
6988      * @return {String} The button text
6989      */
6990     getText : function(){
6991         return this.text;  
6992     },
6993     
6994     /**
6995      * Show this button
6996      */
6997     show: function(){
6998         this.hidden = false;
6999         if(this.el){
7000             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7001         }
7002     },
7003     
7004     /**
7005      * Hide this button
7006      */
7007     hide: function(){
7008         this.hidden = true;
7009         if(this.el){
7010             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7011         }
7012     },
7013     
7014     /**
7015      * Convenience function for boolean show/hide
7016      * @param {Boolean} visible True to show, false to hide
7017      */
7018     setVisible: function(visible){
7019         if(visible) {
7020             this.show();
7021         }else{
7022             this.hide();
7023         }
7024     },
7025     
7026     /**
7027      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7028      * @param {Boolean} state (optional) Force a particular state
7029      */
7030     toggle : function(state){
7031         state = state === undefined ? !this.pressed : state;
7032         if(state != this.pressed){
7033             if(state){
7034                 this.el.addClass("x-btn-pressed");
7035                 this.pressed = true;
7036                 this.fireEvent("toggle", this, true);
7037             }else{
7038                 this.el.removeClass("x-btn-pressed");
7039                 this.pressed = false;
7040                 this.fireEvent("toggle", this, false);
7041             }
7042             if(this.toggleHandler){
7043                 this.toggleHandler.call(this.scope || this, this, state);
7044             }
7045         }
7046     },
7047     
7048     /**
7049      * Focus the button
7050      */
7051     focus : function(){
7052         this.el.child('button:first').focus();
7053     },
7054     
7055     /**
7056      * Disable this button
7057      */
7058     disable : function(){
7059         if(this.el){
7060             this.el.addClass("x-btn-disabled");
7061         }
7062         this.disabled = true;
7063     },
7064     
7065     /**
7066      * Enable this button
7067      */
7068     enable : function(){
7069         if(this.el){
7070             this.el.removeClass("x-btn-disabled");
7071         }
7072         this.disabled = false;
7073     },
7074
7075     /**
7076      * Convenience function for boolean enable/disable
7077      * @param {Boolean} enabled True to enable, false to disable
7078      */
7079     setDisabled : function(v){
7080         this[v !== true ? "enable" : "disable"]();
7081     },
7082
7083     // private
7084     onClick : function(e)
7085     {
7086         if(e){
7087             e.preventDefault();
7088         }
7089         if(e.button != 0){
7090             return;
7091         }
7092         if(!this.disabled){
7093             if(this.enableToggle){
7094                 this.toggle();
7095             }
7096             if(this.menu && !this.menu.isVisible()){
7097                 this.menu.show(this.el, this.menuAlign);
7098             }
7099             this.fireEvent("click", this, e);
7100             if(this.handler){
7101                 this.el.removeClass("x-btn-over");
7102                 this.handler.call(this.scope || this, this, e);
7103             }
7104         }
7105     },
7106     // private
7107     onMouseOver : function(e){
7108         if(!this.disabled){
7109             this.el.addClass("x-btn-over");
7110             this.fireEvent('mouseover', this, e);
7111         }
7112     },
7113     // private
7114     onMouseOut : function(e){
7115         if(!e.within(this.el,  true)){
7116             this.el.removeClass("x-btn-over");
7117             this.fireEvent('mouseout', this, e);
7118         }
7119     },
7120     // private
7121     onFocus : function(e){
7122         if(!this.disabled){
7123             this.el.addClass("x-btn-focus");
7124         }
7125     },
7126     // private
7127     onBlur : function(e){
7128         this.el.removeClass("x-btn-focus");
7129     },
7130     // private
7131     onMouseDown : function(e){
7132         if(!this.disabled && e.button == 0){
7133             this.el.addClass("x-btn-click");
7134             Roo.get(document).on('mouseup', this.onMouseUp, this);
7135         }
7136     },
7137     // private
7138     onMouseUp : function(e){
7139         if(e.button == 0){
7140             this.el.removeClass("x-btn-click");
7141             Roo.get(document).un('mouseup', this.onMouseUp, this);
7142         }
7143     },
7144     // private
7145     onMenuShow : function(e){
7146         this.el.addClass("x-btn-menu-active");
7147     },
7148     // private
7149     onMenuHide : function(e){
7150         this.el.removeClass("x-btn-menu-active");
7151     }   
7152 });
7153
7154 // Private utility class used by Button
7155 Roo.ButtonToggleMgr = function(){
7156    var groups = {};
7157    
7158    function toggleGroup(btn, state){
7159        if(state){
7160            var g = groups[btn.toggleGroup];
7161            for(var i = 0, l = g.length; i < l; i++){
7162                if(g[i] != btn){
7163                    g[i].toggle(false);
7164                }
7165            }
7166        }
7167    }
7168    
7169    return {
7170        register : function(btn){
7171            if(!btn.toggleGroup){
7172                return;
7173            }
7174            var g = groups[btn.toggleGroup];
7175            if(!g){
7176                g = groups[btn.toggleGroup] = [];
7177            }
7178            g.push(btn);
7179            btn.on("toggle", toggleGroup);
7180        },
7181        
7182        unregister : function(btn){
7183            if(!btn.toggleGroup){
7184                return;
7185            }
7186            var g = groups[btn.toggleGroup];
7187            if(g){
7188                g.remove(btn);
7189                btn.un("toggle", toggleGroup);
7190            }
7191        }
7192    };
7193 }();/*
7194  * Based on:
7195  * Ext JS Library 1.1.1
7196  * Copyright(c) 2006-2007, Ext JS, LLC.
7197  *
7198  * Originally Released Under LGPL - original licence link has changed is not relivant.
7199  *
7200  * Fork - LGPL
7201  * <script type="text/javascript">
7202  */
7203  
7204 /**
7205  * @class Roo.SplitButton
7206  * @extends Roo.Button
7207  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7208  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7209  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7210  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7211  * @cfg {String} arrowTooltip The title attribute of the arrow
7212  * @constructor
7213  * Create a new menu button
7214  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7215  * @param {Object} config The config object
7216  */
7217 Roo.SplitButton = function(renderTo, config){
7218     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7219     /**
7220      * @event arrowclick
7221      * Fires when this button's arrow is clicked
7222      * @param {SplitButton} this
7223      * @param {EventObject} e The click event
7224      */
7225     this.addEvents({"arrowclick":true});
7226 };
7227
7228 Roo.extend(Roo.SplitButton, Roo.Button, {
7229     render : function(renderTo){
7230         // this is one sweet looking template!
7231         var tpl = new Roo.Template(
7232             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7233             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7234             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
7235             "</tbody></table></td><td>",
7236             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7237             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
7238             "</tbody></table></td></tr></table>"
7239         );
7240         var btn = tpl.append(renderTo, [this.text, this.type], true);
7241         var btnEl = btn.child("button");
7242         if(this.cls){
7243             btn.addClass(this.cls);
7244         }
7245         if(this.icon){
7246             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7247         }
7248         if(this.iconCls){
7249             btnEl.addClass(this.iconCls);
7250             if(!this.cls){
7251                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7252             }
7253         }
7254         this.el = btn;
7255         if(this.handleMouseEvents){
7256             btn.on("mouseover", this.onMouseOver, this);
7257             btn.on("mouseout", this.onMouseOut, this);
7258             btn.on("mousedown", this.onMouseDown, this);
7259             btn.on("mouseup", this.onMouseUp, this);
7260         }
7261         btn.on(this.clickEvent, this.onClick, this);
7262         if(this.tooltip){
7263             if(typeof this.tooltip == 'object'){
7264                 Roo.QuickTips.tips(Roo.apply({
7265                       target: btnEl.id
7266                 }, this.tooltip));
7267             } else {
7268                 btnEl.dom[this.tooltipType] = this.tooltip;
7269             }
7270         }
7271         if(this.arrowTooltip){
7272             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7273         }
7274         if(this.hidden){
7275             this.hide();
7276         }
7277         if(this.disabled){
7278             this.disable();
7279         }
7280         if(this.pressed){
7281             this.el.addClass("x-btn-pressed");
7282         }
7283         if(Roo.isIE && !Roo.isIE7){
7284             this.autoWidth.defer(1, this);
7285         }else{
7286             this.autoWidth();
7287         }
7288         if(this.menu){
7289             this.menu.on("show", this.onMenuShow, this);
7290             this.menu.on("hide", this.onMenuHide, this);
7291         }
7292         this.fireEvent('render', this);
7293     },
7294
7295     // private
7296     autoWidth : function(){
7297         if(this.el){
7298             var tbl = this.el.child("table:first");
7299             var tbl2 = this.el.child("table:last");
7300             this.el.setWidth("auto");
7301             tbl.setWidth("auto");
7302             if(Roo.isIE7 && Roo.isStrict){
7303                 var ib = this.el.child('button:first');
7304                 if(ib && ib.getWidth() > 20){
7305                     ib.clip();
7306                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7307                 }
7308             }
7309             if(this.minWidth){
7310                 if(this.hidden){
7311                     this.el.beginMeasure();
7312                 }
7313                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7314                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7315                 }
7316                 if(this.hidden){
7317                     this.el.endMeasure();
7318                 }
7319             }
7320             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7321         } 
7322     },
7323     /**
7324      * Sets this button's click handler
7325      * @param {Function} handler The function to call when the button is clicked
7326      * @param {Object} scope (optional) Scope for the function passed above
7327      */
7328     setHandler : function(handler, scope){
7329         this.handler = handler;
7330         this.scope = scope;  
7331     },
7332     
7333     /**
7334      * Sets this button's arrow click handler
7335      * @param {Function} handler The function to call when the arrow is clicked
7336      * @param {Object} scope (optional) Scope for the function passed above
7337      */
7338     setArrowHandler : function(handler, scope){
7339         this.arrowHandler = handler;
7340         this.scope = scope;  
7341     },
7342     
7343     /**
7344      * Focus the button
7345      */
7346     focus : function(){
7347         if(this.el){
7348             this.el.child("button:first").focus();
7349         }
7350     },
7351
7352     // private
7353     onClick : function(e){
7354         e.preventDefault();
7355         if(!this.disabled){
7356             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7357                 if(this.menu && !this.menu.isVisible()){
7358                     this.menu.show(this.el, this.menuAlign);
7359                 }
7360                 this.fireEvent("arrowclick", this, e);
7361                 if(this.arrowHandler){
7362                     this.arrowHandler.call(this.scope || this, this, e);
7363                 }
7364             }else{
7365                 this.fireEvent("click", this, e);
7366                 if(this.handler){
7367                     this.handler.call(this.scope || this, this, e);
7368                 }
7369             }
7370         }
7371     },
7372     // private
7373     onMouseDown : function(e){
7374         if(!this.disabled){
7375             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7376         }
7377     },
7378     // private
7379     onMouseUp : function(e){
7380         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7381     }   
7382 });
7383
7384
7385 // backwards compat
7386 Roo.MenuButton = Roo.SplitButton;/*
7387  * Based on:
7388  * Ext JS Library 1.1.1
7389  * Copyright(c) 2006-2007, Ext JS, LLC.
7390  *
7391  * Originally Released Under LGPL - original licence link has changed is not relivant.
7392  *
7393  * Fork - LGPL
7394  * <script type="text/javascript">
7395  */
7396
7397 /**
7398  * @class Roo.Toolbar
7399  * Basic Toolbar class.
7400  * @constructor
7401  * Creates a new Toolbar
7402  * @param {Object} container The config object
7403  */ 
7404 Roo.Toolbar = function(container, buttons, config)
7405 {
7406     /// old consturctor format still supported..
7407     if(container instanceof Array){ // omit the container for later rendering
7408         buttons = container;
7409         config = buttons;
7410         container = null;
7411     }
7412     if (typeof(container) == 'object' && container.xtype) {
7413         config = container;
7414         container = config.container;
7415         buttons = config.buttons || []; // not really - use items!!
7416     }
7417     var xitems = [];
7418     if (config && config.items) {
7419         xitems = config.items;
7420         delete config.items;
7421     }
7422     Roo.apply(this, config);
7423     this.buttons = buttons;
7424     
7425     if(container){
7426         this.render(container);
7427     }
7428     this.xitems = xitems;
7429     Roo.each(xitems, function(b) {
7430         this.add(b);
7431     }, this);
7432     
7433 };
7434
7435 Roo.Toolbar.prototype = {
7436     /**
7437      * @cfg {Array} items
7438      * array of button configs or elements to add (will be converted to a MixedCollection)
7439      */
7440     
7441     /**
7442      * @cfg {String/HTMLElement/Element} container
7443      * The id or element that will contain the toolbar
7444      */
7445     // private
7446     render : function(ct){
7447         this.el = Roo.get(ct);
7448         if(this.cls){
7449             this.el.addClass(this.cls);
7450         }
7451         // using a table allows for vertical alignment
7452         // 100% width is needed by Safari...
7453         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7454         this.tr = this.el.child("tr", true);
7455         var autoId = 0;
7456         this.items = new Roo.util.MixedCollection(false, function(o){
7457             return o.id || ("item" + (++autoId));
7458         });
7459         if(this.buttons){
7460             this.add.apply(this, this.buttons);
7461             delete this.buttons;
7462         }
7463     },
7464
7465     /**
7466      * Adds element(s) to the toolbar -- this function takes a variable number of 
7467      * arguments of mixed type and adds them to the toolbar.
7468      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7469      * <ul>
7470      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7471      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7472      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7473      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7474      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7475      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7476      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7477      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7478      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7479      * </ul>
7480      * @param {Mixed} arg2
7481      * @param {Mixed} etc.
7482      */
7483     add : function(){
7484         var a = arguments, l = a.length;
7485         for(var i = 0; i < l; i++){
7486             this._add(a[i]);
7487         }
7488     },
7489     // private..
7490     _add : function(el) {
7491         
7492         if (el.xtype) {
7493             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7494         }
7495         
7496         if (el.applyTo){ // some kind of form field
7497             return this.addField(el);
7498         } 
7499         if (el.render){ // some kind of Toolbar.Item
7500             return this.addItem(el);
7501         }
7502         if (typeof el == "string"){ // string
7503             if(el == "separator" || el == "-"){
7504                 return this.addSeparator();
7505             }
7506             if (el == " "){
7507                 return this.addSpacer();
7508             }
7509             if(el == "->"){
7510                 return this.addFill();
7511             }
7512             return this.addText(el);
7513             
7514         }
7515         if(el.tagName){ // element
7516             return this.addElement(el);
7517         }
7518         if(typeof el == "object"){ // must be button config?
7519             return this.addButton(el);
7520         }
7521         // and now what?!?!
7522         return false;
7523         
7524     },
7525     
7526     /**
7527      * Add an Xtype element
7528      * @param {Object} xtype Xtype Object
7529      * @return {Object} created Object
7530      */
7531     addxtype : function(e){
7532         return this.add(e);  
7533     },
7534     
7535     /**
7536      * Returns the Element for this toolbar.
7537      * @return {Roo.Element}
7538      */
7539     getEl : function(){
7540         return this.el;  
7541     },
7542     
7543     /**
7544      * Adds a separator
7545      * @return {Roo.Toolbar.Item} The separator item
7546      */
7547     addSeparator : function(){
7548         return this.addItem(new Roo.Toolbar.Separator());
7549     },
7550
7551     /**
7552      * Adds a spacer element
7553      * @return {Roo.Toolbar.Spacer} The spacer item
7554      */
7555     addSpacer : function(){
7556         return this.addItem(new Roo.Toolbar.Spacer());
7557     },
7558
7559     /**
7560      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7561      * @return {Roo.Toolbar.Fill} The fill item
7562      */
7563     addFill : function(){
7564         return this.addItem(new Roo.Toolbar.Fill());
7565     },
7566
7567     /**
7568      * Adds any standard HTML element to the toolbar
7569      * @param {String/HTMLElement/Element} el The element or id of the element to add
7570      * @return {Roo.Toolbar.Item} The element's item
7571      */
7572     addElement : function(el){
7573         return this.addItem(new Roo.Toolbar.Item(el));
7574     },
7575     /**
7576      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7577      * @type Roo.util.MixedCollection  
7578      */
7579     items : false,
7580      
7581     /**
7582      * Adds any Toolbar.Item or subclass
7583      * @param {Roo.Toolbar.Item} item
7584      * @return {Roo.Toolbar.Item} The item
7585      */
7586     addItem : function(item){
7587         var td = this.nextBlock();
7588         item.render(td);
7589         this.items.add(item);
7590         return item;
7591     },
7592     
7593     /**
7594      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7595      * @param {Object/Array} config A button config or array of configs
7596      * @return {Roo.Toolbar.Button/Array}
7597      */
7598     addButton : function(config){
7599         if(config instanceof Array){
7600             var buttons = [];
7601             for(var i = 0, len = config.length; i < len; i++) {
7602                 buttons.push(this.addButton(config[i]));
7603             }
7604             return buttons;
7605         }
7606         var b = config;
7607         if(!(config instanceof Roo.Toolbar.Button)){
7608             b = config.split ?
7609                 new Roo.Toolbar.SplitButton(config) :
7610                 new Roo.Toolbar.Button(config);
7611         }
7612         var td = this.nextBlock();
7613         b.render(td);
7614         this.items.add(b);
7615         return b;
7616     },
7617     
7618     /**
7619      * Adds text to the toolbar
7620      * @param {String} text The text to add
7621      * @return {Roo.Toolbar.Item} The element's item
7622      */
7623     addText : function(text){
7624         return this.addItem(new Roo.Toolbar.TextItem(text));
7625     },
7626     
7627     /**
7628      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7629      * @param {Number} index The index where the item is to be inserted
7630      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7631      * @return {Roo.Toolbar.Button/Item}
7632      */
7633     insertButton : function(index, item){
7634         if(item instanceof Array){
7635             var buttons = [];
7636             for(var i = 0, len = item.length; i < len; i++) {
7637                buttons.push(this.insertButton(index + i, item[i]));
7638             }
7639             return buttons;
7640         }
7641         if (!(item instanceof Roo.Toolbar.Button)){
7642            item = new Roo.Toolbar.Button(item);
7643         }
7644         var td = document.createElement("td");
7645         this.tr.insertBefore(td, this.tr.childNodes[index]);
7646         item.render(td);
7647         this.items.insert(index, item);
7648         return item;
7649     },
7650     
7651     /**
7652      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7653      * @param {Object} config
7654      * @return {Roo.Toolbar.Item} The element's item
7655      */
7656     addDom : function(config, returnEl){
7657         var td = this.nextBlock();
7658         Roo.DomHelper.overwrite(td, config);
7659         var ti = new Roo.Toolbar.Item(td.firstChild);
7660         ti.render(td);
7661         this.items.add(ti);
7662         return ti;
7663     },
7664
7665     /**
7666      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7667      * @type Roo.util.MixedCollection  
7668      */
7669     fields : false,
7670     
7671     /**
7672      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7673      * Note: the field should not have been rendered yet. For a field that has already been
7674      * rendered, use {@link #addElement}.
7675      * @param {Roo.form.Field} field
7676      * @return {Roo.ToolbarItem}
7677      */
7678      
7679       
7680     addField : function(field) {
7681         if (!this.fields) {
7682             var autoId = 0;
7683             this.fields = new Roo.util.MixedCollection(false, function(o){
7684                 return o.id || ("item" + (++autoId));
7685             });
7686
7687         }
7688         
7689         var td = this.nextBlock();
7690         field.render(td);
7691         var ti = new Roo.Toolbar.Item(td.firstChild);
7692         ti.render(td);
7693         this.items.add(ti);
7694         this.fields.add(field);
7695         return ti;
7696     },
7697     /**
7698      * Hide the toolbar
7699      * @method hide
7700      */
7701      
7702       
7703     hide : function()
7704     {
7705         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7706         this.el.child('div').hide();
7707     },
7708     /**
7709      * Show the toolbar
7710      * @method show
7711      */
7712     show : function()
7713     {
7714         this.el.child('div').show();
7715     },
7716       
7717     // private
7718     nextBlock : function(){
7719         var td = document.createElement("td");
7720         this.tr.appendChild(td);
7721         return td;
7722     },
7723
7724     // private
7725     destroy : function(){
7726         if(this.items){ // rendered?
7727             Roo.destroy.apply(Roo, this.items.items);
7728         }
7729         if(this.fields){ // rendered?
7730             Roo.destroy.apply(Roo, this.fields.items);
7731         }
7732         Roo.Element.uncache(this.el, this.tr);
7733     }
7734 };
7735
7736 /**
7737  * @class Roo.Toolbar.Item
7738  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7739  * @constructor
7740  * Creates a new Item
7741  * @param {HTMLElement} el 
7742  */
7743 Roo.Toolbar.Item = function(el){
7744     var cfg = {};
7745     if (typeof (el.xtype) != 'undefined') {
7746         cfg = el;
7747         el = cfg.el;
7748     }
7749     
7750     this.el = Roo.getDom(el);
7751     this.id = Roo.id(this.el);
7752     this.hidden = false;
7753     
7754     this.addEvents({
7755          /**
7756              * @event render
7757              * Fires when the button is rendered
7758              * @param {Button} this
7759              */
7760         'render': true
7761     });
7762     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7763 };
7764 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7765 //Roo.Toolbar.Item.prototype = {
7766     
7767     /**
7768      * Get this item's HTML Element
7769      * @return {HTMLElement}
7770      */
7771     getEl : function(){
7772        return this.el;  
7773     },
7774
7775     // private
7776     render : function(td){
7777         
7778          this.td = td;
7779         td.appendChild(this.el);
7780         
7781         this.fireEvent('render', this);
7782     },
7783     
7784     /**
7785      * Removes and destroys this item.
7786      */
7787     destroy : function(){
7788         this.td.parentNode.removeChild(this.td);
7789     },
7790     
7791     /**
7792      * Shows this item.
7793      */
7794     show: function(){
7795         this.hidden = false;
7796         this.td.style.display = "";
7797     },
7798     
7799     /**
7800      * Hides this item.
7801      */
7802     hide: function(){
7803         this.hidden = true;
7804         this.td.style.display = "none";
7805     },
7806     
7807     /**
7808      * Convenience function for boolean show/hide.
7809      * @param {Boolean} visible true to show/false to hide
7810      */
7811     setVisible: function(visible){
7812         if(visible) {
7813             this.show();
7814         }else{
7815             this.hide();
7816         }
7817     },
7818     
7819     /**
7820      * Try to focus this item.
7821      */
7822     focus : function(){
7823         Roo.fly(this.el).focus();
7824     },
7825     
7826     /**
7827      * Disables this item.
7828      */
7829     disable : function(){
7830         Roo.fly(this.td).addClass("x-item-disabled");
7831         this.disabled = true;
7832         this.el.disabled = true;
7833     },
7834     
7835     /**
7836      * Enables this item.
7837      */
7838     enable : function(){
7839         Roo.fly(this.td).removeClass("x-item-disabled");
7840         this.disabled = false;
7841         this.el.disabled = false;
7842     }
7843 });
7844
7845
7846 /**
7847  * @class Roo.Toolbar.Separator
7848  * @extends Roo.Toolbar.Item
7849  * A simple toolbar separator class
7850  * @constructor
7851  * Creates a new Separator
7852  */
7853 Roo.Toolbar.Separator = function(cfg){
7854     
7855     var s = document.createElement("span");
7856     s.className = "ytb-sep";
7857     if (cfg) {
7858         cfg.el = s;
7859     }
7860     
7861     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7862 };
7863 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7864     enable:Roo.emptyFn,
7865     disable:Roo.emptyFn,
7866     focus:Roo.emptyFn
7867 });
7868
7869 /**
7870  * @class Roo.Toolbar.Spacer
7871  * @extends Roo.Toolbar.Item
7872  * A simple element that adds extra horizontal space to a toolbar.
7873  * @constructor
7874  * Creates a new Spacer
7875  */
7876 Roo.Toolbar.Spacer = function(cfg){
7877     var s = document.createElement("div");
7878     s.className = "ytb-spacer";
7879     if (cfg) {
7880         cfg.el = s;
7881     }
7882     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7883 };
7884 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7885     enable:Roo.emptyFn,
7886     disable:Roo.emptyFn,
7887     focus:Roo.emptyFn
7888 });
7889
7890 /**
7891  * @class Roo.Toolbar.Fill
7892  * @extends Roo.Toolbar.Spacer
7893  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7894  * @constructor
7895  * Creates a new Spacer
7896  */
7897 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7898     // private
7899     render : function(td){
7900         td.style.width = '100%';
7901         Roo.Toolbar.Fill.superclass.render.call(this, td);
7902     }
7903 });
7904
7905 /**
7906  * @class Roo.Toolbar.TextItem
7907  * @extends Roo.Toolbar.Item
7908  * A simple class that renders text directly into a toolbar.
7909  * @constructor
7910  * Creates a new TextItem
7911  * @param {String} text
7912  */
7913 Roo.Toolbar.TextItem = function(cfg){
7914     var  text = cfg || "";
7915     if (typeof(cfg) == 'object') {
7916         text = cfg.text || "";
7917     }  else {
7918         cfg = null;
7919     }
7920     var s = document.createElement("span");
7921     s.className = "ytb-text";
7922     s.innerHTML = text;
7923     if (cfg) {
7924         cfg.el  = s;
7925     }
7926     
7927     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7928 };
7929 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7930     
7931      
7932     enable:Roo.emptyFn,
7933     disable:Roo.emptyFn,
7934     focus:Roo.emptyFn
7935 });
7936
7937 /**
7938  * @class Roo.Toolbar.Button
7939  * @extends Roo.Button
7940  * A button that renders into a toolbar.
7941  * @constructor
7942  * Creates a new Button
7943  * @param {Object} config A standard {@link Roo.Button} config object
7944  */
7945 Roo.Toolbar.Button = function(config){
7946     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7947 };
7948 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7949     render : function(td){
7950         this.td = td;
7951         Roo.Toolbar.Button.superclass.render.call(this, td);
7952     },
7953     
7954     /**
7955      * Removes and destroys this button
7956      */
7957     destroy : function(){
7958         Roo.Toolbar.Button.superclass.destroy.call(this);
7959         this.td.parentNode.removeChild(this.td);
7960     },
7961     
7962     /**
7963      * Shows this button
7964      */
7965     show: function(){
7966         this.hidden = false;
7967         this.td.style.display = "";
7968     },
7969     
7970     /**
7971      * Hides this button
7972      */
7973     hide: function(){
7974         this.hidden = true;
7975         this.td.style.display = "none";
7976     },
7977
7978     /**
7979      * Disables this item
7980      */
7981     disable : function(){
7982         Roo.fly(this.td).addClass("x-item-disabled");
7983         this.disabled = true;
7984     },
7985
7986     /**
7987      * Enables this item
7988      */
7989     enable : function(){
7990         Roo.fly(this.td).removeClass("x-item-disabled");
7991         this.disabled = false;
7992     }
7993 });
7994 // backwards compat
7995 Roo.ToolbarButton = Roo.Toolbar.Button;
7996
7997 /**
7998  * @class Roo.Toolbar.SplitButton
7999  * @extends Roo.SplitButton
8000  * A menu button that renders into a toolbar.
8001  * @constructor
8002  * Creates a new SplitButton
8003  * @param {Object} config A standard {@link Roo.SplitButton} config object
8004  */
8005 Roo.Toolbar.SplitButton = function(config){
8006     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8007 };
8008 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8009     render : function(td){
8010         this.td = td;
8011         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8012     },
8013     
8014     /**
8015      * Removes and destroys this button
8016      */
8017     destroy : function(){
8018         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8019         this.td.parentNode.removeChild(this.td);
8020     },
8021     
8022     /**
8023      * Shows this button
8024      */
8025     show: function(){
8026         this.hidden = false;
8027         this.td.style.display = "";
8028     },
8029     
8030     /**
8031      * Hides this button
8032      */
8033     hide: function(){
8034         this.hidden = true;
8035         this.td.style.display = "none";
8036     }
8037 });
8038
8039 // backwards compat
8040 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8041  * Based on:
8042  * Ext JS Library 1.1.1
8043  * Copyright(c) 2006-2007, Ext JS, LLC.
8044  *
8045  * Originally Released Under LGPL - original licence link has changed is not relivant.
8046  *
8047  * Fork - LGPL
8048  * <script type="text/javascript">
8049  */
8050  
8051 /**
8052  * @class Roo.PagingToolbar
8053  * @extends Roo.Toolbar
8054  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8055  * @constructor
8056  * Create a new PagingToolbar
8057  * @param {Object} config The config object
8058  */
8059 Roo.PagingToolbar = function(el, ds, config)
8060 {
8061     // old args format still supported... - xtype is prefered..
8062     if (typeof(el) == 'object' && el.xtype) {
8063         // created from xtype...
8064         config = el;
8065         ds = el.dataSource;
8066         el = config.container;
8067     }
8068     var items = [];
8069     if (config.items) {
8070         items = config.items;
8071         config.items = [];
8072     }
8073     
8074     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8075     this.ds = ds;
8076     this.cursor = 0;
8077     this.renderButtons(this.el);
8078     this.bind(ds);
8079     
8080     // supprot items array.
8081    
8082     Roo.each(items, function(e) {
8083         this.add(Roo.factory(e));
8084     },this);
8085     
8086 };
8087
8088 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8089     /**
8090      * @cfg {Roo.data.Store} dataSource
8091      * The underlying data store providing the paged data
8092      */
8093     /**
8094      * @cfg {String/HTMLElement/Element} container
8095      * container The id or element that will contain the toolbar
8096      */
8097     /**
8098      * @cfg {Boolean} displayInfo
8099      * True to display the displayMsg (defaults to false)
8100      */
8101     /**
8102      * @cfg {Number} pageSize
8103      * The number of records to display per page (defaults to 20)
8104      */
8105     pageSize: 20,
8106     /**
8107      * @cfg {String} displayMsg
8108      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8109      */
8110     displayMsg : 'Displaying {0} - {1} of {2}',
8111     /**
8112      * @cfg {String} emptyMsg
8113      * The message to display when no records are found (defaults to "No data to display")
8114      */
8115     emptyMsg : 'No data to display',
8116     /**
8117      * Customizable piece of the default paging text (defaults to "Page")
8118      * @type String
8119      */
8120     beforePageText : "Page",
8121     /**
8122      * Customizable piece of the default paging text (defaults to "of %0")
8123      * @type String
8124      */
8125     afterPageText : "of {0}",
8126     /**
8127      * Customizable piece of the default paging text (defaults to "First Page")
8128      * @type String
8129      */
8130     firstText : "First Page",
8131     /**
8132      * Customizable piece of the default paging text (defaults to "Previous Page")
8133      * @type String
8134      */
8135     prevText : "Previous Page",
8136     /**
8137      * Customizable piece of the default paging text (defaults to "Next Page")
8138      * @type String
8139      */
8140     nextText : "Next Page",
8141     /**
8142      * Customizable piece of the default paging text (defaults to "Last Page")
8143      * @type String
8144      */
8145     lastText : "Last Page",
8146     /**
8147      * Customizable piece of the default paging text (defaults to "Refresh")
8148      * @type String
8149      */
8150     refreshText : "Refresh",
8151
8152     // private
8153     renderButtons : function(el){
8154         Roo.PagingToolbar.superclass.render.call(this, el);
8155         this.first = this.addButton({
8156             tooltip: this.firstText,
8157             cls: "x-btn-icon x-grid-page-first",
8158             disabled: true,
8159             handler: this.onClick.createDelegate(this, ["first"])
8160         });
8161         this.prev = this.addButton({
8162             tooltip: this.prevText,
8163             cls: "x-btn-icon x-grid-page-prev",
8164             disabled: true,
8165             handler: this.onClick.createDelegate(this, ["prev"])
8166         });
8167         //this.addSeparator();
8168         this.add(this.beforePageText);
8169         this.field = Roo.get(this.addDom({
8170            tag: "input",
8171            type: "text",
8172            size: "3",
8173            value: "1",
8174            cls: "x-grid-page-number"
8175         }).el);
8176         this.field.on("keydown", this.onPagingKeydown, this);
8177         this.field.on("focus", function(){this.dom.select();});
8178         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8179         this.field.setHeight(18);
8180         //this.addSeparator();
8181         this.next = this.addButton({
8182             tooltip: this.nextText,
8183             cls: "x-btn-icon x-grid-page-next",
8184             disabled: true,
8185             handler: this.onClick.createDelegate(this, ["next"])
8186         });
8187         this.last = this.addButton({
8188             tooltip: this.lastText,
8189             cls: "x-btn-icon x-grid-page-last",
8190             disabled: true,
8191             handler: this.onClick.createDelegate(this, ["last"])
8192         });
8193         //this.addSeparator();
8194         this.loading = this.addButton({
8195             tooltip: this.refreshText,
8196             cls: "x-btn-icon x-grid-loading",
8197             handler: this.onClick.createDelegate(this, ["refresh"])
8198         });
8199
8200         if(this.displayInfo){
8201             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8202         }
8203     },
8204
8205     // private
8206     updateInfo : function(){
8207         if(this.displayEl){
8208             var count = this.ds.getCount();
8209             var msg = count == 0 ?
8210                 this.emptyMsg :
8211                 String.format(
8212                     this.displayMsg,
8213                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8214                 );
8215             this.displayEl.update(msg);
8216         }
8217     },
8218
8219     // private
8220     onLoad : function(ds, r, o){
8221        this.cursor = o.params ? o.params.start : 0;
8222        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8223
8224        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8225        this.field.dom.value = ap;
8226        this.first.setDisabled(ap == 1);
8227        this.prev.setDisabled(ap == 1);
8228        this.next.setDisabled(ap == ps);
8229        this.last.setDisabled(ap == ps);
8230        this.loading.enable();
8231        this.updateInfo();
8232     },
8233
8234     // private
8235     getPageData : function(){
8236         var total = this.ds.getTotalCount();
8237         return {
8238             total : total,
8239             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8240             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8241         };
8242     },
8243
8244     // private
8245     onLoadError : function(){
8246         this.loading.enable();
8247     },
8248
8249     // private
8250     onPagingKeydown : function(e){
8251         var k = e.getKey();
8252         var d = this.getPageData();
8253         if(k == e.RETURN){
8254             var v = this.field.dom.value, pageNum;
8255             if(!v || isNaN(pageNum = parseInt(v, 10))){
8256                 this.field.dom.value = d.activePage;
8257                 return;
8258             }
8259             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8260             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8261             e.stopEvent();
8262         }
8263         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
8264         {
8265           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8266           this.field.dom.value = pageNum;
8267           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8268           e.stopEvent();
8269         }
8270         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8271         {
8272           var v = this.field.dom.value, pageNum; 
8273           var increment = (e.shiftKey) ? 10 : 1;
8274           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8275             increment *= -1;
8276           }
8277           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8278             this.field.dom.value = d.activePage;
8279             return;
8280           }
8281           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8282           {
8283             this.field.dom.value = parseInt(v, 10) + increment;
8284             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8285             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8286           }
8287           e.stopEvent();
8288         }
8289     },
8290
8291     // private
8292     beforeLoad : function(){
8293         if(this.loading){
8294             this.loading.disable();
8295         }
8296     },
8297
8298     // private
8299     onClick : function(which){
8300         var ds = this.ds;
8301         switch(which){
8302             case "first":
8303                 ds.load({params:{start: 0, limit: this.pageSize}});
8304             break;
8305             case "prev":
8306                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8307             break;
8308             case "next":
8309                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8310             break;
8311             case "last":
8312                 var total = ds.getTotalCount();
8313                 var extra = total % this.pageSize;
8314                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8315                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8316             break;
8317             case "refresh":
8318                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8319             break;
8320         }
8321     },
8322
8323     /**
8324      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8325      * @param {Roo.data.Store} store The data store to unbind
8326      */
8327     unbind : function(ds){
8328         ds.un("beforeload", this.beforeLoad, this);
8329         ds.un("load", this.onLoad, this);
8330         ds.un("loadexception", this.onLoadError, this);
8331         ds.un("remove", this.updateInfo, this);
8332         ds.un("add", this.updateInfo, this);
8333         this.ds = undefined;
8334     },
8335
8336     /**
8337      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8338      * @param {Roo.data.Store} store The data store to bind
8339      */
8340     bind : function(ds){
8341         ds.on("beforeload", this.beforeLoad, this);
8342         ds.on("load", this.onLoad, this);
8343         ds.on("loadexception", this.onLoadError, this);
8344         ds.on("remove", this.updateInfo, this);
8345         ds.on("add", this.updateInfo, this);
8346         this.ds = ds;
8347     }
8348 });/*
8349  * Based on:
8350  * Ext JS Library 1.1.1
8351  * Copyright(c) 2006-2007, Ext JS, LLC.
8352  *
8353  * Originally Released Under LGPL - original licence link has changed is not relivant.
8354  *
8355  * Fork - LGPL
8356  * <script type="text/javascript">
8357  */
8358
8359 /**
8360  * @class Roo.Resizable
8361  * @extends Roo.util.Observable
8362  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8363  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8364  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
8365  * the element will be wrapped for you automatically.</p>
8366  * <p>Here is the list of valid resize handles:</p>
8367  * <pre>
8368 Value   Description
8369 ------  -------------------
8370  'n'     north
8371  's'     south
8372  'e'     east
8373  'w'     west
8374  'nw'    northwest
8375  'sw'    southwest
8376  'se'    southeast
8377  'ne'    northeast
8378  'hd'    horizontal drag
8379  'all'   all
8380 </pre>
8381  * <p>Here's an example showing the creation of a typical Resizable:</p>
8382  * <pre><code>
8383 var resizer = new Roo.Resizable("element-id", {
8384     handles: 'all',
8385     minWidth: 200,
8386     minHeight: 100,
8387     maxWidth: 500,
8388     maxHeight: 400,
8389     pinned: true
8390 });
8391 resizer.on("resize", myHandler);
8392 </code></pre>
8393  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8394  * resizer.east.setDisplayed(false);</p>
8395  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8396  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8397  * resize operation's new size (defaults to [0, 0])
8398  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8399  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8400  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8401  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8402  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8403  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8404  * @cfg {Number} width The width of the element in pixels (defaults to null)
8405  * @cfg {Number} height The height of the element in pixels (defaults to null)
8406  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8407  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8408  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8409  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8410  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8411  * in favor of the handles config option (defaults to false)
8412  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8413  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8414  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8415  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8416  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8417  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8418  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8419  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8420  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8421  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8422  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8423  * @constructor
8424  * Create a new resizable component
8425  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8426  * @param {Object} config configuration options
8427   */
8428 Roo.Resizable = function(el, config)
8429 {
8430     this.el = Roo.get(el);
8431
8432     if(config && config.wrap){
8433         config.resizeChild = this.el;
8434         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8435         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8436         this.el.setStyle("overflow", "hidden");
8437         this.el.setPositioning(config.resizeChild.getPositioning());
8438         config.resizeChild.clearPositioning();
8439         if(!config.width || !config.height){
8440             var csize = config.resizeChild.getSize();
8441             this.el.setSize(csize.width, csize.height);
8442         }
8443         if(config.pinned && !config.adjustments){
8444             config.adjustments = "auto";
8445         }
8446     }
8447
8448     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8449     this.proxy.unselectable();
8450     this.proxy.enableDisplayMode('block');
8451
8452     Roo.apply(this, config);
8453
8454     if(this.pinned){
8455         this.disableTrackOver = true;
8456         this.el.addClass("x-resizable-pinned");
8457     }
8458     // if the element isn't positioned, make it relative
8459     var position = this.el.getStyle("position");
8460     if(position != "absolute" && position != "fixed"){
8461         this.el.setStyle("position", "relative");
8462     }
8463     if(!this.handles){ // no handles passed, must be legacy style
8464         this.handles = 's,e,se';
8465         if(this.multiDirectional){
8466             this.handles += ',n,w';
8467         }
8468     }
8469     if(this.handles == "all"){
8470         this.handles = "n s e w ne nw se sw";
8471     }
8472     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8473     var ps = Roo.Resizable.positions;
8474     for(var i = 0, len = hs.length; i < len; i++){
8475         if(hs[i] && ps[hs[i]]){
8476             var pos = ps[hs[i]];
8477             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8478         }
8479     }
8480     // legacy
8481     this.corner = this.southeast;
8482     
8483     // updateBox = the box can move..
8484     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8485         this.updateBox = true;
8486     }
8487
8488     this.activeHandle = null;
8489
8490     if(this.resizeChild){
8491         if(typeof this.resizeChild == "boolean"){
8492             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8493         }else{
8494             this.resizeChild = Roo.get(this.resizeChild, true);
8495         }
8496     }
8497     
8498     if(this.adjustments == "auto"){
8499         var rc = this.resizeChild;
8500         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8501         if(rc && (hw || hn)){
8502             rc.position("relative");
8503             rc.setLeft(hw ? hw.el.getWidth() : 0);
8504             rc.setTop(hn ? hn.el.getHeight() : 0);
8505         }
8506         this.adjustments = [
8507             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8508             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8509         ];
8510     }
8511
8512     if(this.draggable){
8513         this.dd = this.dynamic ?
8514             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8515         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8516     }
8517
8518     // public events
8519     this.addEvents({
8520         /**
8521          * @event beforeresize
8522          * Fired before resize is allowed. Set enabled to false to cancel resize.
8523          * @param {Roo.Resizable} this
8524          * @param {Roo.EventObject} e The mousedown event
8525          */
8526         "beforeresize" : true,
8527         /**
8528          * @event resizing
8529          * Fired a resizing.
8530          * @param {Roo.Resizable} this
8531          * @param {Number} x The new x position
8532          * @param {Number} y The new y position
8533          * @param {Number} w The new w width
8534          * @param {Number} h The new h hight
8535          * @param {Roo.EventObject} e The mouseup event
8536          */
8537         "resizing" : true,
8538         /**
8539          * @event resize
8540          * Fired after a resize.
8541          * @param {Roo.Resizable} this
8542          * @param {Number} width The new width
8543          * @param {Number} height The new height
8544          * @param {Roo.EventObject} e The mouseup event
8545          */
8546         "resize" : true
8547     });
8548
8549     if(this.width !== null && this.height !== null){
8550         this.resizeTo(this.width, this.height);
8551     }else{
8552         this.updateChildSize();
8553     }
8554     if(Roo.isIE){
8555         this.el.dom.style.zoom = 1;
8556     }
8557     Roo.Resizable.superclass.constructor.call(this);
8558 };
8559
8560 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8561         resizeChild : false,
8562         adjustments : [0, 0],
8563         minWidth : 5,
8564         minHeight : 5,
8565         maxWidth : 10000,
8566         maxHeight : 10000,
8567         enabled : true,
8568         animate : false,
8569         duration : .35,
8570         dynamic : false,
8571         handles : false,
8572         multiDirectional : false,
8573         disableTrackOver : false,
8574         easing : 'easeOutStrong',
8575         widthIncrement : 0,
8576         heightIncrement : 0,
8577         pinned : false,
8578         width : null,
8579         height : null,
8580         preserveRatio : false,
8581         transparent: false,
8582         minX: 0,
8583         minY: 0,
8584         draggable: false,
8585
8586         /**
8587          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8588          */
8589         constrainTo: undefined,
8590         /**
8591          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8592          */
8593         resizeRegion: undefined,
8594
8595
8596     /**
8597      * Perform a manual resize
8598      * @param {Number} width
8599      * @param {Number} height
8600      */
8601     resizeTo : function(width, height){
8602         this.el.setSize(width, height);
8603         this.updateChildSize();
8604         this.fireEvent("resize", this, width, height, null);
8605     },
8606
8607     // private
8608     startSizing : function(e, handle){
8609         this.fireEvent("beforeresize", this, e);
8610         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8611
8612             if(!this.overlay){
8613                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8614                 this.overlay.unselectable();
8615                 this.overlay.enableDisplayMode("block");
8616                 this.overlay.on("mousemove", this.onMouseMove, this);
8617                 this.overlay.on("mouseup", this.onMouseUp, this);
8618             }
8619             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8620
8621             this.resizing = true;
8622             this.startBox = this.el.getBox();
8623             this.startPoint = e.getXY();
8624             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8625                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8626
8627             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8628             this.overlay.show();
8629
8630             if(this.constrainTo) {
8631                 var ct = Roo.get(this.constrainTo);
8632                 this.resizeRegion = ct.getRegion().adjust(
8633                     ct.getFrameWidth('t'),
8634                     ct.getFrameWidth('l'),
8635                     -ct.getFrameWidth('b'),
8636                     -ct.getFrameWidth('r')
8637                 );
8638             }
8639
8640             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8641             this.proxy.show();
8642             this.proxy.setBox(this.startBox);
8643             if(!this.dynamic){
8644                 this.proxy.setStyle('visibility', 'visible');
8645             }
8646         }
8647     },
8648
8649     // private
8650     onMouseDown : function(handle, e){
8651         if(this.enabled){
8652             e.stopEvent();
8653             this.activeHandle = handle;
8654             this.startSizing(e, handle);
8655         }
8656     },
8657
8658     // private
8659     onMouseUp : function(e){
8660         var size = this.resizeElement();
8661         this.resizing = false;
8662         this.handleOut();
8663         this.overlay.hide();
8664         this.proxy.hide();
8665         this.fireEvent("resize", this, size.width, size.height, e);
8666     },
8667
8668     // private
8669     updateChildSize : function(){
8670         
8671         if(this.resizeChild){
8672             var el = this.el;
8673             var child = this.resizeChild;
8674             var adj = this.adjustments;
8675             if(el.dom.offsetWidth){
8676                 var b = el.getSize(true);
8677                 child.setSize(b.width+adj[0], b.height+adj[1]);
8678             }
8679             // Second call here for IE
8680             // The first call enables instant resizing and
8681             // the second call corrects scroll bars if they
8682             // exist
8683             if(Roo.isIE){
8684                 setTimeout(function(){
8685                     if(el.dom.offsetWidth){
8686                         var b = el.getSize(true);
8687                         child.setSize(b.width+adj[0], b.height+adj[1]);
8688                     }
8689                 }, 10);
8690             }
8691         }
8692     },
8693
8694     // private
8695     snap : function(value, inc, min){
8696         if(!inc || !value) {
8697             return value;
8698         }
8699         var newValue = value;
8700         var m = value % inc;
8701         if(m > 0){
8702             if(m > (inc/2)){
8703                 newValue = value + (inc-m);
8704             }else{
8705                 newValue = value - m;
8706             }
8707         }
8708         return Math.max(min, newValue);
8709     },
8710
8711     // private
8712     resizeElement : function(){
8713         var box = this.proxy.getBox();
8714         if(this.updateBox){
8715             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8716         }else{
8717             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8718         }
8719         this.updateChildSize();
8720         if(!this.dynamic){
8721             this.proxy.hide();
8722         }
8723         return box;
8724     },
8725
8726     // private
8727     constrain : function(v, diff, m, mx){
8728         if(v - diff < m){
8729             diff = v - m;
8730         }else if(v - diff > mx){
8731             diff = mx - v;
8732         }
8733         return diff;
8734     },
8735
8736     // private
8737     onMouseMove : function(e){
8738         
8739         if(this.enabled){
8740             try{// try catch so if something goes wrong the user doesn't get hung
8741
8742             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8743                 return;
8744             }
8745
8746             //var curXY = this.startPoint;
8747             var curSize = this.curSize || this.startBox;
8748             var x = this.startBox.x, y = this.startBox.y;
8749             var ox = x, oy = y;
8750             var w = curSize.width, h = curSize.height;
8751             var ow = w, oh = h;
8752             var mw = this.minWidth, mh = this.minHeight;
8753             var mxw = this.maxWidth, mxh = this.maxHeight;
8754             var wi = this.widthIncrement;
8755             var hi = this.heightIncrement;
8756
8757             var eventXY = e.getXY();
8758             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8759             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8760
8761             var pos = this.activeHandle.position;
8762
8763             switch(pos){
8764                 case "east":
8765                     w += diffX;
8766                     w = Math.min(Math.max(mw, w), mxw);
8767                     break;
8768              
8769                 case "south":
8770                     h += diffY;
8771                     h = Math.min(Math.max(mh, h), mxh);
8772                     break;
8773                 case "southeast":
8774                     w += diffX;
8775                     h += diffY;
8776                     w = Math.min(Math.max(mw, w), mxw);
8777                     h = Math.min(Math.max(mh, h), mxh);
8778                     break;
8779                 case "north":
8780                     diffY = this.constrain(h, diffY, mh, mxh);
8781                     y += diffY;
8782                     h -= diffY;
8783                     break;
8784                 case "hdrag":
8785                     
8786                     if (wi) {
8787                         var adiffX = Math.abs(diffX);
8788                         var sub = (adiffX % wi); // how much 
8789                         if (sub > (wi/2)) { // far enough to snap
8790                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8791                         } else {
8792                             // remove difference.. 
8793                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8794                         }
8795                     }
8796                     x += diffX;
8797                     x = Math.max(this.minX, x);
8798                     break;
8799                 case "west":
8800                     diffX = this.constrain(w, diffX, mw, mxw);
8801                     x += diffX;
8802                     w -= diffX;
8803                     break;
8804                 case "northeast":
8805                     w += diffX;
8806                     w = Math.min(Math.max(mw, w), mxw);
8807                     diffY = this.constrain(h, diffY, mh, mxh);
8808                     y += diffY;
8809                     h -= diffY;
8810                     break;
8811                 case "northwest":
8812                     diffX = this.constrain(w, diffX, mw, mxw);
8813                     diffY = this.constrain(h, diffY, mh, mxh);
8814                     y += diffY;
8815                     h -= diffY;
8816                     x += diffX;
8817                     w -= diffX;
8818                     break;
8819                case "southwest":
8820                     diffX = this.constrain(w, diffX, mw, mxw);
8821                     h += diffY;
8822                     h = Math.min(Math.max(mh, h), mxh);
8823                     x += diffX;
8824                     w -= diffX;
8825                     break;
8826             }
8827
8828             var sw = this.snap(w, wi, mw);
8829             var sh = this.snap(h, hi, mh);
8830             if(sw != w || sh != h){
8831                 switch(pos){
8832                     case "northeast":
8833                         y -= sh - h;
8834                     break;
8835                     case "north":
8836                         y -= sh - h;
8837                         break;
8838                     case "southwest":
8839                         x -= sw - w;
8840                     break;
8841                     case "west":
8842                         x -= sw - w;
8843                         break;
8844                     case "northwest":
8845                         x -= sw - w;
8846                         y -= sh - h;
8847                     break;
8848                 }
8849                 w = sw;
8850                 h = sh;
8851             }
8852
8853             if(this.preserveRatio){
8854                 switch(pos){
8855                     case "southeast":
8856                     case "east":
8857                         h = oh * (w/ow);
8858                         h = Math.min(Math.max(mh, h), mxh);
8859                         w = ow * (h/oh);
8860                        break;
8861                     case "south":
8862                         w = ow * (h/oh);
8863                         w = Math.min(Math.max(mw, w), mxw);
8864                         h = oh * (w/ow);
8865                         break;
8866                     case "northeast":
8867                         w = ow * (h/oh);
8868                         w = Math.min(Math.max(mw, w), mxw);
8869                         h = oh * (w/ow);
8870                     break;
8871                     case "north":
8872                         var tw = w;
8873                         w = ow * (h/oh);
8874                         w = Math.min(Math.max(mw, w), mxw);
8875                         h = oh * (w/ow);
8876                         x += (tw - w) / 2;
8877                         break;
8878                     case "southwest":
8879                         h = oh * (w/ow);
8880                         h = Math.min(Math.max(mh, h), mxh);
8881                         var tw = w;
8882                         w = ow * (h/oh);
8883                         x += tw - w;
8884                         break;
8885                     case "west":
8886                         var th = h;
8887                         h = oh * (w/ow);
8888                         h = Math.min(Math.max(mh, h), mxh);
8889                         y += (th - h) / 2;
8890                         var tw = w;
8891                         w = ow * (h/oh);
8892                         x += tw - w;
8893                        break;
8894                     case "northwest":
8895                         var tw = w;
8896                         var th = h;
8897                         h = oh * (w/ow);
8898                         h = Math.min(Math.max(mh, h), mxh);
8899                         w = ow * (h/oh);
8900                         y += th - h;
8901                         x += tw - w;
8902                        break;
8903
8904                 }
8905             }
8906             if (pos == 'hdrag') {
8907                 w = ow;
8908             }
8909             this.proxy.setBounds(x, y, w, h);
8910             if(this.dynamic){
8911                 this.resizeElement();
8912             }
8913             }catch(e){}
8914         }
8915         this.fireEvent("resizing", this, x, y, w, h, e);
8916     },
8917
8918     // private
8919     handleOver : function(){
8920         if(this.enabled){
8921             this.el.addClass("x-resizable-over");
8922         }
8923     },
8924
8925     // private
8926     handleOut : function(){
8927         if(!this.resizing){
8928             this.el.removeClass("x-resizable-over");
8929         }
8930     },
8931
8932     /**
8933      * Returns the element this component is bound to.
8934      * @return {Roo.Element}
8935      */
8936     getEl : function(){
8937         return this.el;
8938     },
8939
8940     /**
8941      * Returns the resizeChild element (or null).
8942      * @return {Roo.Element}
8943      */
8944     getResizeChild : function(){
8945         return this.resizeChild;
8946     },
8947     groupHandler : function()
8948     {
8949         
8950     },
8951     /**
8952      * Destroys this resizable. If the element was wrapped and
8953      * removeEl is not true then the element remains.
8954      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8955      */
8956     destroy : function(removeEl){
8957         this.proxy.remove();
8958         if(this.overlay){
8959             this.overlay.removeAllListeners();
8960             this.overlay.remove();
8961         }
8962         var ps = Roo.Resizable.positions;
8963         for(var k in ps){
8964             if(typeof ps[k] != "function" && this[ps[k]]){
8965                 var h = this[ps[k]];
8966                 h.el.removeAllListeners();
8967                 h.el.remove();
8968             }
8969         }
8970         if(removeEl){
8971             this.el.update("");
8972             this.el.remove();
8973         }
8974     }
8975 });
8976
8977 // private
8978 // hash to map config positions to true positions
8979 Roo.Resizable.positions = {
8980     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8981     hd: "hdrag"
8982 };
8983
8984 // private
8985 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8986     if(!this.tpl){
8987         // only initialize the template if resizable is used
8988         var tpl = Roo.DomHelper.createTemplate(
8989             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8990         );
8991         tpl.compile();
8992         Roo.Resizable.Handle.prototype.tpl = tpl;
8993     }
8994     this.position = pos;
8995     this.rz = rz;
8996     // show north drag fro topdra
8997     var handlepos = pos == 'hdrag' ? 'north' : pos;
8998     
8999     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9000     if (pos == 'hdrag') {
9001         this.el.setStyle('cursor', 'pointer');
9002     }
9003     this.el.unselectable();
9004     if(transparent){
9005         this.el.setOpacity(0);
9006     }
9007     this.el.on("mousedown", this.onMouseDown, this);
9008     if(!disableTrackOver){
9009         this.el.on("mouseover", this.onMouseOver, this);
9010         this.el.on("mouseout", this.onMouseOut, this);
9011     }
9012 };
9013
9014 // private
9015 Roo.Resizable.Handle.prototype = {
9016     afterResize : function(rz){
9017         Roo.log('after?');
9018         // do nothing
9019     },
9020     // private
9021     onMouseDown : function(e){
9022         this.rz.onMouseDown(this, e);
9023     },
9024     // private
9025     onMouseOver : function(e){
9026         this.rz.handleOver(this, e);
9027     },
9028     // private
9029     onMouseOut : function(e){
9030         this.rz.handleOut(this, e);
9031     }
9032 };/*
9033  * Based on:
9034  * Ext JS Library 1.1.1
9035  * Copyright(c) 2006-2007, Ext JS, LLC.
9036  *
9037  * Originally Released Under LGPL - original licence link has changed is not relivant.
9038  *
9039  * Fork - LGPL
9040  * <script type="text/javascript">
9041  */
9042
9043 /**
9044  * @class Roo.Editor
9045  * @extends Roo.Component
9046  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9047  * @constructor
9048  * Create a new Editor
9049  * @param {Roo.form.Field} field The Field object (or descendant)
9050  * @param {Object} config The config object
9051  */
9052 Roo.Editor = function(field, config){
9053     Roo.Editor.superclass.constructor.call(this, config);
9054     this.field = field;
9055     this.addEvents({
9056         /**
9057              * @event beforestartedit
9058              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9059              * false from the handler of this event.
9060              * @param {Editor} this
9061              * @param {Roo.Element} boundEl The underlying element bound to this editor
9062              * @param {Mixed} value The field value being set
9063              */
9064         "beforestartedit" : true,
9065         /**
9066              * @event startedit
9067              * Fires when this editor is displayed
9068              * @param {Roo.Element} boundEl The underlying element bound to this editor
9069              * @param {Mixed} value The starting field value
9070              */
9071         "startedit" : true,
9072         /**
9073              * @event beforecomplete
9074              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9075              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9076              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9077              * event will not fire since no edit actually occurred.
9078              * @param {Editor} this
9079              * @param {Mixed} value The current field value
9080              * @param {Mixed} startValue The original field value
9081              */
9082         "beforecomplete" : true,
9083         /**
9084              * @event complete
9085              * Fires after editing is complete and any changed value has been written to the underlying field.
9086              * @param {Editor} this
9087              * @param {Mixed} value The current field value
9088              * @param {Mixed} startValue The original field value
9089              */
9090         "complete" : true,
9091         /**
9092          * @event specialkey
9093          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9094          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9095          * @param {Roo.form.Field} this
9096          * @param {Roo.EventObject} e The event object
9097          */
9098         "specialkey" : true
9099     });
9100 };
9101
9102 Roo.extend(Roo.Editor, Roo.Component, {
9103     /**
9104      * @cfg {Boolean/String} autosize
9105      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9106      * or "height" to adopt the height only (defaults to false)
9107      */
9108     /**
9109      * @cfg {Boolean} revertInvalid
9110      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9111      * validation fails (defaults to true)
9112      */
9113     /**
9114      * @cfg {Boolean} ignoreNoChange
9115      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9116      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9117      * will never be ignored.
9118      */
9119     /**
9120      * @cfg {Boolean} hideEl
9121      * False to keep the bound element visible while the editor is displayed (defaults to true)
9122      */
9123     /**
9124      * @cfg {Mixed} value
9125      * The data value of the underlying field (defaults to "")
9126      */
9127     value : "",
9128     /**
9129      * @cfg {String} alignment
9130      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9131      */
9132     alignment: "c-c?",
9133     /**
9134      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9135      * for bottom-right shadow (defaults to "frame")
9136      */
9137     shadow : "frame",
9138     /**
9139      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9140      */
9141     constrain : false,
9142     /**
9143      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9144      */
9145     completeOnEnter : false,
9146     /**
9147      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9148      */
9149     cancelOnEsc : false,
9150     /**
9151      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9152      */
9153     updateEl : false,
9154
9155     // private
9156     onRender : function(ct, position){
9157         this.el = new Roo.Layer({
9158             shadow: this.shadow,
9159             cls: "x-editor",
9160             parentEl : ct,
9161             shim : this.shim,
9162             shadowOffset:4,
9163             id: this.id,
9164             constrain: this.constrain
9165         });
9166         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9167         if(this.field.msgTarget != 'title'){
9168             this.field.msgTarget = 'qtip';
9169         }
9170         this.field.render(this.el);
9171         if(Roo.isGecko){
9172             this.field.el.dom.setAttribute('autocomplete', 'off');
9173         }
9174         this.field.on("specialkey", this.onSpecialKey, this);
9175         if(this.swallowKeys){
9176             this.field.el.swallowEvent(['keydown','keypress']);
9177         }
9178         this.field.show();
9179         this.field.on("blur", this.onBlur, this);
9180         if(this.field.grow){
9181             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9182         }
9183     },
9184
9185     onSpecialKey : function(field, e)
9186     {
9187         //Roo.log('editor onSpecialKey');
9188         if(this.completeOnEnter && e.getKey() == e.ENTER){
9189             e.stopEvent();
9190             this.completeEdit();
9191             return;
9192         }
9193         // do not fire special key otherwise it might hide close the editor...
9194         if(e.getKey() == e.ENTER){    
9195             return;
9196         }
9197         if(this.cancelOnEsc && e.getKey() == e.ESC){
9198             this.cancelEdit();
9199             return;
9200         } 
9201         this.fireEvent('specialkey', field, e);
9202     
9203     },
9204
9205     /**
9206      * Starts the editing process and shows the editor.
9207      * @param {String/HTMLElement/Element} el The element to edit
9208      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9209       * to the innerHTML of el.
9210      */
9211     startEdit : function(el, value){
9212         if(this.editing){
9213             this.completeEdit();
9214         }
9215         this.boundEl = Roo.get(el);
9216         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9217         if(!this.rendered){
9218             this.render(this.parentEl || document.body);
9219         }
9220         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9221             return;
9222         }
9223         this.startValue = v;
9224         this.field.setValue(v);
9225         if(this.autoSize){
9226             var sz = this.boundEl.getSize();
9227             switch(this.autoSize){
9228                 case "width":
9229                 this.setSize(sz.width,  "");
9230                 break;
9231                 case "height":
9232                 this.setSize("",  sz.height);
9233                 break;
9234                 default:
9235                 this.setSize(sz.width,  sz.height);
9236             }
9237         }
9238         this.el.alignTo(this.boundEl, this.alignment);
9239         this.editing = true;
9240         if(Roo.QuickTips){
9241             Roo.QuickTips.disable();
9242         }
9243         this.show();
9244     },
9245
9246     /**
9247      * Sets the height and width of this editor.
9248      * @param {Number} width The new width
9249      * @param {Number} height The new height
9250      */
9251     setSize : function(w, h){
9252         this.field.setSize(w, h);
9253         if(this.el){
9254             this.el.sync();
9255         }
9256     },
9257
9258     /**
9259      * Realigns the editor to the bound field based on the current alignment config value.
9260      */
9261     realign : function(){
9262         this.el.alignTo(this.boundEl, this.alignment);
9263     },
9264
9265     /**
9266      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9267      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9268      */
9269     completeEdit : function(remainVisible){
9270         if(!this.editing){
9271             return;
9272         }
9273         var v = this.getValue();
9274         if(this.revertInvalid !== false && !this.field.isValid()){
9275             v = this.startValue;
9276             this.cancelEdit(true);
9277         }
9278         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9279             this.editing = false;
9280             this.hide();
9281             return;
9282         }
9283         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9284             this.editing = false;
9285             if(this.updateEl && this.boundEl){
9286                 this.boundEl.update(v);
9287             }
9288             if(remainVisible !== true){
9289                 this.hide();
9290             }
9291             this.fireEvent("complete", this, v, this.startValue);
9292         }
9293     },
9294
9295     // private
9296     onShow : function(){
9297         this.el.show();
9298         if(this.hideEl !== false){
9299             this.boundEl.hide();
9300         }
9301         this.field.show();
9302         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9303             this.fixIEFocus = true;
9304             this.deferredFocus.defer(50, this);
9305         }else{
9306             this.field.focus();
9307         }
9308         this.fireEvent("startedit", this.boundEl, this.startValue);
9309     },
9310
9311     deferredFocus : function(){
9312         if(this.editing){
9313             this.field.focus();
9314         }
9315     },
9316
9317     /**
9318      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9319      * reverted to the original starting value.
9320      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9321      * cancel (defaults to false)
9322      */
9323     cancelEdit : function(remainVisible){
9324         if(this.editing){
9325             this.setValue(this.startValue);
9326             if(remainVisible !== true){
9327                 this.hide();
9328             }
9329         }
9330     },
9331
9332     // private
9333     onBlur : function(){
9334         if(this.allowBlur !== true && this.editing){
9335             this.completeEdit();
9336         }
9337     },
9338
9339     // private
9340     onHide : function(){
9341         if(this.editing){
9342             this.completeEdit();
9343             return;
9344         }
9345         this.field.blur();
9346         if(this.field.collapse){
9347             this.field.collapse();
9348         }
9349         this.el.hide();
9350         if(this.hideEl !== false){
9351             this.boundEl.show();
9352         }
9353         if(Roo.QuickTips){
9354             Roo.QuickTips.enable();
9355         }
9356     },
9357
9358     /**
9359      * Sets the data value of the editor
9360      * @param {Mixed} value Any valid value supported by the underlying field
9361      */
9362     setValue : function(v){
9363         this.field.setValue(v);
9364     },
9365
9366     /**
9367      * Gets the data value of the editor
9368      * @return {Mixed} The data value
9369      */
9370     getValue : function(){
9371         return this.field.getValue();
9372     }
9373 });/*
9374  * Based on:
9375  * Ext JS Library 1.1.1
9376  * Copyright(c) 2006-2007, Ext JS, LLC.
9377  *
9378  * Originally Released Under LGPL - original licence link has changed is not relivant.
9379  *
9380  * Fork - LGPL
9381  * <script type="text/javascript">
9382  */
9383  
9384 /**
9385  * @class Roo.BasicDialog
9386  * @extends Roo.util.Observable
9387  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9388  * <pre><code>
9389 var dlg = new Roo.BasicDialog("my-dlg", {
9390     height: 200,
9391     width: 300,
9392     minHeight: 100,
9393     minWidth: 150,
9394     modal: true,
9395     proxyDrag: true,
9396     shadow: true
9397 });
9398 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9399 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9400 dlg.addButton('Cancel', dlg.hide, dlg);
9401 dlg.show();
9402 </code></pre>
9403   <b>A Dialog should always be a direct child of the body element.</b>
9404  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9405  * @cfg {String} title Default text to display in the title bar (defaults to null)
9406  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9407  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9408  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9409  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9410  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9411  * (defaults to null with no animation)
9412  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9413  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9414  * property for valid values (defaults to 'all')
9415  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9416  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9417  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9418  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9419  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9420  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9421  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9422  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9423  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9424  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9425  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9426  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9427  * draggable = true (defaults to false)
9428  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9429  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9430  * shadow (defaults to false)
9431  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9432  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9433  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9434  * @cfg {Array} buttons Array of buttons
9435  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9436  * @constructor
9437  * Create a new BasicDialog.
9438  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9439  * @param {Object} config Configuration options
9440  */
9441 Roo.BasicDialog = function(el, config){
9442     this.el = Roo.get(el);
9443     var dh = Roo.DomHelper;
9444     if(!this.el && config && config.autoCreate){
9445         if(typeof config.autoCreate == "object"){
9446             if(!config.autoCreate.id){
9447                 config.autoCreate.id = el;
9448             }
9449             this.el = dh.append(document.body,
9450                         config.autoCreate, true);
9451         }else{
9452             this.el = dh.append(document.body,
9453                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9454         }
9455     }
9456     el = this.el;
9457     el.setDisplayed(true);
9458     el.hide = this.hideAction;
9459     this.id = el.id;
9460     el.addClass("x-dlg");
9461
9462     Roo.apply(this, config);
9463
9464     this.proxy = el.createProxy("x-dlg-proxy");
9465     this.proxy.hide = this.hideAction;
9466     this.proxy.setOpacity(.5);
9467     this.proxy.hide();
9468
9469     if(config.width){
9470         el.setWidth(config.width);
9471     }
9472     if(config.height){
9473         el.setHeight(config.height);
9474     }
9475     this.size = el.getSize();
9476     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9477         this.xy = [config.x,config.y];
9478     }else{
9479         this.xy = el.getCenterXY(true);
9480     }
9481     /** The header element @type Roo.Element */
9482     this.header = el.child("> .x-dlg-hd");
9483     /** The body element @type Roo.Element */
9484     this.body = el.child("> .x-dlg-bd");
9485     /** The footer element @type Roo.Element */
9486     this.footer = el.child("> .x-dlg-ft");
9487
9488     if(!this.header){
9489         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9490     }
9491     if(!this.body){
9492         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9493     }
9494
9495     this.header.unselectable();
9496     if(this.title){
9497         this.header.update(this.title);
9498     }
9499     // this element allows the dialog to be focused for keyboard event
9500     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9501     this.focusEl.swallowEvent("click", true);
9502
9503     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9504
9505     // wrap the body and footer for special rendering
9506     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9507     if(this.footer){
9508         this.bwrap.dom.appendChild(this.footer.dom);
9509     }
9510
9511     this.bg = this.el.createChild({
9512         tag: "div", cls:"x-dlg-bg",
9513         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9514     });
9515     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9516
9517
9518     if(this.autoScroll !== false && !this.autoTabs){
9519         this.body.setStyle("overflow", "auto");
9520     }
9521
9522     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9523
9524     if(this.closable !== false){
9525         this.el.addClass("x-dlg-closable");
9526         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9527         this.close.on("click", this.closeClick, this);
9528         this.close.addClassOnOver("x-dlg-close-over");
9529     }
9530     if(this.collapsible !== false){
9531         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9532         this.collapseBtn.on("click", this.collapseClick, this);
9533         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9534         this.header.on("dblclick", this.collapseClick, this);
9535     }
9536     if(this.resizable !== false){
9537         this.el.addClass("x-dlg-resizable");
9538         this.resizer = new Roo.Resizable(el, {
9539             minWidth: this.minWidth || 80,
9540             minHeight:this.minHeight || 80,
9541             handles: this.resizeHandles || "all",
9542             pinned: true
9543         });
9544         this.resizer.on("beforeresize", this.beforeResize, this);
9545         this.resizer.on("resize", this.onResize, this);
9546     }
9547     if(this.draggable !== false){
9548         el.addClass("x-dlg-draggable");
9549         if (!this.proxyDrag) {
9550             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9551         }
9552         else {
9553             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9554         }
9555         dd.setHandleElId(this.header.id);
9556         dd.endDrag = this.endMove.createDelegate(this);
9557         dd.startDrag = this.startMove.createDelegate(this);
9558         dd.onDrag = this.onDrag.createDelegate(this);
9559         dd.scroll = false;
9560         this.dd = dd;
9561     }
9562     if(this.modal){
9563         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9564         this.mask.enableDisplayMode("block");
9565         this.mask.hide();
9566         this.el.addClass("x-dlg-modal");
9567     }
9568     if(this.shadow){
9569         this.shadow = new Roo.Shadow({
9570             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9571             offset : this.shadowOffset
9572         });
9573     }else{
9574         this.shadowOffset = 0;
9575     }
9576     if(Roo.useShims && this.shim !== false){
9577         this.shim = this.el.createShim();
9578         this.shim.hide = this.hideAction;
9579         this.shim.hide();
9580     }else{
9581         this.shim = false;
9582     }
9583     if(this.autoTabs){
9584         this.initTabs();
9585     }
9586     if (this.buttons) { 
9587         var bts= this.buttons;
9588         this.buttons = [];
9589         Roo.each(bts, function(b) {
9590             this.addButton(b);
9591         }, this);
9592     }
9593     
9594     
9595     this.addEvents({
9596         /**
9597          * @event keydown
9598          * Fires when a key is pressed
9599          * @param {Roo.BasicDialog} this
9600          * @param {Roo.EventObject} e
9601          */
9602         "keydown" : true,
9603         /**
9604          * @event move
9605          * Fires when this dialog is moved by the user.
9606          * @param {Roo.BasicDialog} this
9607          * @param {Number} x The new page X
9608          * @param {Number} y The new page Y
9609          */
9610         "move" : true,
9611         /**
9612          * @event resize
9613          * Fires when this dialog is resized by the user.
9614          * @param {Roo.BasicDialog} this
9615          * @param {Number} width The new width
9616          * @param {Number} height The new height
9617          */
9618         "resize" : true,
9619         /**
9620          * @event beforehide
9621          * Fires before this dialog is hidden.
9622          * @param {Roo.BasicDialog} this
9623          */
9624         "beforehide" : true,
9625         /**
9626          * @event hide
9627          * Fires when this dialog is hidden.
9628          * @param {Roo.BasicDialog} this
9629          */
9630         "hide" : true,
9631         /**
9632          * @event beforeshow
9633          * Fires before this dialog is shown.
9634          * @param {Roo.BasicDialog} this
9635          */
9636         "beforeshow" : true,
9637         /**
9638          * @event show
9639          * Fires when this dialog is shown.
9640          * @param {Roo.BasicDialog} this
9641          */
9642         "show" : true
9643     });
9644     el.on("keydown", this.onKeyDown, this);
9645     el.on("mousedown", this.toFront, this);
9646     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9647     this.el.hide();
9648     Roo.DialogManager.register(this);
9649     Roo.BasicDialog.superclass.constructor.call(this);
9650 };
9651
9652 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9653     shadowOffset: Roo.isIE ? 6 : 5,
9654     minHeight: 80,
9655     minWidth: 200,
9656     minButtonWidth: 75,
9657     defaultButton: null,
9658     buttonAlign: "right",
9659     tabTag: 'div',
9660     firstShow: true,
9661
9662     /**
9663      * Sets the dialog title text
9664      * @param {String} text The title text to display
9665      * @return {Roo.BasicDialog} this
9666      */
9667     setTitle : function(text){
9668         this.header.update(text);
9669         return this;
9670     },
9671
9672     // private
9673     closeClick : function(){
9674         this.hide();
9675     },
9676
9677     // private
9678     collapseClick : function(){
9679         this[this.collapsed ? "expand" : "collapse"]();
9680     },
9681
9682     /**
9683      * Collapses the dialog to its minimized state (only the title bar is visible).
9684      * Equivalent to the user clicking the collapse dialog button.
9685      */
9686     collapse : function(){
9687         if(!this.collapsed){
9688             this.collapsed = true;
9689             this.el.addClass("x-dlg-collapsed");
9690             this.restoreHeight = this.el.getHeight();
9691             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9692         }
9693     },
9694
9695     /**
9696      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9697      * clicking the expand dialog button.
9698      */
9699     expand : function(){
9700         if(this.collapsed){
9701             this.collapsed = false;
9702             this.el.removeClass("x-dlg-collapsed");
9703             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9704         }
9705     },
9706
9707     /**
9708      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9709      * @return {Roo.TabPanel} The tabs component
9710      */
9711     initTabs : function(){
9712         var tabs = this.getTabs();
9713         while(tabs.getTab(0)){
9714             tabs.removeTab(0);
9715         }
9716         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9717             var dom = el.dom;
9718             tabs.addTab(Roo.id(dom), dom.title);
9719             dom.title = "";
9720         });
9721         tabs.activate(0);
9722         return tabs;
9723     },
9724
9725     // private
9726     beforeResize : function(){
9727         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9728     },
9729
9730     // private
9731     onResize : function(){
9732         this.refreshSize();
9733         this.syncBodyHeight();
9734         this.adjustAssets();
9735         this.focus();
9736         this.fireEvent("resize", this, this.size.width, this.size.height);
9737     },
9738
9739     // private
9740     onKeyDown : function(e){
9741         if(this.isVisible()){
9742             this.fireEvent("keydown", this, e);
9743         }
9744     },
9745
9746     /**
9747      * Resizes the dialog.
9748      * @param {Number} width
9749      * @param {Number} height
9750      * @return {Roo.BasicDialog} this
9751      */
9752     resizeTo : function(width, height){
9753         this.el.setSize(width, height);
9754         this.size = {width: width, height: height};
9755         this.syncBodyHeight();
9756         if(this.fixedcenter){
9757             this.center();
9758         }
9759         if(this.isVisible()){
9760             this.constrainXY();
9761             this.adjustAssets();
9762         }
9763         this.fireEvent("resize", this, width, height);
9764         return this;
9765     },
9766
9767
9768     /**
9769      * Resizes the dialog to fit the specified content size.
9770      * @param {Number} width
9771      * @param {Number} height
9772      * @return {Roo.BasicDialog} this
9773      */
9774     setContentSize : function(w, h){
9775         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9776         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9777         //if(!this.el.isBorderBox()){
9778             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9779             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9780         //}
9781         if(this.tabs){
9782             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9783             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9784         }
9785         this.resizeTo(w, h);
9786         return this;
9787     },
9788
9789     /**
9790      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9791      * executed in response to a particular key being pressed while the dialog is active.
9792      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9793      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9794      * @param {Function} fn The function to call
9795      * @param {Object} scope (optional) The scope of the function
9796      * @return {Roo.BasicDialog} this
9797      */
9798     addKeyListener : function(key, fn, scope){
9799         var keyCode, shift, ctrl, alt;
9800         if(typeof key == "object" && !(key instanceof Array)){
9801             keyCode = key["key"];
9802             shift = key["shift"];
9803             ctrl = key["ctrl"];
9804             alt = key["alt"];
9805         }else{
9806             keyCode = key;
9807         }
9808         var handler = function(dlg, e){
9809             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9810                 var k = e.getKey();
9811                 if(keyCode instanceof Array){
9812                     for(var i = 0, len = keyCode.length; i < len; i++){
9813                         if(keyCode[i] == k){
9814                           fn.call(scope || window, dlg, k, e);
9815                           return;
9816                         }
9817                     }
9818                 }else{
9819                     if(k == keyCode){
9820                         fn.call(scope || window, dlg, k, e);
9821                     }
9822                 }
9823             }
9824         };
9825         this.on("keydown", handler);
9826         return this;
9827     },
9828
9829     /**
9830      * Returns the TabPanel component (creates it if it doesn't exist).
9831      * Note: If you wish to simply check for the existence of tabs without creating them,
9832      * check for a null 'tabs' property.
9833      * @return {Roo.TabPanel} The tabs component
9834      */
9835     getTabs : function(){
9836         if(!this.tabs){
9837             this.el.addClass("x-dlg-auto-tabs");
9838             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9839             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9840         }
9841         return this.tabs;
9842     },
9843
9844     /**
9845      * Adds a button to the footer section of the dialog.
9846      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9847      * object or a valid Roo.DomHelper element config
9848      * @param {Function} handler The function called when the button is clicked
9849      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9850      * @return {Roo.Button} The new button
9851      */
9852     addButton : function(config, handler, scope){
9853         var dh = Roo.DomHelper;
9854         if(!this.footer){
9855             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9856         }
9857         if(!this.btnContainer){
9858             var tb = this.footer.createChild({
9859
9860                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9861                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9862             }, null, true);
9863             this.btnContainer = tb.firstChild.firstChild.firstChild;
9864         }
9865         var bconfig = {
9866             handler: handler,
9867             scope: scope,
9868             minWidth: this.minButtonWidth,
9869             hideParent:true
9870         };
9871         if(typeof config == "string"){
9872             bconfig.text = config;
9873         }else{
9874             if(config.tag){
9875                 bconfig.dhconfig = config;
9876             }else{
9877                 Roo.apply(bconfig, config);
9878             }
9879         }
9880         var fc = false;
9881         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9882             bconfig.position = Math.max(0, bconfig.position);
9883             fc = this.btnContainer.childNodes[bconfig.position];
9884         }
9885          
9886         var btn = new Roo.Button(
9887             fc ? 
9888                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9889                 : this.btnContainer.appendChild(document.createElement("td")),
9890             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9891             bconfig
9892         );
9893         this.syncBodyHeight();
9894         if(!this.buttons){
9895             /**
9896              * Array of all the buttons that have been added to this dialog via addButton
9897              * @type Array
9898              */
9899             this.buttons = [];
9900         }
9901         this.buttons.push(btn);
9902         return btn;
9903     },
9904
9905     /**
9906      * Sets the default button to be focused when the dialog is displayed.
9907      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9908      * @return {Roo.BasicDialog} this
9909      */
9910     setDefaultButton : function(btn){
9911         this.defaultButton = btn;
9912         return this;
9913     },
9914
9915     // private
9916     getHeaderFooterHeight : function(safe){
9917         var height = 0;
9918         if(this.header){
9919            height += this.header.getHeight();
9920         }
9921         if(this.footer){
9922            var fm = this.footer.getMargins();
9923             height += (this.footer.getHeight()+fm.top+fm.bottom);
9924         }
9925         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9926         height += this.centerBg.getPadding("tb");
9927         return height;
9928     },
9929
9930     // private
9931     syncBodyHeight : function()
9932     {
9933         var bd = this.body, // the text
9934             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9935             bw = this.bwrap;
9936         var height = this.size.height - this.getHeaderFooterHeight(false);
9937         bd.setHeight(height-bd.getMargins("tb"));
9938         var hh = this.header.getHeight();
9939         var h = this.size.height-hh;
9940         cb.setHeight(h);
9941         
9942         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9943         bw.setHeight(h-cb.getPadding("tb"));
9944         
9945         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9946         bd.setWidth(bw.getWidth(true));
9947         if(this.tabs){
9948             this.tabs.syncHeight();
9949             if(Roo.isIE){
9950                 this.tabs.el.repaint();
9951             }
9952         }
9953     },
9954
9955     /**
9956      * Restores the previous state of the dialog if Roo.state is configured.
9957      * @return {Roo.BasicDialog} this
9958      */
9959     restoreState : function(){
9960         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9961         if(box && box.width){
9962             this.xy = [box.x, box.y];
9963             this.resizeTo(box.width, box.height);
9964         }
9965         return this;
9966     },
9967
9968     // private
9969     beforeShow : function(){
9970         this.expand();
9971         if(this.fixedcenter){
9972             this.xy = this.el.getCenterXY(true);
9973         }
9974         if(this.modal){
9975             Roo.get(document.body).addClass("x-body-masked");
9976             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9977             this.mask.show();
9978         }
9979         this.constrainXY();
9980     },
9981
9982     // private
9983     animShow : function(){
9984         var b = Roo.get(this.animateTarget).getBox();
9985         this.proxy.setSize(b.width, b.height);
9986         this.proxy.setLocation(b.x, b.y);
9987         this.proxy.show();
9988         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9989                     true, .35, this.showEl.createDelegate(this));
9990     },
9991
9992     /**
9993      * Shows the dialog.
9994      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9995      * @return {Roo.BasicDialog} this
9996      */
9997     show : function(animateTarget){
9998         if (this.fireEvent("beforeshow", this) === false){
9999             return;
10000         }
10001         if(this.syncHeightBeforeShow){
10002             this.syncBodyHeight();
10003         }else if(this.firstShow){
10004             this.firstShow = false;
10005             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10006         }
10007         this.animateTarget = animateTarget || this.animateTarget;
10008         if(!this.el.isVisible()){
10009             this.beforeShow();
10010             if(this.animateTarget && Roo.get(this.animateTarget)){
10011                 this.animShow();
10012             }else{
10013                 this.showEl();
10014             }
10015         }
10016         return this;
10017     },
10018
10019     // private
10020     showEl : function(){
10021         this.proxy.hide();
10022         this.el.setXY(this.xy);
10023         this.el.show();
10024         this.adjustAssets(true);
10025         this.toFront();
10026         this.focus();
10027         // IE peekaboo bug - fix found by Dave Fenwick
10028         if(Roo.isIE){
10029             this.el.repaint();
10030         }
10031         this.fireEvent("show", this);
10032     },
10033
10034     /**
10035      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10036      * dialog itself will receive focus.
10037      */
10038     focus : function(){
10039         if(this.defaultButton){
10040             this.defaultButton.focus();
10041         }else{
10042             this.focusEl.focus();
10043         }
10044     },
10045
10046     // private
10047     constrainXY : function(){
10048         if(this.constraintoviewport !== false){
10049             if(!this.viewSize){
10050                 if(this.container){
10051                     var s = this.container.getSize();
10052                     this.viewSize = [s.width, s.height];
10053                 }else{
10054                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10055                 }
10056             }
10057             var s = Roo.get(this.container||document).getScroll();
10058
10059             var x = this.xy[0], y = this.xy[1];
10060             var w = this.size.width, h = this.size.height;
10061             var vw = this.viewSize[0], vh = this.viewSize[1];
10062             // only move it if it needs it
10063             var moved = false;
10064             // first validate right/bottom
10065             if(x + w > vw+s.left){
10066                 x = vw - w;
10067                 moved = true;
10068             }
10069             if(y + h > vh+s.top){
10070                 y = vh - h;
10071                 moved = true;
10072             }
10073             // then make sure top/left isn't negative
10074             if(x < s.left){
10075                 x = s.left;
10076                 moved = true;
10077             }
10078             if(y < s.top){
10079                 y = s.top;
10080                 moved = true;
10081             }
10082             if(moved){
10083                 // cache xy
10084                 this.xy = [x, y];
10085                 if(this.isVisible()){
10086                     this.el.setLocation(x, y);
10087                     this.adjustAssets();
10088                 }
10089             }
10090         }
10091     },
10092
10093     // private
10094     onDrag : function(){
10095         if(!this.proxyDrag){
10096             this.xy = this.el.getXY();
10097             this.adjustAssets();
10098         }
10099     },
10100
10101     // private
10102     adjustAssets : function(doShow){
10103         var x = this.xy[0], y = this.xy[1];
10104         var w = this.size.width, h = this.size.height;
10105         if(doShow === true){
10106             if(this.shadow){
10107                 this.shadow.show(this.el);
10108             }
10109             if(this.shim){
10110                 this.shim.show();
10111             }
10112         }
10113         if(this.shadow && this.shadow.isVisible()){
10114             this.shadow.show(this.el);
10115         }
10116         if(this.shim && this.shim.isVisible()){
10117             this.shim.setBounds(x, y, w, h);
10118         }
10119     },
10120
10121     // private
10122     adjustViewport : function(w, h){
10123         if(!w || !h){
10124             w = Roo.lib.Dom.getViewWidth();
10125             h = Roo.lib.Dom.getViewHeight();
10126         }
10127         // cache the size
10128         this.viewSize = [w, h];
10129         if(this.modal && this.mask.isVisible()){
10130             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10131             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10132         }
10133         if(this.isVisible()){
10134             this.constrainXY();
10135         }
10136     },
10137
10138     /**
10139      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10140      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10141      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10142      */
10143     destroy : function(removeEl){
10144         if(this.isVisible()){
10145             this.animateTarget = null;
10146             this.hide();
10147         }
10148         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10149         if(this.tabs){
10150             this.tabs.destroy(removeEl);
10151         }
10152         Roo.destroy(
10153              this.shim,
10154              this.proxy,
10155              this.resizer,
10156              this.close,
10157              this.mask
10158         );
10159         if(this.dd){
10160             this.dd.unreg();
10161         }
10162         if(this.buttons){
10163            for(var i = 0, len = this.buttons.length; i < len; i++){
10164                this.buttons[i].destroy();
10165            }
10166         }
10167         this.el.removeAllListeners();
10168         if(removeEl === true){
10169             this.el.update("");
10170             this.el.remove();
10171         }
10172         Roo.DialogManager.unregister(this);
10173     },
10174
10175     // private
10176     startMove : function(){
10177         if(this.proxyDrag){
10178             this.proxy.show();
10179         }
10180         if(this.constraintoviewport !== false){
10181             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10182         }
10183     },
10184
10185     // private
10186     endMove : function(){
10187         if(!this.proxyDrag){
10188             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10189         }else{
10190             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10191             this.proxy.hide();
10192         }
10193         this.refreshSize();
10194         this.adjustAssets();
10195         this.focus();
10196         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10197     },
10198
10199     /**
10200      * Brings this dialog to the front of any other visible dialogs
10201      * @return {Roo.BasicDialog} this
10202      */
10203     toFront : function(){
10204         Roo.DialogManager.bringToFront(this);
10205         return this;
10206     },
10207
10208     /**
10209      * Sends this dialog to the back (under) of any other visible dialogs
10210      * @return {Roo.BasicDialog} this
10211      */
10212     toBack : function(){
10213         Roo.DialogManager.sendToBack(this);
10214         return this;
10215     },
10216
10217     /**
10218      * Centers this dialog in the viewport
10219      * @return {Roo.BasicDialog} this
10220      */
10221     center : function(){
10222         var xy = this.el.getCenterXY(true);
10223         this.moveTo(xy[0], xy[1]);
10224         return this;
10225     },
10226
10227     /**
10228      * Moves the dialog's top-left corner to the specified point
10229      * @param {Number} x
10230      * @param {Number} y
10231      * @return {Roo.BasicDialog} this
10232      */
10233     moveTo : function(x, y){
10234         this.xy = [x,y];
10235         if(this.isVisible()){
10236             this.el.setXY(this.xy);
10237             this.adjustAssets();
10238         }
10239         return this;
10240     },
10241
10242     /**
10243      * Aligns the dialog to the specified element
10244      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10245      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10246      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10247      * @return {Roo.BasicDialog} this
10248      */
10249     alignTo : function(element, position, offsets){
10250         this.xy = this.el.getAlignToXY(element, position, offsets);
10251         if(this.isVisible()){
10252             this.el.setXY(this.xy);
10253             this.adjustAssets();
10254         }
10255         return this;
10256     },
10257
10258     /**
10259      * Anchors an element to another element and realigns it when the window is resized.
10260      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10261      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10262      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10263      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10264      * is a number, it is used as the buffer delay (defaults to 50ms).
10265      * @return {Roo.BasicDialog} this
10266      */
10267     anchorTo : function(el, alignment, offsets, monitorScroll){
10268         var action = function(){
10269             this.alignTo(el, alignment, offsets);
10270         };
10271         Roo.EventManager.onWindowResize(action, this);
10272         var tm = typeof monitorScroll;
10273         if(tm != 'undefined'){
10274             Roo.EventManager.on(window, 'scroll', action, this,
10275                 {buffer: tm == 'number' ? monitorScroll : 50});
10276         }
10277         action.call(this);
10278         return this;
10279     },
10280
10281     /**
10282      * Returns true if the dialog is visible
10283      * @return {Boolean}
10284      */
10285     isVisible : function(){
10286         return this.el.isVisible();
10287     },
10288
10289     // private
10290     animHide : function(callback){
10291         var b = Roo.get(this.animateTarget).getBox();
10292         this.proxy.show();
10293         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10294         this.el.hide();
10295         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10296                     this.hideEl.createDelegate(this, [callback]));
10297     },
10298
10299     /**
10300      * Hides the dialog.
10301      * @param {Function} callback (optional) Function to call when the dialog is hidden
10302      * @return {Roo.BasicDialog} this
10303      */
10304     hide : function(callback){
10305         if (this.fireEvent("beforehide", this) === false){
10306             return;
10307         }
10308         if(this.shadow){
10309             this.shadow.hide();
10310         }
10311         if(this.shim) {
10312           this.shim.hide();
10313         }
10314         // sometimes animateTarget seems to get set.. causing problems...
10315         // this just double checks..
10316         if(this.animateTarget && Roo.get(this.animateTarget)) {
10317            this.animHide(callback);
10318         }else{
10319             this.el.hide();
10320             this.hideEl(callback);
10321         }
10322         return this;
10323     },
10324
10325     // private
10326     hideEl : function(callback){
10327         this.proxy.hide();
10328         if(this.modal){
10329             this.mask.hide();
10330             Roo.get(document.body).removeClass("x-body-masked");
10331         }
10332         this.fireEvent("hide", this);
10333         if(typeof callback == "function"){
10334             callback();
10335         }
10336     },
10337
10338     // private
10339     hideAction : function(){
10340         this.setLeft("-10000px");
10341         this.setTop("-10000px");
10342         this.setStyle("visibility", "hidden");
10343     },
10344
10345     // private
10346     refreshSize : function(){
10347         this.size = this.el.getSize();
10348         this.xy = this.el.getXY();
10349         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10350     },
10351
10352     // private
10353     // z-index is managed by the DialogManager and may be overwritten at any time
10354     setZIndex : function(index){
10355         if(this.modal){
10356             this.mask.setStyle("z-index", index);
10357         }
10358         if(this.shim){
10359             this.shim.setStyle("z-index", ++index);
10360         }
10361         if(this.shadow){
10362             this.shadow.setZIndex(++index);
10363         }
10364         this.el.setStyle("z-index", ++index);
10365         if(this.proxy){
10366             this.proxy.setStyle("z-index", ++index);
10367         }
10368         if(this.resizer){
10369             this.resizer.proxy.setStyle("z-index", ++index);
10370         }
10371
10372         this.lastZIndex = index;
10373     },
10374
10375     /**
10376      * Returns the element for this dialog
10377      * @return {Roo.Element} The underlying dialog Element
10378      */
10379     getEl : function(){
10380         return this.el;
10381     }
10382 });
10383
10384 /**
10385  * @class Roo.DialogManager
10386  * Provides global access to BasicDialogs that have been created and
10387  * support for z-indexing (layering) multiple open dialogs.
10388  */
10389 Roo.DialogManager = function(){
10390     var list = {};
10391     var accessList = [];
10392     var front = null;
10393
10394     // private
10395     var sortDialogs = function(d1, d2){
10396         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10397     };
10398
10399     // private
10400     var orderDialogs = function(){
10401         accessList.sort(sortDialogs);
10402         var seed = Roo.DialogManager.zseed;
10403         for(var i = 0, len = accessList.length; i < len; i++){
10404             var dlg = accessList[i];
10405             if(dlg){
10406                 dlg.setZIndex(seed + (i*10));
10407             }
10408         }
10409     };
10410
10411     return {
10412         /**
10413          * The starting z-index for BasicDialogs (defaults to 9000)
10414          * @type Number The z-index value
10415          */
10416         zseed : 9000,
10417
10418         // private
10419         register : function(dlg){
10420             list[dlg.id] = dlg;
10421             accessList.push(dlg);
10422         },
10423
10424         // private
10425         unregister : function(dlg){
10426             delete list[dlg.id];
10427             var i=0;
10428             var len=0;
10429             if(!accessList.indexOf){
10430                 for(  i = 0, len = accessList.length; i < len; i++){
10431                     if(accessList[i] == dlg){
10432                         accessList.splice(i, 1);
10433                         return;
10434                     }
10435                 }
10436             }else{
10437                  i = accessList.indexOf(dlg);
10438                 if(i != -1){
10439                     accessList.splice(i, 1);
10440                 }
10441             }
10442         },
10443
10444         /**
10445          * Gets a registered dialog by id
10446          * @param {String/Object} id The id of the dialog or a dialog
10447          * @return {Roo.BasicDialog} this
10448          */
10449         get : function(id){
10450             return typeof id == "object" ? id : list[id];
10451         },
10452
10453         /**
10454          * Brings the specified dialog to the front
10455          * @param {String/Object} dlg The id of the dialog or a dialog
10456          * @return {Roo.BasicDialog} this
10457          */
10458         bringToFront : function(dlg){
10459             dlg = this.get(dlg);
10460             if(dlg != front){
10461                 front = dlg;
10462                 dlg._lastAccess = new Date().getTime();
10463                 orderDialogs();
10464             }
10465             return dlg;
10466         },
10467
10468         /**
10469          * Sends the specified dialog to the back
10470          * @param {String/Object} dlg The id of the dialog or a dialog
10471          * @return {Roo.BasicDialog} this
10472          */
10473         sendToBack : function(dlg){
10474             dlg = this.get(dlg);
10475             dlg._lastAccess = -(new Date().getTime());
10476             orderDialogs();
10477             return dlg;
10478         },
10479
10480         /**
10481          * Hides all dialogs
10482          */
10483         hideAll : function(){
10484             for(var id in list){
10485                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10486                     list[id].hide();
10487                 }
10488             }
10489         }
10490     };
10491 }();
10492
10493 /**
10494  * @class Roo.LayoutDialog
10495  * @extends Roo.BasicDialog
10496  * Dialog which provides adjustments for working with a layout in a Dialog.
10497  * Add your necessary layout config options to the dialog's config.<br>
10498  * Example usage (including a nested layout):
10499  * <pre><code>
10500 if(!dialog){
10501     dialog = new Roo.LayoutDialog("download-dlg", {
10502         modal: true,
10503         width:600,
10504         height:450,
10505         shadow:true,
10506         minWidth:500,
10507         minHeight:350,
10508         autoTabs:true,
10509         proxyDrag:true,
10510         // layout config merges with the dialog config
10511         center:{
10512             tabPosition: "top",
10513             alwaysShowTabs: true
10514         }
10515     });
10516     dialog.addKeyListener(27, dialog.hide, dialog);
10517     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10518     dialog.addButton("Build It!", this.getDownload, this);
10519
10520     // we can even add nested layouts
10521     var innerLayout = new Roo.BorderLayout("dl-inner", {
10522         east: {
10523             initialSize: 200,
10524             autoScroll:true,
10525             split:true
10526         },
10527         center: {
10528             autoScroll:true
10529         }
10530     });
10531     innerLayout.beginUpdate();
10532     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10533     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10534     innerLayout.endUpdate(true);
10535
10536     var layout = dialog.getLayout();
10537     layout.beginUpdate();
10538     layout.add("center", new Roo.ContentPanel("standard-panel",
10539                         {title: "Download the Source", fitToFrame:true}));
10540     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10541                {title: "Build your own roo.js"}));
10542     layout.getRegion("center").showPanel(sp);
10543     layout.endUpdate();
10544 }
10545 </code></pre>
10546     * @constructor
10547     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10548     * @param {Object} config configuration options
10549   */
10550 Roo.LayoutDialog = function(el, cfg){
10551     
10552     var config=  cfg;
10553     if (typeof(cfg) == 'undefined') {
10554         config = Roo.apply({}, el);
10555         // not sure why we use documentElement here.. - it should always be body.
10556         // IE7 borks horribly if we use documentElement.
10557         // webkit also does not like documentElement - it creates a body element...
10558         el = Roo.get( document.body || document.documentElement ).createChild();
10559         //config.autoCreate = true;
10560     }
10561     
10562     
10563     config.autoTabs = false;
10564     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10565     this.body.setStyle({overflow:"hidden", position:"relative"});
10566     this.layout = new Roo.BorderLayout(this.body.dom, config);
10567     this.layout.monitorWindowResize = false;
10568     this.el.addClass("x-dlg-auto-layout");
10569     // fix case when center region overwrites center function
10570     this.center = Roo.BasicDialog.prototype.center;
10571     this.on("show", this.layout.layout, this.layout, true);
10572     if (config.items) {
10573         var xitems = config.items;
10574         delete config.items;
10575         Roo.each(xitems, this.addxtype, this);
10576     }
10577     
10578     
10579 };
10580 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10581     /**
10582      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10583      * @deprecated
10584      */
10585     endUpdate : function(){
10586         this.layout.endUpdate();
10587     },
10588
10589     /**
10590      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10591      *  @deprecated
10592      */
10593     beginUpdate : function(){
10594         this.layout.beginUpdate();
10595     },
10596
10597     /**
10598      * Get the BorderLayout for this dialog
10599      * @return {Roo.BorderLayout}
10600      */
10601     getLayout : function(){
10602         return this.layout;
10603     },
10604
10605     showEl : function(){
10606         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10607         if(Roo.isIE7){
10608             this.layout.layout();
10609         }
10610     },
10611
10612     // private
10613     // Use the syncHeightBeforeShow config option to control this automatically
10614     syncBodyHeight : function(){
10615         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10616         if(this.layout){this.layout.layout();}
10617     },
10618     
10619       /**
10620      * Add an xtype element (actually adds to the layout.)
10621      * @return {Object} xdata xtype object data.
10622      */
10623     
10624     addxtype : function(c) {
10625         return this.layout.addxtype(c);
10626     }
10627 });/*
10628  * Based on:
10629  * Ext JS Library 1.1.1
10630  * Copyright(c) 2006-2007, Ext JS, LLC.
10631  *
10632  * Originally Released Under LGPL - original licence link has changed is not relivant.
10633  *
10634  * Fork - LGPL
10635  * <script type="text/javascript">
10636  */
10637  
10638 /**
10639  * @class Roo.MessageBox
10640  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10641  * Example usage:
10642  *<pre><code>
10643 // Basic alert:
10644 Roo.Msg.alert('Status', 'Changes saved successfully.');
10645
10646 // Prompt for user data:
10647 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10648     if (btn == 'ok'){
10649         // process text value...
10650     }
10651 });
10652
10653 // Show a dialog using config options:
10654 Roo.Msg.show({
10655    title:'Save Changes?',
10656    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10657    buttons: Roo.Msg.YESNOCANCEL,
10658    fn: processResult,
10659    animEl: 'elId'
10660 });
10661 </code></pre>
10662  * @singleton
10663  */
10664 Roo.MessageBox = function(){
10665     var dlg, opt, mask, waitTimer;
10666     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10667     var buttons, activeTextEl, bwidth;
10668
10669     // private
10670     var handleButton = function(button){
10671         dlg.hide();
10672         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10673     };
10674
10675     // private
10676     var handleHide = function(){
10677         if(opt && opt.cls){
10678             dlg.el.removeClass(opt.cls);
10679         }
10680         if(waitTimer){
10681             Roo.TaskMgr.stop(waitTimer);
10682             waitTimer = null;
10683         }
10684     };
10685
10686     // private
10687     var updateButtons = function(b){
10688         var width = 0;
10689         if(!b){
10690             buttons["ok"].hide();
10691             buttons["cancel"].hide();
10692             buttons["yes"].hide();
10693             buttons["no"].hide();
10694             dlg.footer.dom.style.display = 'none';
10695             return width;
10696         }
10697         dlg.footer.dom.style.display = '';
10698         for(var k in buttons){
10699             if(typeof buttons[k] != "function"){
10700                 if(b[k]){
10701                     buttons[k].show();
10702                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10703                     width += buttons[k].el.getWidth()+15;
10704                 }else{
10705                     buttons[k].hide();
10706                 }
10707             }
10708         }
10709         return width;
10710     };
10711
10712     // private
10713     var handleEsc = function(d, k, e){
10714         if(opt && opt.closable !== false){
10715             dlg.hide();
10716         }
10717         if(e){
10718             e.stopEvent();
10719         }
10720     };
10721
10722     return {
10723         /**
10724          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10725          * @return {Roo.BasicDialog} The BasicDialog element
10726          */
10727         getDialog : function(){
10728            if(!dlg){
10729                 dlg = new Roo.BasicDialog("x-msg-box", {
10730                     autoCreate : true,
10731                     shadow: true,
10732                     draggable: true,
10733                     resizable:false,
10734                     constraintoviewport:false,
10735                     fixedcenter:true,
10736                     collapsible : false,
10737                     shim:true,
10738                     modal: true,
10739                     width:400, height:100,
10740                     buttonAlign:"center",
10741                     closeClick : function(){
10742                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10743                             handleButton("no");
10744                         }else{
10745                             handleButton("cancel");
10746                         }
10747                     }
10748                 });
10749                 dlg.on("hide", handleHide);
10750                 mask = dlg.mask;
10751                 dlg.addKeyListener(27, handleEsc);
10752                 buttons = {};
10753                 var bt = this.buttonText;
10754                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10755                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10756                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10757                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10758                 bodyEl = dlg.body.createChild({
10759
10760                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
10761                 });
10762                 msgEl = bodyEl.dom.firstChild;
10763                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10764                 textboxEl.enableDisplayMode();
10765                 textboxEl.addKeyListener([10,13], function(){
10766                     if(dlg.isVisible() && opt && opt.buttons){
10767                         if(opt.buttons.ok){
10768                             handleButton("ok");
10769                         }else if(opt.buttons.yes){
10770                             handleButton("yes");
10771                         }
10772                     }
10773                 });
10774                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10775                 textareaEl.enableDisplayMode();
10776                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10777                 progressEl.enableDisplayMode();
10778                 var pf = progressEl.dom.firstChild;
10779                 if (pf) {
10780                     pp = Roo.get(pf.firstChild);
10781                     pp.setHeight(pf.offsetHeight);
10782                 }
10783                 
10784             }
10785             return dlg;
10786         },
10787
10788         /**
10789          * Updates the message box body text
10790          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10791          * the XHTML-compliant non-breaking space character '&amp;#160;')
10792          * @return {Roo.MessageBox} This message box
10793          */
10794         updateText : function(text){
10795             if(!dlg.isVisible() && !opt.width){
10796                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10797             }
10798             msgEl.innerHTML = text || '&#160;';
10799       
10800             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10801             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10802             var w = Math.max(
10803                     Math.min(opt.width || cw , this.maxWidth), 
10804                     Math.max(opt.minWidth || this.minWidth, bwidth)
10805             );
10806             if(opt.prompt){
10807                 activeTextEl.setWidth(w);
10808             }
10809             if(dlg.isVisible()){
10810                 dlg.fixedcenter = false;
10811             }
10812             // to big, make it scroll. = But as usual stupid IE does not support
10813             // !important..
10814             
10815             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10816                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10817                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10818             } else {
10819                 bodyEl.dom.style.height = '';
10820                 bodyEl.dom.style.overflowY = '';
10821             }
10822             if (cw > w) {
10823                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10824             } else {
10825                 bodyEl.dom.style.overflowX = '';
10826             }
10827             
10828             dlg.setContentSize(w, bodyEl.getHeight());
10829             if(dlg.isVisible()){
10830                 dlg.fixedcenter = true;
10831             }
10832             return this;
10833         },
10834
10835         /**
10836          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10837          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10838          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10839          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10840          * @return {Roo.MessageBox} This message box
10841          */
10842         updateProgress : function(value, text){
10843             if(text){
10844                 this.updateText(text);
10845             }
10846             if (pp) { // weird bug on my firefox - for some reason this is not defined
10847                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10848             }
10849             return this;
10850         },        
10851
10852         /**
10853          * Returns true if the message box is currently displayed
10854          * @return {Boolean} True if the message box is visible, else false
10855          */
10856         isVisible : function(){
10857             return dlg && dlg.isVisible();  
10858         },
10859
10860         /**
10861          * Hides the message box if it is displayed
10862          */
10863         hide : function(){
10864             if(this.isVisible()){
10865                 dlg.hide();
10866             }  
10867         },
10868
10869         /**
10870          * Displays a new message box, or reinitializes an existing message box, based on the config options
10871          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10872          * The following config object properties are supported:
10873          * <pre>
10874 Property    Type             Description
10875 ----------  ---------------  ------------------------------------------------------------------------------------
10876 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10877                                    closes (defaults to undefined)
10878 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10879                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10880 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10881                                    progress and wait dialogs will ignore this property and always hide the
10882                                    close button as they can only be closed programmatically.
10883 cls               String           A custom CSS class to apply to the message box element
10884 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10885                                    displayed (defaults to 75)
10886 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10887                                    function will be btn (the name of the button that was clicked, if applicable,
10888                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10889                                    Progress and wait dialogs will ignore this option since they do not respond to
10890                                    user actions and can only be closed programmatically, so any required function
10891                                    should be called by the same code after it closes the dialog.
10892 icon              String           A CSS class that provides a background image to be used as an icon for
10893                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10894 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10895 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10896 modal             Boolean          False to allow user interaction with the page while the message box is
10897                                    displayed (defaults to true)
10898 msg               String           A string that will replace the existing message box body text (defaults
10899                                    to the XHTML-compliant non-breaking space character '&#160;')
10900 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10901 progress          Boolean          True to display a progress bar (defaults to false)
10902 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10903 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10904 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10905 title             String           The title text
10906 value             String           The string value to set into the active textbox element if displayed
10907 wait              Boolean          True to display a progress bar (defaults to false)
10908 width             Number           The width of the dialog in pixels
10909 </pre>
10910          *
10911          * Example usage:
10912          * <pre><code>
10913 Roo.Msg.show({
10914    title: 'Address',
10915    msg: 'Please enter your address:',
10916    width: 300,
10917    buttons: Roo.MessageBox.OKCANCEL,
10918    multiline: true,
10919    fn: saveAddress,
10920    animEl: 'addAddressBtn'
10921 });
10922 </code></pre>
10923          * @param {Object} config Configuration options
10924          * @return {Roo.MessageBox} This message box
10925          */
10926         show : function(options)
10927         {
10928             
10929             // this causes nightmares if you show one dialog after another
10930             // especially on callbacks..
10931              
10932             if(this.isVisible()){
10933                 
10934                 this.hide();
10935                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10936                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10937                 Roo.log("New Dialog Message:" +  options.msg )
10938                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10939                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10940                 
10941             }
10942             var d = this.getDialog();
10943             opt = options;
10944             d.setTitle(opt.title || "&#160;");
10945             d.close.setDisplayed(opt.closable !== false);
10946             activeTextEl = textboxEl;
10947             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10948             if(opt.prompt){
10949                 if(opt.multiline){
10950                     textboxEl.hide();
10951                     textareaEl.show();
10952                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10953                         opt.multiline : this.defaultTextHeight);
10954                     activeTextEl = textareaEl;
10955                 }else{
10956                     textboxEl.show();
10957                     textareaEl.hide();
10958                 }
10959             }else{
10960                 textboxEl.hide();
10961                 textareaEl.hide();
10962             }
10963             progressEl.setDisplayed(opt.progress === true);
10964             this.updateProgress(0);
10965             activeTextEl.dom.value = opt.value || "";
10966             if(opt.prompt){
10967                 dlg.setDefaultButton(activeTextEl);
10968             }else{
10969                 var bs = opt.buttons;
10970                 var db = null;
10971                 if(bs && bs.ok){
10972                     db = buttons["ok"];
10973                 }else if(bs && bs.yes){
10974                     db = buttons["yes"];
10975                 }
10976                 dlg.setDefaultButton(db);
10977             }
10978             bwidth = updateButtons(opt.buttons);
10979             this.updateText(opt.msg);
10980             if(opt.cls){
10981                 d.el.addClass(opt.cls);
10982             }
10983             d.proxyDrag = opt.proxyDrag === true;
10984             d.modal = opt.modal !== false;
10985             d.mask = opt.modal !== false ? mask : false;
10986             if(!d.isVisible()){
10987                 // force it to the end of the z-index stack so it gets a cursor in FF
10988                 document.body.appendChild(dlg.el.dom);
10989                 d.animateTarget = null;
10990                 d.show(options.animEl);
10991             }
10992             return this;
10993         },
10994
10995         /**
10996          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10997          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10998          * and closing the message box when the process is complete.
10999          * @param {String} title The title bar text
11000          * @param {String} msg The message box body text
11001          * @return {Roo.MessageBox} This message box
11002          */
11003         progress : function(title, msg){
11004             this.show({
11005                 title : title,
11006                 msg : msg,
11007                 buttons: false,
11008                 progress:true,
11009                 closable:false,
11010                 minWidth: this.minProgressWidth,
11011                 modal : true
11012             });
11013             return this;
11014         },
11015
11016         /**
11017          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11018          * If a callback function is passed it will be called after the user clicks the button, and the
11019          * id of the button that was clicked will be passed as the only parameter to the callback
11020          * (could also be the top-right close button).
11021          * @param {String} title The title bar text
11022          * @param {String} msg The message box body text
11023          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11024          * @param {Object} scope (optional) The scope of the callback function
11025          * @return {Roo.MessageBox} This message box
11026          */
11027         alert : function(title, msg, fn, scope){
11028             this.show({
11029                 title : title,
11030                 msg : msg,
11031                 buttons: this.OK,
11032                 fn: fn,
11033                 scope : scope,
11034                 modal : true
11035             });
11036             return this;
11037         },
11038
11039         /**
11040          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11041          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11042          * You are responsible for closing the message box when the process is complete.
11043          * @param {String} msg The message box body text
11044          * @param {String} title (optional) The title bar text
11045          * @return {Roo.MessageBox} This message box
11046          */
11047         wait : function(msg, title){
11048             this.show({
11049                 title : title,
11050                 msg : msg,
11051                 buttons: false,
11052                 closable:false,
11053                 progress:true,
11054                 modal:true,
11055                 width:300,
11056                 wait:true
11057             });
11058             waitTimer = Roo.TaskMgr.start({
11059                 run: function(i){
11060                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11061                 },
11062                 interval: 1000
11063             });
11064             return this;
11065         },
11066
11067         /**
11068          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11069          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11070          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11071          * @param {String} title The title bar text
11072          * @param {String} msg The message box body text
11073          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11074          * @param {Object} scope (optional) The scope of the callback function
11075          * @return {Roo.MessageBox} This message box
11076          */
11077         confirm : function(title, msg, fn, scope){
11078             this.show({
11079                 title : title,
11080                 msg : msg,
11081                 buttons: this.YESNO,
11082                 fn: fn,
11083                 scope : scope,
11084                 modal : true
11085             });
11086             return this;
11087         },
11088
11089         /**
11090          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11091          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11092          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11093          * (could also be the top-right close button) and the text that was entered will be passed as the two
11094          * parameters to the callback.
11095          * @param {String} title The title bar text
11096          * @param {String} msg The message box body text
11097          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11098          * @param {Object} scope (optional) The scope of the callback function
11099          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11100          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11101          * @return {Roo.MessageBox} This message box
11102          */
11103         prompt : function(title, msg, fn, scope, multiline){
11104             this.show({
11105                 title : title,
11106                 msg : msg,
11107                 buttons: this.OKCANCEL,
11108                 fn: fn,
11109                 minWidth:250,
11110                 scope : scope,
11111                 prompt:true,
11112                 multiline: multiline,
11113                 modal : true
11114             });
11115             return this;
11116         },
11117
11118         /**
11119          * Button config that displays a single OK button
11120          * @type Object
11121          */
11122         OK : {ok:true},
11123         /**
11124          * Button config that displays Yes and No buttons
11125          * @type Object
11126          */
11127         YESNO : {yes:true, no:true},
11128         /**
11129          * Button config that displays OK and Cancel buttons
11130          * @type Object
11131          */
11132         OKCANCEL : {ok:true, cancel:true},
11133         /**
11134          * Button config that displays Yes, No and Cancel buttons
11135          * @type Object
11136          */
11137         YESNOCANCEL : {yes:true, no:true, cancel:true},
11138
11139         /**
11140          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11141          * @type Number
11142          */
11143         defaultTextHeight : 75,
11144         /**
11145          * The maximum width in pixels of the message box (defaults to 600)
11146          * @type Number
11147          */
11148         maxWidth : 600,
11149         /**
11150          * The minimum width in pixels of the message box (defaults to 100)
11151          * @type Number
11152          */
11153         minWidth : 100,
11154         /**
11155          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11156          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11157          * @type Number
11158          */
11159         minProgressWidth : 250,
11160         /**
11161          * An object containing the default button text strings that can be overriden for localized language support.
11162          * Supported properties are: ok, cancel, yes and no.
11163          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11164          * @type Object
11165          */
11166         buttonText : {
11167             ok : "OK",
11168             cancel : "Cancel",
11169             yes : "Yes",
11170             no : "No"
11171         }
11172     };
11173 }();
11174
11175 /**
11176  * Shorthand for {@link Roo.MessageBox}
11177  */
11178 Roo.Msg = Roo.MessageBox;/*
11179  * Based on:
11180  * Ext JS Library 1.1.1
11181  * Copyright(c) 2006-2007, Ext JS, LLC.
11182  *
11183  * Originally Released Under LGPL - original licence link has changed is not relivant.
11184  *
11185  * Fork - LGPL
11186  * <script type="text/javascript">
11187  */
11188 /**
11189  * @class Roo.QuickTips
11190  * Provides attractive and customizable tooltips for any element.
11191  * @singleton
11192  */
11193 Roo.QuickTips = function(){
11194     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11195     var ce, bd, xy, dd;
11196     var visible = false, disabled = true, inited = false;
11197     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11198     
11199     var onOver = function(e){
11200         if(disabled){
11201             return;
11202         }
11203         var t = e.getTarget();
11204         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11205             return;
11206         }
11207         if(ce && t == ce.el){
11208             clearTimeout(hideProc);
11209             return;
11210         }
11211         if(t && tagEls[t.id]){
11212             tagEls[t.id].el = t;
11213             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11214             return;
11215         }
11216         var ttp, et = Roo.fly(t);
11217         var ns = cfg.namespace;
11218         if(tm.interceptTitles && t.title){
11219             ttp = t.title;
11220             t.qtip = ttp;
11221             t.removeAttribute("title");
11222             e.preventDefault();
11223         }else{
11224             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11225         }
11226         if(ttp){
11227             showProc = show.defer(tm.showDelay, tm, [{
11228                 el: t, 
11229                 text: ttp.replace(/\\n/g,'<br/>'),
11230                 width: et.getAttributeNS(ns, cfg.width),
11231                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11232                 title: et.getAttributeNS(ns, cfg.title),
11233                     cls: et.getAttributeNS(ns, cfg.cls)
11234             }]);
11235         }
11236     };
11237     
11238     var onOut = function(e){
11239         clearTimeout(showProc);
11240         var t = e.getTarget();
11241         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11242             hideProc = setTimeout(hide, tm.hideDelay);
11243         }
11244     };
11245     
11246     var onMove = function(e){
11247         if(disabled){
11248             return;
11249         }
11250         xy = e.getXY();
11251         xy[1] += 18;
11252         if(tm.trackMouse && ce){
11253             el.setXY(xy);
11254         }
11255     };
11256     
11257     var onDown = function(e){
11258         clearTimeout(showProc);
11259         clearTimeout(hideProc);
11260         if(!e.within(el)){
11261             if(tm.hideOnClick){
11262                 hide();
11263                 tm.disable();
11264                 tm.enable.defer(100, tm);
11265             }
11266         }
11267     };
11268     
11269     var getPad = function(){
11270         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11271     };
11272
11273     var show = function(o){
11274         if(disabled){
11275             return;
11276         }
11277         clearTimeout(dismissProc);
11278         ce = o;
11279         if(removeCls){ // in case manually hidden
11280             el.removeClass(removeCls);
11281             removeCls = null;
11282         }
11283         if(ce.cls){
11284             el.addClass(ce.cls);
11285             removeCls = ce.cls;
11286         }
11287         if(ce.title){
11288             tipTitle.update(ce.title);
11289             tipTitle.show();
11290         }else{
11291             tipTitle.update('');
11292             tipTitle.hide();
11293         }
11294         el.dom.style.width  = tm.maxWidth+'px';
11295         //tipBody.dom.style.width = '';
11296         tipBodyText.update(o.text);
11297         var p = getPad(), w = ce.width;
11298         if(!w){
11299             var td = tipBodyText.dom;
11300             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11301             if(aw > tm.maxWidth){
11302                 w = tm.maxWidth;
11303             }else if(aw < tm.minWidth){
11304                 w = tm.minWidth;
11305             }else{
11306                 w = aw;
11307             }
11308         }
11309         //tipBody.setWidth(w);
11310         el.setWidth(parseInt(w, 10) + p);
11311         if(ce.autoHide === false){
11312             close.setDisplayed(true);
11313             if(dd){
11314                 dd.unlock();
11315             }
11316         }else{
11317             close.setDisplayed(false);
11318             if(dd){
11319                 dd.lock();
11320             }
11321         }
11322         if(xy){
11323             el.avoidY = xy[1]-18;
11324             el.setXY(xy);
11325         }
11326         if(tm.animate){
11327             el.setOpacity(.1);
11328             el.setStyle("visibility", "visible");
11329             el.fadeIn({callback: afterShow});
11330         }else{
11331             afterShow();
11332         }
11333     };
11334     
11335     var afterShow = function(){
11336         if(ce){
11337             el.show();
11338             esc.enable();
11339             if(tm.autoDismiss && ce.autoHide !== false){
11340                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11341             }
11342         }
11343     };
11344     
11345     var hide = function(noanim){
11346         clearTimeout(dismissProc);
11347         clearTimeout(hideProc);
11348         ce = null;
11349         if(el.isVisible()){
11350             esc.disable();
11351             if(noanim !== true && tm.animate){
11352                 el.fadeOut({callback: afterHide});
11353             }else{
11354                 afterHide();
11355             } 
11356         }
11357     };
11358     
11359     var afterHide = function(){
11360         el.hide();
11361         if(removeCls){
11362             el.removeClass(removeCls);
11363             removeCls = null;
11364         }
11365     };
11366     
11367     return {
11368         /**
11369         * @cfg {Number} minWidth
11370         * The minimum width of the quick tip (defaults to 40)
11371         */
11372        minWidth : 40,
11373         /**
11374         * @cfg {Number} maxWidth
11375         * The maximum width of the quick tip (defaults to 300)
11376         */
11377        maxWidth : 300,
11378         /**
11379         * @cfg {Boolean} interceptTitles
11380         * True to automatically use the element's DOM title value if available (defaults to false)
11381         */
11382        interceptTitles : false,
11383         /**
11384         * @cfg {Boolean} trackMouse
11385         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11386         */
11387        trackMouse : false,
11388         /**
11389         * @cfg {Boolean} hideOnClick
11390         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11391         */
11392        hideOnClick : true,
11393         /**
11394         * @cfg {Number} showDelay
11395         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11396         */
11397        showDelay : 500,
11398         /**
11399         * @cfg {Number} hideDelay
11400         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11401         */
11402        hideDelay : 200,
11403         /**
11404         * @cfg {Boolean} autoHide
11405         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11406         * Used in conjunction with hideDelay.
11407         */
11408        autoHide : true,
11409         /**
11410         * @cfg {Boolean}
11411         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11412         * (defaults to true).  Used in conjunction with autoDismissDelay.
11413         */
11414        autoDismiss : true,
11415         /**
11416         * @cfg {Number}
11417         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11418         */
11419        autoDismissDelay : 5000,
11420        /**
11421         * @cfg {Boolean} animate
11422         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11423         */
11424        animate : false,
11425
11426        /**
11427         * @cfg {String} title
11428         * Title text to display (defaults to '').  This can be any valid HTML markup.
11429         */
11430         title: '',
11431        /**
11432         * @cfg {String} text
11433         * Body text to display (defaults to '').  This can be any valid HTML markup.
11434         */
11435         text : '',
11436        /**
11437         * @cfg {String} cls
11438         * A CSS class to apply to the base quick tip element (defaults to '').
11439         */
11440         cls : '',
11441        /**
11442         * @cfg {Number} width
11443         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11444         * minWidth or maxWidth.
11445         */
11446         width : null,
11447
11448     /**
11449      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11450      * or display QuickTips in a page.
11451      */
11452        init : function(){
11453           tm = Roo.QuickTips;
11454           cfg = tm.tagConfig;
11455           if(!inited){
11456               if(!Roo.isReady){ // allow calling of init() before onReady
11457                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11458                   return;
11459               }
11460               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11461               el.fxDefaults = {stopFx: true};
11462               // maximum custom styling
11463               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
11464               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
11465               tipTitle = el.child('h3');
11466               tipTitle.enableDisplayMode("block");
11467               tipBody = el.child('div.x-tip-bd');
11468               tipBodyText = el.child('div.x-tip-bd-inner');
11469               //bdLeft = el.child('div.x-tip-bd-left');
11470               //bdRight = el.child('div.x-tip-bd-right');
11471               close = el.child('div.x-tip-close');
11472               close.enableDisplayMode("block");
11473               close.on("click", hide);
11474               var d = Roo.get(document);
11475               d.on("mousedown", onDown);
11476               d.on("mouseover", onOver);
11477               d.on("mouseout", onOut);
11478               d.on("mousemove", onMove);
11479               esc = d.addKeyListener(27, hide);
11480               esc.disable();
11481               if(Roo.dd.DD){
11482                   dd = el.initDD("default", null, {
11483                       onDrag : function(){
11484                           el.sync();  
11485                       }
11486                   });
11487                   dd.setHandleElId(tipTitle.id);
11488                   dd.lock();
11489               }
11490               inited = true;
11491           }
11492           this.enable(); 
11493        },
11494
11495     /**
11496      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11497      * are supported:
11498      * <pre>
11499 Property    Type                   Description
11500 ----------  ---------------------  ------------------------------------------------------------------------
11501 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11502      * </ul>
11503      * @param {Object} config The config object
11504      */
11505        register : function(config){
11506            var cs = config instanceof Array ? config : arguments;
11507            for(var i = 0, len = cs.length; i < len; i++) {
11508                var c = cs[i];
11509                var target = c.target;
11510                if(target){
11511                    if(target instanceof Array){
11512                        for(var j = 0, jlen = target.length; j < jlen; j++){
11513                            tagEls[target[j]] = c;
11514                        }
11515                    }else{
11516                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11517                    }
11518                }
11519            }
11520        },
11521
11522     /**
11523      * Removes this quick tip from its element and destroys it.
11524      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11525      */
11526        unregister : function(el){
11527            delete tagEls[Roo.id(el)];
11528        },
11529
11530     /**
11531      * Enable this quick tip.
11532      */
11533        enable : function(){
11534            if(inited && disabled){
11535                locks.pop();
11536                if(locks.length < 1){
11537                    disabled = false;
11538                }
11539            }
11540        },
11541
11542     /**
11543      * Disable this quick tip.
11544      */
11545        disable : function(){
11546           disabled = true;
11547           clearTimeout(showProc);
11548           clearTimeout(hideProc);
11549           clearTimeout(dismissProc);
11550           if(ce){
11551               hide(true);
11552           }
11553           locks.push(1);
11554        },
11555
11556     /**
11557      * Returns true if the quick tip is enabled, else false.
11558      */
11559        isEnabled : function(){
11560             return !disabled;
11561        },
11562
11563         // private
11564        tagConfig : {
11565            namespace : "roo", // was ext?? this may break..
11566            alt_namespace : "ext",
11567            attribute : "qtip",
11568            width : "width",
11569            target : "target",
11570            title : "qtitle",
11571            hide : "hide",
11572            cls : "qclass"
11573        }
11574    };
11575 }();
11576
11577 // backwards compat
11578 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11579  * Based on:
11580  * Ext JS Library 1.1.1
11581  * Copyright(c) 2006-2007, Ext JS, LLC.
11582  *
11583  * Originally Released Under LGPL - original licence link has changed is not relivant.
11584  *
11585  * Fork - LGPL
11586  * <script type="text/javascript">
11587  */
11588  
11589
11590 /**
11591  * @class Roo.tree.TreePanel
11592  * @extends Roo.data.Tree
11593
11594  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11595  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11596  * @cfg {Boolean} enableDD true to enable drag and drop
11597  * @cfg {Boolean} enableDrag true to enable just drag
11598  * @cfg {Boolean} enableDrop true to enable just drop
11599  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11600  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11601  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11602  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11603  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11604  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11605  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11606  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11607  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11608  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11609  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11610  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11611  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11612  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11613  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11614  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11615  * 
11616  * @constructor
11617  * @param {String/HTMLElement/Element} el The container element
11618  * @param {Object} config
11619  */
11620 Roo.tree.TreePanel = function(el, config){
11621     var root = false;
11622     var loader = false;
11623     if (config.root) {
11624         root = config.root;
11625         delete config.root;
11626     }
11627     if (config.loader) {
11628         loader = config.loader;
11629         delete config.loader;
11630     }
11631     
11632     Roo.apply(this, config);
11633     Roo.tree.TreePanel.superclass.constructor.call(this);
11634     this.el = Roo.get(el);
11635     this.el.addClass('x-tree');
11636     //console.log(root);
11637     if (root) {
11638         this.setRootNode( Roo.factory(root, Roo.tree));
11639     }
11640     if (loader) {
11641         this.loader = Roo.factory(loader, Roo.tree);
11642     }
11643    /**
11644     * Read-only. The id of the container element becomes this TreePanel's id.
11645     */
11646     this.id = this.el.id;
11647     this.addEvents({
11648         /**
11649         * @event beforeload
11650         * Fires before a node is loaded, return false to cancel
11651         * @param {Node} node The node being loaded
11652         */
11653         "beforeload" : true,
11654         /**
11655         * @event load
11656         * Fires when a node is loaded
11657         * @param {Node} node The node that was loaded
11658         */
11659         "load" : true,
11660         /**
11661         * @event textchange
11662         * Fires when the text for a node is changed
11663         * @param {Node} node The node
11664         * @param {String} text The new text
11665         * @param {String} oldText The old text
11666         */
11667         "textchange" : true,
11668         /**
11669         * @event beforeexpand
11670         * Fires before a node is expanded, return false to cancel.
11671         * @param {Node} node The node
11672         * @param {Boolean} deep
11673         * @param {Boolean} anim
11674         */
11675         "beforeexpand" : true,
11676         /**
11677         * @event beforecollapse
11678         * Fires before a node is collapsed, return false to cancel.
11679         * @param {Node} node The node
11680         * @param {Boolean} deep
11681         * @param {Boolean} anim
11682         */
11683         "beforecollapse" : true,
11684         /**
11685         * @event expand
11686         * Fires when a node is expanded
11687         * @param {Node} node The node
11688         */
11689         "expand" : true,
11690         /**
11691         * @event disabledchange
11692         * Fires when the disabled status of a node changes
11693         * @param {Node} node The node
11694         * @param {Boolean} disabled
11695         */
11696         "disabledchange" : true,
11697         /**
11698         * @event collapse
11699         * Fires when a node is collapsed
11700         * @param {Node} node The node
11701         */
11702         "collapse" : true,
11703         /**
11704         * @event beforeclick
11705         * Fires before click processing on a node. Return false to cancel the default action.
11706         * @param {Node} node The node
11707         * @param {Roo.EventObject} e The event object
11708         */
11709         "beforeclick":true,
11710         /**
11711         * @event checkchange
11712         * Fires when a node with a checkbox's checked property changes
11713         * @param {Node} this This node
11714         * @param {Boolean} checked
11715         */
11716         "checkchange":true,
11717         /**
11718         * @event click
11719         * Fires when a node is clicked
11720         * @param {Node} node The node
11721         * @param {Roo.EventObject} e The event object
11722         */
11723         "click":true,
11724         /**
11725         * @event dblclick
11726         * Fires when a node is double clicked
11727         * @param {Node} node The node
11728         * @param {Roo.EventObject} e The event object
11729         */
11730         "dblclick":true,
11731         /**
11732         * @event contextmenu
11733         * Fires when a node is right clicked
11734         * @param {Node} node The node
11735         * @param {Roo.EventObject} e The event object
11736         */
11737         "contextmenu":true,
11738         /**
11739         * @event beforechildrenrendered
11740         * Fires right before the child nodes for a node are rendered
11741         * @param {Node} node The node
11742         */
11743         "beforechildrenrendered":true,
11744         /**
11745         * @event startdrag
11746         * Fires when a node starts being dragged
11747         * @param {Roo.tree.TreePanel} this
11748         * @param {Roo.tree.TreeNode} node
11749         * @param {event} e The raw browser event
11750         */ 
11751        "startdrag" : true,
11752        /**
11753         * @event enddrag
11754         * Fires when a drag operation is complete
11755         * @param {Roo.tree.TreePanel} this
11756         * @param {Roo.tree.TreeNode} node
11757         * @param {event} e The raw browser event
11758         */
11759        "enddrag" : true,
11760        /**
11761         * @event dragdrop
11762         * Fires when a dragged node is dropped on a valid DD target
11763         * @param {Roo.tree.TreePanel} this
11764         * @param {Roo.tree.TreeNode} node
11765         * @param {DD} dd The dd it was dropped on
11766         * @param {event} e The raw browser event
11767         */
11768        "dragdrop" : true,
11769        /**
11770         * @event beforenodedrop
11771         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11772         * passed to handlers has the following properties:<br />
11773         * <ul style="padding:5px;padding-left:16px;">
11774         * <li>tree - The TreePanel</li>
11775         * <li>target - The node being targeted for the drop</li>
11776         * <li>data - The drag data from the drag source</li>
11777         * <li>point - The point of the drop - append, above or below</li>
11778         * <li>source - The drag source</li>
11779         * <li>rawEvent - Raw mouse event</li>
11780         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11781         * to be inserted by setting them on this object.</li>
11782         * <li>cancel - Set this to true to cancel the drop.</li>
11783         * </ul>
11784         * @param {Object} dropEvent
11785         */
11786        "beforenodedrop" : true,
11787        /**
11788         * @event nodedrop
11789         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11790         * passed to handlers has the following properties:<br />
11791         * <ul style="padding:5px;padding-left:16px;">
11792         * <li>tree - The TreePanel</li>
11793         * <li>target - The node being targeted for the drop</li>
11794         * <li>data - The drag data from the drag source</li>
11795         * <li>point - The point of the drop - append, above or below</li>
11796         * <li>source - The drag source</li>
11797         * <li>rawEvent - Raw mouse event</li>
11798         * <li>dropNode - Dropped node(s).</li>
11799         * </ul>
11800         * @param {Object} dropEvent
11801         */
11802        "nodedrop" : true,
11803         /**
11804         * @event nodedragover
11805         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11806         * passed to handlers has the following properties:<br />
11807         * <ul style="padding:5px;padding-left:16px;">
11808         * <li>tree - The TreePanel</li>
11809         * <li>target - The node being targeted for the drop</li>
11810         * <li>data - The drag data from the drag source</li>
11811         * <li>point - The point of the drop - append, above or below</li>
11812         * <li>source - The drag source</li>
11813         * <li>rawEvent - Raw mouse event</li>
11814         * <li>dropNode - Drop node(s) provided by the source.</li>
11815         * <li>cancel - Set this to true to signal drop not allowed.</li>
11816         * </ul>
11817         * @param {Object} dragOverEvent
11818         */
11819        "nodedragover" : true
11820         
11821     });
11822     if(this.singleExpand){
11823        this.on("beforeexpand", this.restrictExpand, this);
11824     }
11825     if (this.editor) {
11826         this.editor.tree = this;
11827         this.editor = Roo.factory(this.editor, Roo.tree);
11828     }
11829     
11830     if (this.selModel) {
11831         this.selModel = Roo.factory(this.selModel, Roo.tree);
11832     }
11833    
11834 };
11835 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11836     rootVisible : true,
11837     animate: Roo.enableFx,
11838     lines : true,
11839     enableDD : false,
11840     hlDrop : Roo.enableFx,
11841   
11842     renderer: false,
11843     
11844     rendererTip: false,
11845     // private
11846     restrictExpand : function(node){
11847         var p = node.parentNode;
11848         if(p){
11849             if(p.expandedChild && p.expandedChild.parentNode == p){
11850                 p.expandedChild.collapse();
11851             }
11852             p.expandedChild = node;
11853         }
11854     },
11855
11856     // private override
11857     setRootNode : function(node){
11858         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11859         if(!this.rootVisible){
11860             node.ui = new Roo.tree.RootTreeNodeUI(node);
11861         }
11862         return node;
11863     },
11864
11865     /**
11866      * Returns the container element for this TreePanel
11867      */
11868     getEl : function(){
11869         return this.el;
11870     },
11871
11872     /**
11873      * Returns the default TreeLoader for this TreePanel
11874      */
11875     getLoader : function(){
11876         return this.loader;
11877     },
11878
11879     /**
11880      * Expand all nodes
11881      */
11882     expandAll : function(){
11883         this.root.expand(true);
11884     },
11885
11886     /**
11887      * Collapse all nodes
11888      */
11889     collapseAll : function(){
11890         this.root.collapse(true);
11891     },
11892
11893     /**
11894      * Returns the selection model used by this TreePanel
11895      */
11896     getSelectionModel : function(){
11897         if(!this.selModel){
11898             this.selModel = new Roo.tree.DefaultSelectionModel();
11899         }
11900         return this.selModel;
11901     },
11902
11903     /**
11904      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11905      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11906      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11907      * @return {Array}
11908      */
11909     getChecked : function(a, startNode){
11910         startNode = startNode || this.root;
11911         var r = [];
11912         var f = function(){
11913             if(this.attributes.checked){
11914                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11915             }
11916         }
11917         startNode.cascade(f);
11918         return r;
11919     },
11920
11921     /**
11922      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11923      * @param {String} path
11924      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11925      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11926      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11927      */
11928     expandPath : function(path, attr, callback){
11929         attr = attr || "id";
11930         var keys = path.split(this.pathSeparator);
11931         var curNode = this.root;
11932         if(curNode.attributes[attr] != keys[1]){ // invalid root
11933             if(callback){
11934                 callback(false, null);
11935             }
11936             return;
11937         }
11938         var index = 1;
11939         var f = function(){
11940             if(++index == keys.length){
11941                 if(callback){
11942                     callback(true, curNode);
11943                 }
11944                 return;
11945             }
11946             var c = curNode.findChild(attr, keys[index]);
11947             if(!c){
11948                 if(callback){
11949                     callback(false, curNode);
11950                 }
11951                 return;
11952             }
11953             curNode = c;
11954             c.expand(false, false, f);
11955         };
11956         curNode.expand(false, false, f);
11957     },
11958
11959     /**
11960      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11961      * @param {String} path
11962      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11963      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11964      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11965      */
11966     selectPath : function(path, attr, callback){
11967         attr = attr || "id";
11968         var keys = path.split(this.pathSeparator);
11969         var v = keys.pop();
11970         if(keys.length > 0){
11971             var f = function(success, node){
11972                 if(success && node){
11973                     var n = node.findChild(attr, v);
11974                     if(n){
11975                         n.select();
11976                         if(callback){
11977                             callback(true, n);
11978                         }
11979                     }else if(callback){
11980                         callback(false, n);
11981                     }
11982                 }else{
11983                     if(callback){
11984                         callback(false, n);
11985                     }
11986                 }
11987             };
11988             this.expandPath(keys.join(this.pathSeparator), attr, f);
11989         }else{
11990             this.root.select();
11991             if(callback){
11992                 callback(true, this.root);
11993             }
11994         }
11995     },
11996
11997     getTreeEl : function(){
11998         return this.el;
11999     },
12000
12001     /**
12002      * Trigger rendering of this TreePanel
12003      */
12004     render : function(){
12005         if (this.innerCt) {
12006             return this; // stop it rendering more than once!!
12007         }
12008         
12009         this.innerCt = this.el.createChild({tag:"ul",
12010                cls:"x-tree-root-ct " +
12011                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12012
12013         if(this.containerScroll){
12014             Roo.dd.ScrollManager.register(this.el);
12015         }
12016         if((this.enableDD || this.enableDrop) && !this.dropZone){
12017            /**
12018             * The dropZone used by this tree if drop is enabled
12019             * @type Roo.tree.TreeDropZone
12020             */
12021              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12022                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12023            });
12024         }
12025         if((this.enableDD || this.enableDrag) && !this.dragZone){
12026            /**
12027             * The dragZone used by this tree if drag is enabled
12028             * @type Roo.tree.TreeDragZone
12029             */
12030             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12031                ddGroup: this.ddGroup || "TreeDD",
12032                scroll: this.ddScroll
12033            });
12034         }
12035         this.getSelectionModel().init(this);
12036         if (!this.root) {
12037             Roo.log("ROOT not set in tree");
12038             return this;
12039         }
12040         this.root.render();
12041         if(!this.rootVisible){
12042             this.root.renderChildren();
12043         }
12044         return this;
12045     }
12046 });/*
12047  * Based on:
12048  * Ext JS Library 1.1.1
12049  * Copyright(c) 2006-2007, Ext JS, LLC.
12050  *
12051  * Originally Released Under LGPL - original licence link has changed is not relivant.
12052  *
12053  * Fork - LGPL
12054  * <script type="text/javascript">
12055  */
12056  
12057
12058 /**
12059  * @class Roo.tree.DefaultSelectionModel
12060  * @extends Roo.util.Observable
12061  * The default single selection for a TreePanel.
12062  * @param {Object} cfg Configuration
12063  */
12064 Roo.tree.DefaultSelectionModel = function(cfg){
12065    this.selNode = null;
12066    
12067    
12068    
12069    this.addEvents({
12070        /**
12071         * @event selectionchange
12072         * Fires when the selected node changes
12073         * @param {DefaultSelectionModel} this
12074         * @param {TreeNode} node the new selection
12075         */
12076        "selectionchange" : true,
12077
12078        /**
12079         * @event beforeselect
12080         * Fires before the selected node changes, return false to cancel the change
12081         * @param {DefaultSelectionModel} this
12082         * @param {TreeNode} node the new selection
12083         * @param {TreeNode} node the old selection
12084         */
12085        "beforeselect" : true
12086    });
12087    
12088     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12089 };
12090
12091 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12092     init : function(tree){
12093         this.tree = tree;
12094         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12095         tree.on("click", this.onNodeClick, this);
12096     },
12097     
12098     onNodeClick : function(node, e){
12099         if (e.ctrlKey && this.selNode == node)  {
12100             this.unselect(node);
12101             return;
12102         }
12103         this.select(node);
12104     },
12105     
12106     /**
12107      * Select a node.
12108      * @param {TreeNode} node The node to select
12109      * @return {TreeNode} The selected node
12110      */
12111     select : function(node){
12112         var last = this.selNode;
12113         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12114             if(last){
12115                 last.ui.onSelectedChange(false);
12116             }
12117             this.selNode = node;
12118             node.ui.onSelectedChange(true);
12119             this.fireEvent("selectionchange", this, node, last);
12120         }
12121         return node;
12122     },
12123     
12124     /**
12125      * Deselect a node.
12126      * @param {TreeNode} node The node to unselect
12127      */
12128     unselect : function(node){
12129         if(this.selNode == node){
12130             this.clearSelections();
12131         }    
12132     },
12133     
12134     /**
12135      * Clear all selections
12136      */
12137     clearSelections : function(){
12138         var n = this.selNode;
12139         if(n){
12140             n.ui.onSelectedChange(false);
12141             this.selNode = null;
12142             this.fireEvent("selectionchange", this, null);
12143         }
12144         return n;
12145     },
12146     
12147     /**
12148      * Get the selected node
12149      * @return {TreeNode} The selected node
12150      */
12151     getSelectedNode : function(){
12152         return this.selNode;    
12153     },
12154     
12155     /**
12156      * Returns true if the node is selected
12157      * @param {TreeNode} node The node to check
12158      * @return {Boolean}
12159      */
12160     isSelected : function(node){
12161         return this.selNode == node;  
12162     },
12163
12164     /**
12165      * Selects the node above the selected node in the tree, intelligently walking the nodes
12166      * @return TreeNode The new selection
12167      */
12168     selectPrevious : function(){
12169         var s = this.selNode || this.lastSelNode;
12170         if(!s){
12171             return null;
12172         }
12173         var ps = s.previousSibling;
12174         if(ps){
12175             if(!ps.isExpanded() || ps.childNodes.length < 1){
12176                 return this.select(ps);
12177             } else{
12178                 var lc = ps.lastChild;
12179                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12180                     lc = lc.lastChild;
12181                 }
12182                 return this.select(lc);
12183             }
12184         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12185             return this.select(s.parentNode);
12186         }
12187         return null;
12188     },
12189
12190     /**
12191      * Selects the node above the selected node in the tree, intelligently walking the nodes
12192      * @return TreeNode The new selection
12193      */
12194     selectNext : function(){
12195         var s = this.selNode || this.lastSelNode;
12196         if(!s){
12197             return null;
12198         }
12199         if(s.firstChild && s.isExpanded()){
12200              return this.select(s.firstChild);
12201          }else if(s.nextSibling){
12202              return this.select(s.nextSibling);
12203          }else if(s.parentNode){
12204             var newS = null;
12205             s.parentNode.bubble(function(){
12206                 if(this.nextSibling){
12207                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12208                     return false;
12209                 }
12210             });
12211             return newS;
12212          }
12213         return null;
12214     },
12215
12216     onKeyDown : function(e){
12217         var s = this.selNode || this.lastSelNode;
12218         // undesirable, but required
12219         var sm = this;
12220         if(!s){
12221             return;
12222         }
12223         var k = e.getKey();
12224         switch(k){
12225              case e.DOWN:
12226                  e.stopEvent();
12227                  this.selectNext();
12228              break;
12229              case e.UP:
12230                  e.stopEvent();
12231                  this.selectPrevious();
12232              break;
12233              case e.RIGHT:
12234                  e.preventDefault();
12235                  if(s.hasChildNodes()){
12236                      if(!s.isExpanded()){
12237                          s.expand();
12238                      }else if(s.firstChild){
12239                          this.select(s.firstChild, e);
12240                      }
12241                  }
12242              break;
12243              case e.LEFT:
12244                  e.preventDefault();
12245                  if(s.hasChildNodes() && s.isExpanded()){
12246                      s.collapse();
12247                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12248                      this.select(s.parentNode, e);
12249                  }
12250              break;
12251         };
12252     }
12253 });
12254
12255 /**
12256  * @class Roo.tree.MultiSelectionModel
12257  * @extends Roo.util.Observable
12258  * Multi selection for a TreePanel.
12259  * @param {Object} cfg Configuration
12260  */
12261 Roo.tree.MultiSelectionModel = function(){
12262    this.selNodes = [];
12263    this.selMap = {};
12264    this.addEvents({
12265        /**
12266         * @event selectionchange
12267         * Fires when the selected nodes change
12268         * @param {MultiSelectionModel} this
12269         * @param {Array} nodes Array of the selected nodes
12270         */
12271        "selectionchange" : true
12272    });
12273    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12274    
12275 };
12276
12277 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12278     init : function(tree){
12279         this.tree = tree;
12280         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12281         tree.on("click", this.onNodeClick, this);
12282     },
12283     
12284     onNodeClick : function(node, e){
12285         this.select(node, e, e.ctrlKey);
12286     },
12287     
12288     /**
12289      * Select a node.
12290      * @param {TreeNode} node The node to select
12291      * @param {EventObject} e (optional) An event associated with the selection
12292      * @param {Boolean} keepExisting True to retain existing selections
12293      * @return {TreeNode} The selected node
12294      */
12295     select : function(node, e, keepExisting){
12296         if(keepExisting !== true){
12297             this.clearSelections(true);
12298         }
12299         if(this.isSelected(node)){
12300             this.lastSelNode = node;
12301             return node;
12302         }
12303         this.selNodes.push(node);
12304         this.selMap[node.id] = node;
12305         this.lastSelNode = node;
12306         node.ui.onSelectedChange(true);
12307         this.fireEvent("selectionchange", this, this.selNodes);
12308         return node;
12309     },
12310     
12311     /**
12312      * Deselect a node.
12313      * @param {TreeNode} node The node to unselect
12314      */
12315     unselect : function(node){
12316         if(this.selMap[node.id]){
12317             node.ui.onSelectedChange(false);
12318             var sn = this.selNodes;
12319             var index = -1;
12320             if(sn.indexOf){
12321                 index = sn.indexOf(node);
12322             }else{
12323                 for(var i = 0, len = sn.length; i < len; i++){
12324                     if(sn[i] == node){
12325                         index = i;
12326                         break;
12327                     }
12328                 }
12329             }
12330             if(index != -1){
12331                 this.selNodes.splice(index, 1);
12332             }
12333             delete this.selMap[node.id];
12334             this.fireEvent("selectionchange", this, this.selNodes);
12335         }
12336     },
12337     
12338     /**
12339      * Clear all selections
12340      */
12341     clearSelections : function(suppressEvent){
12342         var sn = this.selNodes;
12343         if(sn.length > 0){
12344             for(var i = 0, len = sn.length; i < len; i++){
12345                 sn[i].ui.onSelectedChange(false);
12346             }
12347             this.selNodes = [];
12348             this.selMap = {};
12349             if(suppressEvent !== true){
12350                 this.fireEvent("selectionchange", this, this.selNodes);
12351             }
12352         }
12353     },
12354     
12355     /**
12356      * Returns true if the node is selected
12357      * @param {TreeNode} node The node to check
12358      * @return {Boolean}
12359      */
12360     isSelected : function(node){
12361         return this.selMap[node.id] ? true : false;  
12362     },
12363     
12364     /**
12365      * Returns an array of the selected nodes
12366      * @return {Array}
12367      */
12368     getSelectedNodes : function(){
12369         return this.selNodes;    
12370     },
12371
12372     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12373
12374     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12375
12376     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12377 });/*
12378  * Based on:
12379  * Ext JS Library 1.1.1
12380  * Copyright(c) 2006-2007, Ext JS, LLC.
12381  *
12382  * Originally Released Under LGPL - original licence link has changed is not relivant.
12383  *
12384  * Fork - LGPL
12385  * <script type="text/javascript">
12386  */
12387  
12388 /**
12389  * @class Roo.tree.TreeNode
12390  * @extends Roo.data.Node
12391  * @cfg {String} text The text for this node
12392  * @cfg {Boolean} expanded true to start the node expanded
12393  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12394  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12395  * @cfg {Boolean} disabled true to start the node disabled
12396  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12397  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
12398  * @cfg {String} cls A css class to be added to the node
12399  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12400  * @cfg {String} href URL of the link used for the node (defaults to #)
12401  * @cfg {String} hrefTarget target frame for the link
12402  * @cfg {String} qtip An Ext QuickTip for the node
12403  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12404  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12405  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12406  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12407  * (defaults to undefined with no checkbox rendered)
12408  * @constructor
12409  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12410  */
12411 Roo.tree.TreeNode = function(attributes){
12412     attributes = attributes || {};
12413     if(typeof attributes == "string"){
12414         attributes = {text: attributes};
12415     }
12416     this.childrenRendered = false;
12417     this.rendered = false;
12418     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12419     this.expanded = attributes.expanded === true;
12420     this.isTarget = attributes.isTarget !== false;
12421     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12422     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12423
12424     /**
12425      * Read-only. The text for this node. To change it use setText().
12426      * @type String
12427      */
12428     this.text = attributes.text;
12429     /**
12430      * True if this node is disabled.
12431      * @type Boolean
12432      */
12433     this.disabled = attributes.disabled === true;
12434
12435     this.addEvents({
12436         /**
12437         * @event textchange
12438         * Fires when the text for this node is changed
12439         * @param {Node} this This node
12440         * @param {String} text The new text
12441         * @param {String} oldText The old text
12442         */
12443         "textchange" : true,
12444         /**
12445         * @event beforeexpand
12446         * Fires before this node is expanded, return false to cancel.
12447         * @param {Node} this This node
12448         * @param {Boolean} deep
12449         * @param {Boolean} anim
12450         */
12451         "beforeexpand" : true,
12452         /**
12453         * @event beforecollapse
12454         * Fires before this node is collapsed, return false to cancel.
12455         * @param {Node} this This node
12456         * @param {Boolean} deep
12457         * @param {Boolean} anim
12458         */
12459         "beforecollapse" : true,
12460         /**
12461         * @event expand
12462         * Fires when this node is expanded
12463         * @param {Node} this This node
12464         */
12465         "expand" : true,
12466         /**
12467         * @event disabledchange
12468         * Fires when the disabled status of this node changes
12469         * @param {Node} this This node
12470         * @param {Boolean} disabled
12471         */
12472         "disabledchange" : true,
12473         /**
12474         * @event collapse
12475         * Fires when this node is collapsed
12476         * @param {Node} this This node
12477         */
12478         "collapse" : true,
12479         /**
12480         * @event beforeclick
12481         * Fires before click processing. Return false to cancel the default action.
12482         * @param {Node} this This node
12483         * @param {Roo.EventObject} e The event object
12484         */
12485         "beforeclick":true,
12486         /**
12487         * @event checkchange
12488         * Fires when a node with a checkbox's checked property changes
12489         * @param {Node} this This node
12490         * @param {Boolean} checked
12491         */
12492         "checkchange":true,
12493         /**
12494         * @event click
12495         * Fires when this node is clicked
12496         * @param {Node} this This node
12497         * @param {Roo.EventObject} e The event object
12498         */
12499         "click":true,
12500         /**
12501         * @event dblclick
12502         * Fires when this node is double clicked
12503         * @param {Node} this This node
12504         * @param {Roo.EventObject} e The event object
12505         */
12506         "dblclick":true,
12507         /**
12508         * @event contextmenu
12509         * Fires when this node is right clicked
12510         * @param {Node} this This node
12511         * @param {Roo.EventObject} e The event object
12512         */
12513         "contextmenu":true,
12514         /**
12515         * @event beforechildrenrendered
12516         * Fires right before the child nodes for this node are rendered
12517         * @param {Node} this This node
12518         */
12519         "beforechildrenrendered":true
12520     });
12521
12522     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12523
12524     /**
12525      * Read-only. The UI for this node
12526      * @type TreeNodeUI
12527      */
12528     this.ui = new uiClass(this);
12529     
12530     // finally support items[]
12531     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12532         return;
12533     }
12534     
12535     
12536     Roo.each(this.attributes.items, function(c) {
12537         this.appendChild(Roo.factory(c,Roo.Tree));
12538     }, this);
12539     delete this.attributes.items;
12540     
12541     
12542     
12543 };
12544 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12545     preventHScroll: true,
12546     /**
12547      * Returns true if this node is expanded
12548      * @return {Boolean}
12549      */
12550     isExpanded : function(){
12551         return this.expanded;
12552     },
12553
12554     /**
12555      * Returns the UI object for this node
12556      * @return {TreeNodeUI}
12557      */
12558     getUI : function(){
12559         return this.ui;
12560     },
12561
12562     // private override
12563     setFirstChild : function(node){
12564         var of = this.firstChild;
12565         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12566         if(this.childrenRendered && of && node != of){
12567             of.renderIndent(true, true);
12568         }
12569         if(this.rendered){
12570             this.renderIndent(true, true);
12571         }
12572     },
12573
12574     // private override
12575     setLastChild : function(node){
12576         var ol = this.lastChild;
12577         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12578         if(this.childrenRendered && ol && node != ol){
12579             ol.renderIndent(true, true);
12580         }
12581         if(this.rendered){
12582             this.renderIndent(true, true);
12583         }
12584     },
12585
12586     // these methods are overridden to provide lazy rendering support
12587     // private override
12588     appendChild : function()
12589     {
12590         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12591         if(node && this.childrenRendered){
12592             node.render();
12593         }
12594         this.ui.updateExpandIcon();
12595         return node;
12596     },
12597
12598     // private override
12599     removeChild : function(node){
12600         this.ownerTree.getSelectionModel().unselect(node);
12601         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12602         // if it's been rendered remove dom node
12603         if(this.childrenRendered){
12604             node.ui.remove();
12605         }
12606         if(this.childNodes.length < 1){
12607             this.collapse(false, false);
12608         }else{
12609             this.ui.updateExpandIcon();
12610         }
12611         if(!this.firstChild) {
12612             this.childrenRendered = false;
12613         }
12614         return node;
12615     },
12616
12617     // private override
12618     insertBefore : function(node, refNode){
12619         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12620         if(newNode && refNode && this.childrenRendered){
12621             node.render();
12622         }
12623         this.ui.updateExpandIcon();
12624         return newNode;
12625     },
12626
12627     /**
12628      * Sets the text for this node
12629      * @param {String} text
12630      */
12631     setText : function(text){
12632         var oldText = this.text;
12633         this.text = text;
12634         this.attributes.text = text;
12635         if(this.rendered){ // event without subscribing
12636             this.ui.onTextChange(this, text, oldText);
12637         }
12638         this.fireEvent("textchange", this, text, oldText);
12639     },
12640
12641     /**
12642      * Triggers selection of this node
12643      */
12644     select : function(){
12645         this.getOwnerTree().getSelectionModel().select(this);
12646     },
12647
12648     /**
12649      * Triggers deselection of this node
12650      */
12651     unselect : function(){
12652         this.getOwnerTree().getSelectionModel().unselect(this);
12653     },
12654
12655     /**
12656      * Returns true if this node is selected
12657      * @return {Boolean}
12658      */
12659     isSelected : function(){
12660         return this.getOwnerTree().getSelectionModel().isSelected(this);
12661     },
12662
12663     /**
12664      * Expand this node.
12665      * @param {Boolean} deep (optional) True to expand all children as well
12666      * @param {Boolean} anim (optional) false to cancel the default animation
12667      * @param {Function} callback (optional) A callback to be called when
12668      * expanding this node completes (does not wait for deep expand to complete).
12669      * Called with 1 parameter, this node.
12670      */
12671     expand : function(deep, anim, callback){
12672         if(!this.expanded){
12673             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12674                 return;
12675             }
12676             if(!this.childrenRendered){
12677                 this.renderChildren();
12678             }
12679             this.expanded = true;
12680             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
12681                 this.ui.animExpand(function(){
12682                     this.fireEvent("expand", this);
12683                     if(typeof callback == "function"){
12684                         callback(this);
12685                     }
12686                     if(deep === true){
12687                         this.expandChildNodes(true);
12688                     }
12689                 }.createDelegate(this));
12690                 return;
12691             }else{
12692                 this.ui.expand();
12693                 this.fireEvent("expand", this);
12694                 if(typeof callback == "function"){
12695                     callback(this);
12696                 }
12697             }
12698         }else{
12699            if(typeof callback == "function"){
12700                callback(this);
12701            }
12702         }
12703         if(deep === true){
12704             this.expandChildNodes(true);
12705         }
12706     },
12707
12708     isHiddenRoot : function(){
12709         return this.isRoot && !this.getOwnerTree().rootVisible;
12710     },
12711
12712     /**
12713      * Collapse this node.
12714      * @param {Boolean} deep (optional) True to collapse all children as well
12715      * @param {Boolean} anim (optional) false to cancel the default animation
12716      */
12717     collapse : function(deep, anim){
12718         if(this.expanded && !this.isHiddenRoot()){
12719             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12720                 return;
12721             }
12722             this.expanded = false;
12723             if((this.getOwnerTree().animate && anim !== false) || anim){
12724                 this.ui.animCollapse(function(){
12725                     this.fireEvent("collapse", this);
12726                     if(deep === true){
12727                         this.collapseChildNodes(true);
12728                     }
12729                 }.createDelegate(this));
12730                 return;
12731             }else{
12732                 this.ui.collapse();
12733                 this.fireEvent("collapse", this);
12734             }
12735         }
12736         if(deep === true){
12737             var cs = this.childNodes;
12738             for(var i = 0, len = cs.length; i < len; i++) {
12739                 cs[i].collapse(true, false);
12740             }
12741         }
12742     },
12743
12744     // private
12745     delayedExpand : function(delay){
12746         if(!this.expandProcId){
12747             this.expandProcId = this.expand.defer(delay, this);
12748         }
12749     },
12750
12751     // private
12752     cancelExpand : function(){
12753         if(this.expandProcId){
12754             clearTimeout(this.expandProcId);
12755         }
12756         this.expandProcId = false;
12757     },
12758
12759     /**
12760      * Toggles expanded/collapsed state of the node
12761      */
12762     toggle : function(){
12763         if(this.expanded){
12764             this.collapse();
12765         }else{
12766             this.expand();
12767         }
12768     },
12769
12770     /**
12771      * Ensures all parent nodes are expanded
12772      */
12773     ensureVisible : function(callback){
12774         var tree = this.getOwnerTree();
12775         tree.expandPath(this.parentNode.getPath(), false, function(){
12776             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12777             Roo.callback(callback);
12778         }.createDelegate(this));
12779     },
12780
12781     /**
12782      * Expand all child nodes
12783      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12784      */
12785     expandChildNodes : function(deep){
12786         var cs = this.childNodes;
12787         for(var i = 0, len = cs.length; i < len; i++) {
12788                 cs[i].expand(deep);
12789         }
12790     },
12791
12792     /**
12793      * Collapse all child nodes
12794      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12795      */
12796     collapseChildNodes : function(deep){
12797         var cs = this.childNodes;
12798         for(var i = 0, len = cs.length; i < len; i++) {
12799                 cs[i].collapse(deep);
12800         }
12801     },
12802
12803     /**
12804      * Disables this node
12805      */
12806     disable : function(){
12807         this.disabled = true;
12808         this.unselect();
12809         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12810             this.ui.onDisableChange(this, true);
12811         }
12812         this.fireEvent("disabledchange", this, true);
12813     },
12814
12815     /**
12816      * Enables this node
12817      */
12818     enable : function(){
12819         this.disabled = false;
12820         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12821             this.ui.onDisableChange(this, false);
12822         }
12823         this.fireEvent("disabledchange", this, false);
12824     },
12825
12826     // private
12827     renderChildren : function(suppressEvent){
12828         if(suppressEvent !== false){
12829             this.fireEvent("beforechildrenrendered", this);
12830         }
12831         var cs = this.childNodes;
12832         for(var i = 0, len = cs.length; i < len; i++){
12833             cs[i].render(true);
12834         }
12835         this.childrenRendered = true;
12836     },
12837
12838     // private
12839     sort : function(fn, scope){
12840         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12841         if(this.childrenRendered){
12842             var cs = this.childNodes;
12843             for(var i = 0, len = cs.length; i < len; i++){
12844                 cs[i].render(true);
12845             }
12846         }
12847     },
12848
12849     // private
12850     render : function(bulkRender){
12851         this.ui.render(bulkRender);
12852         if(!this.rendered){
12853             this.rendered = true;
12854             if(this.expanded){
12855                 this.expanded = false;
12856                 this.expand(false, false);
12857             }
12858         }
12859     },
12860
12861     // private
12862     renderIndent : function(deep, refresh){
12863         if(refresh){
12864             this.ui.childIndent = null;
12865         }
12866         this.ui.renderIndent();
12867         if(deep === true && this.childrenRendered){
12868             var cs = this.childNodes;
12869             for(var i = 0, len = cs.length; i < len; i++){
12870                 cs[i].renderIndent(true, refresh);
12871             }
12872         }
12873     }
12874 });/*
12875  * Based on:
12876  * Ext JS Library 1.1.1
12877  * Copyright(c) 2006-2007, Ext JS, LLC.
12878  *
12879  * Originally Released Under LGPL - original licence link has changed is not relivant.
12880  *
12881  * Fork - LGPL
12882  * <script type="text/javascript">
12883  */
12884  
12885 /**
12886  * @class Roo.tree.AsyncTreeNode
12887  * @extends Roo.tree.TreeNode
12888  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12889  * @constructor
12890  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12891  */
12892  Roo.tree.AsyncTreeNode = function(config){
12893     this.loaded = false;
12894     this.loading = false;
12895     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12896     /**
12897     * @event beforeload
12898     * Fires before this node is loaded, return false to cancel
12899     * @param {Node} this This node
12900     */
12901     this.addEvents({'beforeload':true, 'load': true});
12902     /**
12903     * @event load
12904     * Fires when this node is loaded
12905     * @param {Node} this This node
12906     */
12907     /**
12908      * The loader used by this node (defaults to using the tree's defined loader)
12909      * @type TreeLoader
12910      * @property loader
12911      */
12912 };
12913 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12914     expand : function(deep, anim, callback){
12915         if(this.loading){ // if an async load is already running, waiting til it's done
12916             var timer;
12917             var f = function(){
12918                 if(!this.loading){ // done loading
12919                     clearInterval(timer);
12920                     this.expand(deep, anim, callback);
12921                 }
12922             }.createDelegate(this);
12923             timer = setInterval(f, 200);
12924             return;
12925         }
12926         if(!this.loaded){
12927             if(this.fireEvent("beforeload", this) === false){
12928                 return;
12929             }
12930             this.loading = true;
12931             this.ui.beforeLoad(this);
12932             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12933             if(loader){
12934                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12935                 return;
12936             }
12937         }
12938         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12939     },
12940     
12941     /**
12942      * Returns true if this node is currently loading
12943      * @return {Boolean}
12944      */
12945     isLoading : function(){
12946         return this.loading;  
12947     },
12948     
12949     loadComplete : function(deep, anim, callback){
12950         this.loading = false;
12951         this.loaded = true;
12952         this.ui.afterLoad(this);
12953         this.fireEvent("load", this);
12954         this.expand(deep, anim, callback);
12955     },
12956     
12957     /**
12958      * Returns true if this node has been loaded
12959      * @return {Boolean}
12960      */
12961     isLoaded : function(){
12962         return this.loaded;
12963     },
12964     
12965     hasChildNodes : function(){
12966         if(!this.isLeaf() && !this.loaded){
12967             return true;
12968         }else{
12969             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12970         }
12971     },
12972
12973     /**
12974      * Trigger a reload for this node
12975      * @param {Function} callback
12976      */
12977     reload : function(callback){
12978         this.collapse(false, false);
12979         while(this.firstChild){
12980             this.removeChild(this.firstChild);
12981         }
12982         this.childrenRendered = false;
12983         this.loaded = false;
12984         if(this.isHiddenRoot()){
12985             this.expanded = false;
12986         }
12987         this.expand(false, false, callback);
12988     }
12989 });/*
12990  * Based on:
12991  * Ext JS Library 1.1.1
12992  * Copyright(c) 2006-2007, Ext JS, LLC.
12993  *
12994  * Originally Released Under LGPL - original licence link has changed is not relivant.
12995  *
12996  * Fork - LGPL
12997  * <script type="text/javascript">
12998  */
12999  
13000 /**
13001  * @class Roo.tree.TreeNodeUI
13002  * @constructor
13003  * @param {Object} node The node to render
13004  * The TreeNode UI implementation is separate from the
13005  * tree implementation. Unless you are customizing the tree UI,
13006  * you should never have to use this directly.
13007  */
13008 Roo.tree.TreeNodeUI = function(node){
13009     this.node = node;
13010     this.rendered = false;
13011     this.animating = false;
13012     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13013 };
13014
13015 Roo.tree.TreeNodeUI.prototype = {
13016     removeChild : function(node){
13017         if(this.rendered){
13018             this.ctNode.removeChild(node.ui.getEl());
13019         }
13020     },
13021
13022     beforeLoad : function(){
13023          this.addClass("x-tree-node-loading");
13024     },
13025
13026     afterLoad : function(){
13027          this.removeClass("x-tree-node-loading");
13028     },
13029
13030     onTextChange : function(node, text, oldText){
13031         if(this.rendered){
13032             this.textNode.innerHTML = text;
13033         }
13034     },
13035
13036     onDisableChange : function(node, state){
13037         this.disabled = state;
13038         if(state){
13039             this.addClass("x-tree-node-disabled");
13040         }else{
13041             this.removeClass("x-tree-node-disabled");
13042         }
13043     },
13044
13045     onSelectedChange : function(state){
13046         if(state){
13047             this.focus();
13048             this.addClass("x-tree-selected");
13049         }else{
13050             //this.blur();
13051             this.removeClass("x-tree-selected");
13052         }
13053     },
13054
13055     onMove : function(tree, node, oldParent, newParent, index, refNode){
13056         this.childIndent = null;
13057         if(this.rendered){
13058             var targetNode = newParent.ui.getContainer();
13059             if(!targetNode){//target not rendered
13060                 this.holder = document.createElement("div");
13061                 this.holder.appendChild(this.wrap);
13062                 return;
13063             }
13064             var insertBefore = refNode ? refNode.ui.getEl() : null;
13065             if(insertBefore){
13066                 targetNode.insertBefore(this.wrap, insertBefore);
13067             }else{
13068                 targetNode.appendChild(this.wrap);
13069             }
13070             this.node.renderIndent(true);
13071         }
13072     },
13073
13074     addClass : function(cls){
13075         if(this.elNode){
13076             Roo.fly(this.elNode).addClass(cls);
13077         }
13078     },
13079
13080     removeClass : function(cls){
13081         if(this.elNode){
13082             Roo.fly(this.elNode).removeClass(cls);
13083         }
13084     },
13085
13086     remove : function(){
13087         if(this.rendered){
13088             this.holder = document.createElement("div");
13089             this.holder.appendChild(this.wrap);
13090         }
13091     },
13092
13093     fireEvent : function(){
13094         return this.node.fireEvent.apply(this.node, arguments);
13095     },
13096
13097     initEvents : function(){
13098         this.node.on("move", this.onMove, this);
13099         var E = Roo.EventManager;
13100         var a = this.anchor;
13101
13102         var el = Roo.fly(a, '_treeui');
13103
13104         if(Roo.isOpera){ // opera render bug ignores the CSS
13105             el.setStyle("text-decoration", "none");
13106         }
13107
13108         el.on("click", this.onClick, this);
13109         el.on("dblclick", this.onDblClick, this);
13110
13111         if(this.checkbox){
13112             Roo.EventManager.on(this.checkbox,
13113                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13114         }
13115
13116         el.on("contextmenu", this.onContextMenu, this);
13117
13118         var icon = Roo.fly(this.iconNode);
13119         icon.on("click", this.onClick, this);
13120         icon.on("dblclick", this.onDblClick, this);
13121         icon.on("contextmenu", this.onContextMenu, this);
13122         E.on(this.ecNode, "click", this.ecClick, this, true);
13123
13124         if(this.node.disabled){
13125             this.addClass("x-tree-node-disabled");
13126         }
13127         if(this.node.hidden){
13128             this.addClass("x-tree-node-disabled");
13129         }
13130         var ot = this.node.getOwnerTree();
13131         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
13132         if(dd && (!this.node.isRoot || ot.rootVisible)){
13133             Roo.dd.Registry.register(this.elNode, {
13134                 node: this.node,
13135                 handles: this.getDDHandles(),
13136                 isHandle: false
13137             });
13138         }
13139     },
13140
13141     getDDHandles : function(){
13142         return [this.iconNode, this.textNode];
13143     },
13144
13145     hide : function(){
13146         if(this.rendered){
13147             this.wrap.style.display = "none";
13148         }
13149     },
13150
13151     show : function(){
13152         if(this.rendered){
13153             this.wrap.style.display = "";
13154         }
13155     },
13156
13157     onContextMenu : function(e){
13158         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13159             e.preventDefault();
13160             this.focus();
13161             this.fireEvent("contextmenu", this.node, e);
13162         }
13163     },
13164
13165     onClick : function(e){
13166         if(this.dropping){
13167             e.stopEvent();
13168             return;
13169         }
13170         if(this.fireEvent("beforeclick", this.node, e) !== false){
13171             if(!this.disabled && this.node.attributes.href){
13172                 this.fireEvent("click", this.node, e);
13173                 return;
13174             }
13175             e.preventDefault();
13176             if(this.disabled){
13177                 return;
13178             }
13179
13180             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13181                 this.node.toggle();
13182             }
13183
13184             this.fireEvent("click", this.node, e);
13185         }else{
13186             e.stopEvent();
13187         }
13188     },
13189
13190     onDblClick : function(e){
13191         e.preventDefault();
13192         if(this.disabled){
13193             return;
13194         }
13195         if(this.checkbox){
13196             this.toggleCheck();
13197         }
13198         if(!this.animating && this.node.hasChildNodes()){
13199             this.node.toggle();
13200         }
13201         this.fireEvent("dblclick", this.node, e);
13202     },
13203
13204     onCheckChange : function(){
13205         var checked = this.checkbox.checked;
13206         this.node.attributes.checked = checked;
13207         this.fireEvent('checkchange', this.node, checked);
13208     },
13209
13210     ecClick : function(e){
13211         if(!this.animating && this.node.hasChildNodes()){
13212             this.node.toggle();
13213         }
13214     },
13215
13216     startDrop : function(){
13217         this.dropping = true;
13218     },
13219
13220     // delayed drop so the click event doesn't get fired on a drop
13221     endDrop : function(){
13222        setTimeout(function(){
13223            this.dropping = false;
13224        }.createDelegate(this), 50);
13225     },
13226
13227     expand : function(){
13228         this.updateExpandIcon();
13229         this.ctNode.style.display = "";
13230     },
13231
13232     focus : function(){
13233         if(!this.node.preventHScroll){
13234             try{this.anchor.focus();
13235             }catch(e){}
13236         }else if(!Roo.isIE){
13237             try{
13238                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13239                 var l = noscroll.scrollLeft;
13240                 this.anchor.focus();
13241                 noscroll.scrollLeft = l;
13242             }catch(e){}
13243         }
13244     },
13245
13246     toggleCheck : function(value){
13247         var cb = this.checkbox;
13248         if(cb){
13249             cb.checked = (value === undefined ? !cb.checked : value);
13250         }
13251     },
13252
13253     blur : function(){
13254         try{
13255             this.anchor.blur();
13256         }catch(e){}
13257     },
13258
13259     animExpand : function(callback){
13260         var ct = Roo.get(this.ctNode);
13261         ct.stopFx();
13262         if(!this.node.hasChildNodes()){
13263             this.updateExpandIcon();
13264             this.ctNode.style.display = "";
13265             Roo.callback(callback);
13266             return;
13267         }
13268         this.animating = true;
13269         this.updateExpandIcon();
13270
13271         ct.slideIn('t', {
13272            callback : function(){
13273                this.animating = false;
13274                Roo.callback(callback);
13275             },
13276             scope: this,
13277             duration: this.node.ownerTree.duration || .25
13278         });
13279     },
13280
13281     highlight : function(){
13282         var tree = this.node.getOwnerTree();
13283         Roo.fly(this.wrap).highlight(
13284             tree.hlColor || "C3DAF9",
13285             {endColor: tree.hlBaseColor}
13286         );
13287     },
13288
13289     collapse : function(){
13290         this.updateExpandIcon();
13291         this.ctNode.style.display = "none";
13292     },
13293
13294     animCollapse : function(callback){
13295         var ct = Roo.get(this.ctNode);
13296         ct.enableDisplayMode('block');
13297         ct.stopFx();
13298
13299         this.animating = true;
13300         this.updateExpandIcon();
13301
13302         ct.slideOut('t', {
13303             callback : function(){
13304                this.animating = false;
13305                Roo.callback(callback);
13306             },
13307             scope: this,
13308             duration: this.node.ownerTree.duration || .25
13309         });
13310     },
13311
13312     getContainer : function(){
13313         return this.ctNode;
13314     },
13315
13316     getEl : function(){
13317         return this.wrap;
13318     },
13319
13320     appendDDGhost : function(ghostNode){
13321         ghostNode.appendChild(this.elNode.cloneNode(true));
13322     },
13323
13324     getDDRepairXY : function(){
13325         return Roo.lib.Dom.getXY(this.iconNode);
13326     },
13327
13328     onRender : function(){
13329         this.render();
13330     },
13331
13332     render : function(bulkRender){
13333         var n = this.node, a = n.attributes;
13334         var targetNode = n.parentNode ?
13335               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13336
13337         if(!this.rendered){
13338             this.rendered = true;
13339
13340             this.renderElements(n, a, targetNode, bulkRender);
13341
13342             if(a.qtip){
13343                if(this.textNode.setAttributeNS){
13344                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13345                    if(a.qtipTitle){
13346                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13347                    }
13348                }else{
13349                    this.textNode.setAttribute("ext:qtip", a.qtip);
13350                    if(a.qtipTitle){
13351                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13352                    }
13353                }
13354             }else if(a.qtipCfg){
13355                 a.qtipCfg.target = Roo.id(this.textNode);
13356                 Roo.QuickTips.register(a.qtipCfg);
13357             }
13358             this.initEvents();
13359             if(!this.node.expanded){
13360                 this.updateExpandIcon();
13361             }
13362         }else{
13363             if(bulkRender === true) {
13364                 targetNode.appendChild(this.wrap);
13365             }
13366         }
13367     },
13368
13369     renderElements : function(n, a, targetNode, bulkRender)
13370     {
13371         // add some indent caching, this helps performance when rendering a large tree
13372         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13373         var t = n.getOwnerTree();
13374         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13375         if (typeof(n.attributes.html) != 'undefined') {
13376             txt = n.attributes.html;
13377         }
13378         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
13379         var cb = typeof a.checked == 'boolean';
13380         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13381         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13382             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13383             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13384             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13385             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13386             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13387              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13388                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13389             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13390             "</li>"];
13391
13392         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13393             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13394                                 n.nextSibling.ui.getEl(), buf.join(""));
13395         }else{
13396             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13397         }
13398
13399         this.elNode = this.wrap.childNodes[0];
13400         this.ctNode = this.wrap.childNodes[1];
13401         var cs = this.elNode.childNodes;
13402         this.indentNode = cs[0];
13403         this.ecNode = cs[1];
13404         this.iconNode = cs[2];
13405         var index = 3;
13406         if(cb){
13407             this.checkbox = cs[3];
13408             index++;
13409         }
13410         this.anchor = cs[index];
13411         this.textNode = cs[index].firstChild;
13412     },
13413
13414     getAnchor : function(){
13415         return this.anchor;
13416     },
13417
13418     getTextEl : function(){
13419         return this.textNode;
13420     },
13421
13422     getIconEl : function(){
13423         return this.iconNode;
13424     },
13425
13426     isChecked : function(){
13427         return this.checkbox ? this.checkbox.checked : false;
13428     },
13429
13430     updateExpandIcon : function(){
13431         if(this.rendered){
13432             var n = this.node, c1, c2;
13433             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13434             var hasChild = n.hasChildNodes();
13435             if(hasChild){
13436                 if(n.expanded){
13437                     cls += "-minus";
13438                     c1 = "x-tree-node-collapsed";
13439                     c2 = "x-tree-node-expanded";
13440                 }else{
13441                     cls += "-plus";
13442                     c1 = "x-tree-node-expanded";
13443                     c2 = "x-tree-node-collapsed";
13444                 }
13445                 if(this.wasLeaf){
13446                     this.removeClass("x-tree-node-leaf");
13447                     this.wasLeaf = false;
13448                 }
13449                 if(this.c1 != c1 || this.c2 != c2){
13450                     Roo.fly(this.elNode).replaceClass(c1, c2);
13451                     this.c1 = c1; this.c2 = c2;
13452                 }
13453             }else{
13454                 // this changes non-leafs into leafs if they have no children.
13455                 // it's not very rational behaviour..
13456                 
13457                 if(!this.wasLeaf && this.node.leaf){
13458                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13459                     delete this.c1;
13460                     delete this.c2;
13461                     this.wasLeaf = true;
13462                 }
13463             }
13464             var ecc = "x-tree-ec-icon "+cls;
13465             if(this.ecc != ecc){
13466                 this.ecNode.className = ecc;
13467                 this.ecc = ecc;
13468             }
13469         }
13470     },
13471
13472     getChildIndent : function(){
13473         if(!this.childIndent){
13474             var buf = [];
13475             var p = this.node;
13476             while(p){
13477                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13478                     if(!p.isLast()) {
13479                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13480                     } else {
13481                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13482                     }
13483                 }
13484                 p = p.parentNode;
13485             }
13486             this.childIndent = buf.join("");
13487         }
13488         return this.childIndent;
13489     },
13490
13491     renderIndent : function(){
13492         if(this.rendered){
13493             var indent = "";
13494             var p = this.node.parentNode;
13495             if(p){
13496                 indent = p.ui.getChildIndent();
13497             }
13498             if(this.indentMarkup != indent){ // don't rerender if not required
13499                 this.indentNode.innerHTML = indent;
13500                 this.indentMarkup = indent;
13501             }
13502             this.updateExpandIcon();
13503         }
13504     }
13505 };
13506
13507 Roo.tree.RootTreeNodeUI = function(){
13508     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13509 };
13510 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13511     render : function(){
13512         if(!this.rendered){
13513             var targetNode = this.node.ownerTree.innerCt.dom;
13514             this.node.expanded = true;
13515             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13516             this.wrap = this.ctNode = targetNode.firstChild;
13517         }
13518     },
13519     collapse : function(){
13520     },
13521     expand : function(){
13522     }
13523 });/*
13524  * Based on:
13525  * Ext JS Library 1.1.1
13526  * Copyright(c) 2006-2007, Ext JS, LLC.
13527  *
13528  * Originally Released Under LGPL - original licence link has changed is not relivant.
13529  *
13530  * Fork - LGPL
13531  * <script type="text/javascript">
13532  */
13533 /**
13534  * @class Roo.tree.TreeLoader
13535  * @extends Roo.util.Observable
13536  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13537  * nodes from a specified URL. The response must be a javascript Array definition
13538  * who's elements are node definition objects. eg:
13539  * <pre><code>
13540 {  success : true,
13541    data :      [
13542    
13543     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13544     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13545     ]
13546 }
13547
13548
13549 </code></pre>
13550  * <br><br>
13551  * The old style respose with just an array is still supported, but not recommended.
13552  * <br><br>
13553  *
13554  * A server request is sent, and child nodes are loaded only when a node is expanded.
13555  * The loading node's id is passed to the server under the parameter name "node" to
13556  * enable the server to produce the correct child nodes.
13557  * <br><br>
13558  * To pass extra parameters, an event handler may be attached to the "beforeload"
13559  * event, and the parameters specified in the TreeLoader's baseParams property:
13560  * <pre><code>
13561     myTreeLoader.on("beforeload", function(treeLoader, node) {
13562         this.baseParams.category = node.attributes.category;
13563     }, this);
13564 </code></pre><
13565  * This would pass an HTTP parameter called "category" to the server containing
13566  * the value of the Node's "category" attribute.
13567  * @constructor
13568  * Creates a new Treeloader.
13569  * @param {Object} config A config object containing config properties.
13570  */
13571 Roo.tree.TreeLoader = function(config){
13572     this.baseParams = {};
13573     this.requestMethod = "POST";
13574     Roo.apply(this, config);
13575
13576     this.addEvents({
13577     
13578         /**
13579          * @event beforeload
13580          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13581          * @param {Object} This TreeLoader object.
13582          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13583          * @param {Object} callback The callback function specified in the {@link #load} call.
13584          */
13585         beforeload : true,
13586         /**
13587          * @event load
13588          * Fires when the node has been successfuly loaded.
13589          * @param {Object} This TreeLoader object.
13590          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13591          * @param {Object} response The response object containing the data from the server.
13592          */
13593         load : true,
13594         /**
13595          * @event loadexception
13596          * Fires if the network request failed.
13597          * @param {Object} This TreeLoader object.
13598          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13599          * @param {Object} response The response object containing the data from the server.
13600          */
13601         loadexception : true,
13602         /**
13603          * @event create
13604          * Fires before a node is created, enabling you to return custom Node types 
13605          * @param {Object} This TreeLoader object.
13606          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13607          */
13608         create : true
13609     });
13610
13611     Roo.tree.TreeLoader.superclass.constructor.call(this);
13612 };
13613
13614 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13615     /**
13616     * @cfg {String} dataUrl The URL from which to request a Json string which
13617     * specifies an array of node definition object representing the child nodes
13618     * to be loaded.
13619     */
13620     /**
13621     * @cfg {String} requestMethod either GET or POST
13622     * defaults to POST (due to BC)
13623     * to be loaded.
13624     */
13625     /**
13626     * @cfg {Object} baseParams (optional) An object containing properties which
13627     * specify HTTP parameters to be passed to each request for child nodes.
13628     */
13629     /**
13630     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13631     * created by this loader. If the attributes sent by the server have an attribute in this object,
13632     * they take priority.
13633     */
13634     /**
13635     * @cfg {Object} uiProviders (optional) An object containing properties which
13636     * 
13637     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13638     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13639     * <i>uiProvider</i> attribute of a returned child node is a string rather
13640     * than a reference to a TreeNodeUI implementation, this that string value
13641     * is used as a property name in the uiProviders object. You can define the provider named
13642     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13643     */
13644     uiProviders : {},
13645
13646     /**
13647     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13648     * child nodes before loading.
13649     */
13650     clearOnLoad : true,
13651
13652     /**
13653     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13654     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13655     * Grid query { data : [ .....] }
13656     */
13657     
13658     root : false,
13659      /**
13660     * @cfg {String} queryParam (optional) 
13661     * Name of the query as it will be passed on the querystring (defaults to 'node')
13662     * eg. the request will be ?node=[id]
13663     */
13664     
13665     
13666     queryParam: false,
13667     
13668     /**
13669      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13670      * This is called automatically when a node is expanded, but may be used to reload
13671      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13672      * @param {Roo.tree.TreeNode} node
13673      * @param {Function} callback
13674      */
13675     load : function(node, callback){
13676         if(this.clearOnLoad){
13677             while(node.firstChild){
13678                 node.removeChild(node.firstChild);
13679             }
13680         }
13681         if(node.attributes.children){ // preloaded json children
13682             var cs = node.attributes.children;
13683             for(var i = 0, len = cs.length; i < len; i++){
13684                 node.appendChild(this.createNode(cs[i]));
13685             }
13686             if(typeof callback == "function"){
13687                 callback();
13688             }
13689         }else if(this.dataUrl){
13690             this.requestData(node, callback);
13691         }
13692     },
13693
13694     getParams: function(node){
13695         var buf = [], bp = this.baseParams;
13696         for(var key in bp){
13697             if(typeof bp[key] != "function"){
13698                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13699             }
13700         }
13701         var n = this.queryParam === false ? 'node' : this.queryParam;
13702         buf.push(n + "=", encodeURIComponent(node.id));
13703         return buf.join("");
13704     },
13705
13706     requestData : function(node, callback){
13707         if(this.fireEvent("beforeload", this, node, callback) !== false){
13708             this.transId = Roo.Ajax.request({
13709                 method:this.requestMethod,
13710                 url: this.dataUrl||this.url,
13711                 success: this.handleResponse,
13712                 failure: this.handleFailure,
13713                 scope: this,
13714                 argument: {callback: callback, node: node},
13715                 params: this.getParams(node)
13716             });
13717         }else{
13718             // if the load is cancelled, make sure we notify
13719             // the node that we are done
13720             if(typeof callback == "function"){
13721                 callback();
13722             }
13723         }
13724     },
13725
13726     isLoading : function(){
13727         return this.transId ? true : false;
13728     },
13729
13730     abort : function(){
13731         if(this.isLoading()){
13732             Roo.Ajax.abort(this.transId);
13733         }
13734     },
13735
13736     // private
13737     createNode : function(attr)
13738     {
13739         // apply baseAttrs, nice idea Corey!
13740         if(this.baseAttrs){
13741             Roo.applyIf(attr, this.baseAttrs);
13742         }
13743         if(this.applyLoader !== false){
13744             attr.loader = this;
13745         }
13746         // uiProvider = depreciated..
13747         
13748         if(typeof(attr.uiProvider) == 'string'){
13749            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13750                 /**  eval:var:attr */ eval(attr.uiProvider);
13751         }
13752         if(typeof(this.uiProviders['default']) != 'undefined') {
13753             attr.uiProvider = this.uiProviders['default'];
13754         }
13755         
13756         this.fireEvent('create', this, attr);
13757         
13758         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13759         return(attr.leaf ?
13760                         new Roo.tree.TreeNode(attr) :
13761                         new Roo.tree.AsyncTreeNode(attr));
13762     },
13763
13764     processResponse : function(response, node, callback)
13765     {
13766         var json = response.responseText;
13767         try {
13768             
13769             var o = Roo.decode(json);
13770             
13771             if (this.root === false && typeof(o.success) != undefined) {
13772                 this.root = 'data'; // the default behaviour for list like data..
13773                 }
13774                 
13775             if (this.root !== false &&  !o.success) {
13776                 // it's a failure condition.
13777                 var a = response.argument;
13778                 this.fireEvent("loadexception", this, a.node, response);
13779                 Roo.log("Load failed - should have a handler really");
13780                 return;
13781             }
13782             
13783             
13784             
13785             if (this.root !== false) {
13786                  o = o[this.root];
13787             }
13788             
13789             for(var i = 0, len = o.length; i < len; i++){
13790                 var n = this.createNode(o[i]);
13791                 if(n){
13792                     node.appendChild(n);
13793                 }
13794             }
13795             if(typeof callback == "function"){
13796                 callback(this, node);
13797             }
13798         }catch(e){
13799             this.handleFailure(response);
13800         }
13801     },
13802
13803     handleResponse : function(response){
13804         this.transId = false;
13805         var a = response.argument;
13806         this.processResponse(response, a.node, a.callback);
13807         this.fireEvent("load", this, a.node, response);
13808     },
13809
13810     handleFailure : function(response)
13811     {
13812         // should handle failure better..
13813         this.transId = false;
13814         var a = response.argument;
13815         this.fireEvent("loadexception", this, a.node, response);
13816         if(typeof a.callback == "function"){
13817             a.callback(this, a.node);
13818         }
13819     }
13820 });/*
13821  * Based on:
13822  * Ext JS Library 1.1.1
13823  * Copyright(c) 2006-2007, Ext JS, LLC.
13824  *
13825  * Originally Released Under LGPL - original licence link has changed is not relivant.
13826  *
13827  * Fork - LGPL
13828  * <script type="text/javascript">
13829  */
13830
13831 /**
13832 * @class Roo.tree.TreeFilter
13833 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13834 * @param {TreePanel} tree
13835 * @param {Object} config (optional)
13836  */
13837 Roo.tree.TreeFilter = function(tree, config){
13838     this.tree = tree;
13839     this.filtered = {};
13840     Roo.apply(this, config);
13841 };
13842
13843 Roo.tree.TreeFilter.prototype = {
13844     clearBlank:false,
13845     reverse:false,
13846     autoClear:false,
13847     remove:false,
13848
13849      /**
13850      * Filter the data by a specific attribute.
13851      * @param {String/RegExp} value Either string that the attribute value
13852      * should start with or a RegExp to test against the attribute
13853      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13854      * @param {TreeNode} startNode (optional) The node to start the filter at.
13855      */
13856     filter : function(value, attr, startNode){
13857         attr = attr || "text";
13858         var f;
13859         if(typeof value == "string"){
13860             var vlen = value.length;
13861             // auto clear empty filter
13862             if(vlen == 0 && this.clearBlank){
13863                 this.clear();
13864                 return;
13865             }
13866             value = value.toLowerCase();
13867             f = function(n){
13868                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13869             };
13870         }else if(value.exec){ // regex?
13871             f = function(n){
13872                 return value.test(n.attributes[attr]);
13873             };
13874         }else{
13875             throw 'Illegal filter type, must be string or regex';
13876         }
13877         this.filterBy(f, null, startNode);
13878         },
13879
13880     /**
13881      * Filter by a function. The passed function will be called with each
13882      * node in the tree (or from the startNode). If the function returns true, the node is kept
13883      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13884      * @param {Function} fn The filter function
13885      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13886      */
13887     filterBy : function(fn, scope, startNode){
13888         startNode = startNode || this.tree.root;
13889         if(this.autoClear){
13890             this.clear();
13891         }
13892         var af = this.filtered, rv = this.reverse;
13893         var f = function(n){
13894             if(n == startNode){
13895                 return true;
13896             }
13897             if(af[n.id]){
13898                 return false;
13899             }
13900             var m = fn.call(scope || n, n);
13901             if(!m || rv){
13902                 af[n.id] = n;
13903                 n.ui.hide();
13904                 return false;
13905             }
13906             return true;
13907         };
13908         startNode.cascade(f);
13909         if(this.remove){
13910            for(var id in af){
13911                if(typeof id != "function"){
13912                    var n = af[id];
13913                    if(n && n.parentNode){
13914                        n.parentNode.removeChild(n);
13915                    }
13916                }
13917            }
13918         }
13919     },
13920
13921     /**
13922      * Clears the current filter. Note: with the "remove" option
13923      * set a filter cannot be cleared.
13924      */
13925     clear : function(){
13926         var t = this.tree;
13927         var af = this.filtered;
13928         for(var id in af){
13929             if(typeof id != "function"){
13930                 var n = af[id];
13931                 if(n){
13932                     n.ui.show();
13933                 }
13934             }
13935         }
13936         this.filtered = {};
13937     }
13938 };
13939 /*
13940  * Based on:
13941  * Ext JS Library 1.1.1
13942  * Copyright(c) 2006-2007, Ext JS, LLC.
13943  *
13944  * Originally Released Under LGPL - original licence link has changed is not relivant.
13945  *
13946  * Fork - LGPL
13947  * <script type="text/javascript">
13948  */
13949  
13950
13951 /**
13952  * @class Roo.tree.TreeSorter
13953  * Provides sorting of nodes in a TreePanel
13954  * 
13955  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13956  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13957  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13958  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13959  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13960  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13961  * @constructor
13962  * @param {TreePanel} tree
13963  * @param {Object} config
13964  */
13965 Roo.tree.TreeSorter = function(tree, config){
13966     Roo.apply(this, config);
13967     tree.on("beforechildrenrendered", this.doSort, this);
13968     tree.on("append", this.updateSort, this);
13969     tree.on("insert", this.updateSort, this);
13970     
13971     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13972     var p = this.property || "text";
13973     var sortType = this.sortType;
13974     var fs = this.folderSort;
13975     var cs = this.caseSensitive === true;
13976     var leafAttr = this.leafAttr || 'leaf';
13977
13978     this.sortFn = function(n1, n2){
13979         if(fs){
13980             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13981                 return 1;
13982             }
13983             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13984                 return -1;
13985             }
13986         }
13987         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13988         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13989         if(v1 < v2){
13990                         return dsc ? +1 : -1;
13991                 }else if(v1 > v2){
13992                         return dsc ? -1 : +1;
13993         }else{
13994                 return 0;
13995         }
13996     };
13997 };
13998
13999 Roo.tree.TreeSorter.prototype = {
14000     doSort : function(node){
14001         node.sort(this.sortFn);
14002     },
14003     
14004     compareNodes : function(n1, n2){
14005         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14006     },
14007     
14008     updateSort : function(tree, node){
14009         if(node.childrenRendered){
14010             this.doSort.defer(1, this, [node]);
14011         }
14012     }
14013 };/*
14014  * Based on:
14015  * Ext JS Library 1.1.1
14016  * Copyright(c) 2006-2007, Ext JS, LLC.
14017  *
14018  * Originally Released Under LGPL - original licence link has changed is not relivant.
14019  *
14020  * Fork - LGPL
14021  * <script type="text/javascript">
14022  */
14023
14024 if(Roo.dd.DropZone){
14025     
14026 Roo.tree.TreeDropZone = function(tree, config){
14027     this.allowParentInsert = false;
14028     this.allowContainerDrop = false;
14029     this.appendOnly = false;
14030     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14031     this.tree = tree;
14032     this.lastInsertClass = "x-tree-no-status";
14033     this.dragOverData = {};
14034 };
14035
14036 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14037     ddGroup : "TreeDD",
14038     scroll:  true,
14039     
14040     expandDelay : 1000,
14041     
14042     expandNode : function(node){
14043         if(node.hasChildNodes() && !node.isExpanded()){
14044             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14045         }
14046     },
14047     
14048     queueExpand : function(node){
14049         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14050     },
14051     
14052     cancelExpand : function(){
14053         if(this.expandProcId){
14054             clearTimeout(this.expandProcId);
14055             this.expandProcId = false;
14056         }
14057     },
14058     
14059     isValidDropPoint : function(n, pt, dd, e, data){
14060         if(!n || !data){ return false; }
14061         var targetNode = n.node;
14062         var dropNode = data.node;
14063         // default drop rules
14064         if(!(targetNode && targetNode.isTarget && pt)){
14065             return false;
14066         }
14067         if(pt == "append" && targetNode.allowChildren === false){
14068             return false;
14069         }
14070         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14071             return false;
14072         }
14073         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14074             return false;
14075         }
14076         // reuse the object
14077         var overEvent = this.dragOverData;
14078         overEvent.tree = this.tree;
14079         overEvent.target = targetNode;
14080         overEvent.data = data;
14081         overEvent.point = pt;
14082         overEvent.source = dd;
14083         overEvent.rawEvent = e;
14084         overEvent.dropNode = dropNode;
14085         overEvent.cancel = false;  
14086         var result = this.tree.fireEvent("nodedragover", overEvent);
14087         return overEvent.cancel === false && result !== false;
14088     },
14089     
14090     getDropPoint : function(e, n, dd)
14091     {
14092         var tn = n.node;
14093         if(tn.isRoot){
14094             return tn.allowChildren !== false ? "append" : false; // always append for root
14095         }
14096         var dragEl = n.ddel;
14097         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14098         var y = Roo.lib.Event.getPageY(e);
14099         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14100         
14101         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14102         var noAppend = tn.allowChildren === false;
14103         if(this.appendOnly || tn.parentNode.allowChildren === false){
14104             return noAppend ? false : "append";
14105         }
14106         var noBelow = false;
14107         if(!this.allowParentInsert){
14108             noBelow = tn.hasChildNodes() && tn.isExpanded();
14109         }
14110         var q = (b - t) / (noAppend ? 2 : 3);
14111         if(y >= t && y < (t + q)){
14112             return "above";
14113         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14114             return "below";
14115         }else{
14116             return "append";
14117         }
14118     },
14119     
14120     onNodeEnter : function(n, dd, e, data)
14121     {
14122         this.cancelExpand();
14123     },
14124     
14125     onNodeOver : function(n, dd, e, data)
14126     {
14127        
14128         var pt = this.getDropPoint(e, n, dd);
14129         var node = n.node;
14130         
14131         // auto node expand check
14132         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14133             this.queueExpand(node);
14134         }else if(pt != "append"){
14135             this.cancelExpand();
14136         }
14137         
14138         // set the insert point style on the target node
14139         var returnCls = this.dropNotAllowed;
14140         if(this.isValidDropPoint(n, pt, dd, e, data)){
14141            if(pt){
14142                var el = n.ddel;
14143                var cls;
14144                if(pt == "above"){
14145                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14146                    cls = "x-tree-drag-insert-above";
14147                }else if(pt == "below"){
14148                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14149                    cls = "x-tree-drag-insert-below";
14150                }else{
14151                    returnCls = "x-tree-drop-ok-append";
14152                    cls = "x-tree-drag-append";
14153                }
14154                if(this.lastInsertClass != cls){
14155                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14156                    this.lastInsertClass = cls;
14157                }
14158            }
14159        }
14160        return returnCls;
14161     },
14162     
14163     onNodeOut : function(n, dd, e, data){
14164         
14165         this.cancelExpand();
14166         this.removeDropIndicators(n);
14167     },
14168     
14169     onNodeDrop : function(n, dd, e, data){
14170         var point = this.getDropPoint(e, n, dd);
14171         var targetNode = n.node;
14172         targetNode.ui.startDrop();
14173         if(!this.isValidDropPoint(n, point, dd, e, data)){
14174             targetNode.ui.endDrop();
14175             return false;
14176         }
14177         // first try to find the drop node
14178         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14179         var dropEvent = {
14180             tree : this.tree,
14181             target: targetNode,
14182             data: data,
14183             point: point,
14184             source: dd,
14185             rawEvent: e,
14186             dropNode: dropNode,
14187             cancel: !dropNode   
14188         };
14189         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14190         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14191             targetNode.ui.endDrop();
14192             return false;
14193         }
14194         // allow target changing
14195         targetNode = dropEvent.target;
14196         if(point == "append" && !targetNode.isExpanded()){
14197             targetNode.expand(false, null, function(){
14198                 this.completeDrop(dropEvent);
14199             }.createDelegate(this));
14200         }else{
14201             this.completeDrop(dropEvent);
14202         }
14203         return true;
14204     },
14205     
14206     completeDrop : function(de){
14207         var ns = de.dropNode, p = de.point, t = de.target;
14208         if(!(ns instanceof Array)){
14209             ns = [ns];
14210         }
14211         var n;
14212         for(var i = 0, len = ns.length; i < len; i++){
14213             n = ns[i];
14214             if(p == "above"){
14215                 t.parentNode.insertBefore(n, t);
14216             }else if(p == "below"){
14217                 t.parentNode.insertBefore(n, t.nextSibling);
14218             }else{
14219                 t.appendChild(n);
14220             }
14221         }
14222         n.ui.focus();
14223         if(this.tree.hlDrop){
14224             n.ui.highlight();
14225         }
14226         t.ui.endDrop();
14227         this.tree.fireEvent("nodedrop", de);
14228     },
14229     
14230     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14231         if(this.tree.hlDrop){
14232             dropNode.ui.focus();
14233             dropNode.ui.highlight();
14234         }
14235         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14236     },
14237     
14238     getTree : function(){
14239         return this.tree;
14240     },
14241     
14242     removeDropIndicators : function(n){
14243         if(n && n.ddel){
14244             var el = n.ddel;
14245             Roo.fly(el).removeClass([
14246                     "x-tree-drag-insert-above",
14247                     "x-tree-drag-insert-below",
14248                     "x-tree-drag-append"]);
14249             this.lastInsertClass = "_noclass";
14250         }
14251     },
14252     
14253     beforeDragDrop : function(target, e, id){
14254         this.cancelExpand();
14255         return true;
14256     },
14257     
14258     afterRepair : function(data){
14259         if(data && Roo.enableFx){
14260             data.node.ui.highlight();
14261         }
14262         this.hideProxy();
14263     } 
14264     
14265 });
14266
14267 }
14268 /*
14269  * Based on:
14270  * Ext JS Library 1.1.1
14271  * Copyright(c) 2006-2007, Ext JS, LLC.
14272  *
14273  * Originally Released Under LGPL - original licence link has changed is not relivant.
14274  *
14275  * Fork - LGPL
14276  * <script type="text/javascript">
14277  */
14278  
14279
14280 if(Roo.dd.DragZone){
14281 Roo.tree.TreeDragZone = function(tree, config){
14282     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14283     this.tree = tree;
14284 };
14285
14286 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14287     ddGroup : "TreeDD",
14288    
14289     onBeforeDrag : function(data, e){
14290         var n = data.node;
14291         return n && n.draggable && !n.disabled;
14292     },
14293      
14294     
14295     onInitDrag : function(e){
14296         var data = this.dragData;
14297         this.tree.getSelectionModel().select(data.node);
14298         this.proxy.update("");
14299         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14300         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14301     },
14302     
14303     getRepairXY : function(e, data){
14304         return data.node.ui.getDDRepairXY();
14305     },
14306     
14307     onEndDrag : function(data, e){
14308         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14309         
14310         
14311     },
14312     
14313     onValidDrop : function(dd, e, id){
14314         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14315         this.hideProxy();
14316     },
14317     
14318     beforeInvalidDrop : function(e, id){
14319         // this scrolls the original position back into view
14320         var sm = this.tree.getSelectionModel();
14321         sm.clearSelections();
14322         sm.select(this.dragData.node);
14323     }
14324 });
14325 }/*
14326  * Based on:
14327  * Ext JS Library 1.1.1
14328  * Copyright(c) 2006-2007, Ext JS, LLC.
14329  *
14330  * Originally Released Under LGPL - original licence link has changed is not relivant.
14331  *
14332  * Fork - LGPL
14333  * <script type="text/javascript">
14334  */
14335 /**
14336  * @class Roo.tree.TreeEditor
14337  * @extends Roo.Editor
14338  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14339  * as the editor field.
14340  * @constructor
14341  * @param {Object} config (used to be the tree panel.)
14342  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14343  * 
14344  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14345  * @cfg {Roo.form.TextField|Object} field The field configuration
14346  *
14347  * 
14348  */
14349 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14350     var tree = config;
14351     var field;
14352     if (oldconfig) { // old style..
14353         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14354     } else {
14355         // new style..
14356         tree = config.tree;
14357         config.field = config.field  || {};
14358         config.field.xtype = 'TextField';
14359         field = Roo.factory(config.field, Roo.form);
14360     }
14361     config = config || {};
14362     
14363     
14364     this.addEvents({
14365         /**
14366          * @event beforenodeedit
14367          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14368          * false from the handler of this event.
14369          * @param {Editor} this
14370          * @param {Roo.tree.Node} node 
14371          */
14372         "beforenodeedit" : true
14373     });
14374     
14375     //Roo.log(config);
14376     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14377
14378     this.tree = tree;
14379
14380     tree.on('beforeclick', this.beforeNodeClick, this);
14381     tree.getTreeEl().on('mousedown', this.hide, this);
14382     this.on('complete', this.updateNode, this);
14383     this.on('beforestartedit', this.fitToTree, this);
14384     this.on('startedit', this.bindScroll, this, {delay:10});
14385     this.on('specialkey', this.onSpecialKey, this);
14386 };
14387
14388 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14389     /**
14390      * @cfg {String} alignment
14391      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14392      */
14393     alignment: "l-l",
14394     // inherit
14395     autoSize: false,
14396     /**
14397      * @cfg {Boolean} hideEl
14398      * True to hide the bound element while the editor is displayed (defaults to false)
14399      */
14400     hideEl : false,
14401     /**
14402      * @cfg {String} cls
14403      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14404      */
14405     cls: "x-small-editor x-tree-editor",
14406     /**
14407      * @cfg {Boolean} shim
14408      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14409      */
14410     shim:false,
14411     // inherit
14412     shadow:"frame",
14413     /**
14414      * @cfg {Number} maxWidth
14415      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14416      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14417      * scroll and client offsets into account prior to each edit.
14418      */
14419     maxWidth: 250,
14420
14421     editDelay : 350,
14422
14423     // private
14424     fitToTree : function(ed, el){
14425         var td = this.tree.getTreeEl().dom, nd = el.dom;
14426         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14427             td.scrollLeft = nd.offsetLeft;
14428         }
14429         var w = Math.min(
14430                 this.maxWidth,
14431                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14432         this.setSize(w, '');
14433         
14434         return this.fireEvent('beforenodeedit', this, this.editNode);
14435         
14436     },
14437
14438     // private
14439     triggerEdit : function(node){
14440         this.completeEdit();
14441         this.editNode = node;
14442         this.startEdit(node.ui.textNode, node.text);
14443     },
14444
14445     // private
14446     bindScroll : function(){
14447         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14448     },
14449
14450     // private
14451     beforeNodeClick : function(node, e){
14452         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14453         this.lastClick = new Date();
14454         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14455             e.stopEvent();
14456             this.triggerEdit(node);
14457             return false;
14458         }
14459         return true;
14460     },
14461
14462     // private
14463     updateNode : function(ed, value){
14464         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14465         this.editNode.setText(value);
14466     },
14467
14468     // private
14469     onHide : function(){
14470         Roo.tree.TreeEditor.superclass.onHide.call(this);
14471         if(this.editNode){
14472             this.editNode.ui.focus();
14473         }
14474     },
14475
14476     // private
14477     onSpecialKey : function(field, e){
14478         var k = e.getKey();
14479         if(k == e.ESC){
14480             e.stopEvent();
14481             this.cancelEdit();
14482         }else if(k == e.ENTER && !e.hasModifier()){
14483             e.stopEvent();
14484             this.completeEdit();
14485         }
14486     }
14487 });//<Script type="text/javascript">
14488 /*
14489  * Based on:
14490  * Ext JS Library 1.1.1
14491  * Copyright(c) 2006-2007, Ext JS, LLC.
14492  *
14493  * Originally Released Under LGPL - original licence link has changed is not relivant.
14494  *
14495  * Fork - LGPL
14496  * <script type="text/javascript">
14497  */
14498  
14499 /**
14500  * Not documented??? - probably should be...
14501  */
14502
14503 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14504     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14505     
14506     renderElements : function(n, a, targetNode, bulkRender){
14507         //consel.log("renderElements?");
14508         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14509
14510         var t = n.getOwnerTree();
14511         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14512         
14513         var cols = t.columns;
14514         var bw = t.borderWidth;
14515         var c = cols[0];
14516         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14517          var cb = typeof a.checked == "boolean";
14518         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14519         var colcls = 'x-t-' + tid + '-c0';
14520         var buf = [
14521             '<li class="x-tree-node">',
14522             
14523                 
14524                 '<div class="x-tree-node-el ', a.cls,'">',
14525                     // extran...
14526                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14527                 
14528                 
14529                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14530                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14531                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14532                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14533                            (a.iconCls ? ' '+a.iconCls : ''),
14534                            '" unselectable="on" />',
14535                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14536                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14537                              
14538                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14539                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14540                             '<span unselectable="on" qtip="' + tx + '">',
14541                              tx,
14542                              '</span></a>' ,
14543                     '</div>',
14544                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14545                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14546                  ];
14547         for(var i = 1, len = cols.length; i < len; i++){
14548             c = cols[i];
14549             colcls = 'x-t-' + tid + '-c' +i;
14550             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14551             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14552                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14553                       "</div>");
14554          }
14555          
14556          buf.push(
14557             '</a>',
14558             '<div class="x-clear"></div></div>',
14559             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14560             "</li>");
14561         
14562         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14563             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14564                                 n.nextSibling.ui.getEl(), buf.join(""));
14565         }else{
14566             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14567         }
14568         var el = this.wrap.firstChild;
14569         this.elRow = el;
14570         this.elNode = el.firstChild;
14571         this.ranchor = el.childNodes[1];
14572         this.ctNode = this.wrap.childNodes[1];
14573         var cs = el.firstChild.childNodes;
14574         this.indentNode = cs[0];
14575         this.ecNode = cs[1];
14576         this.iconNode = cs[2];
14577         var index = 3;
14578         if(cb){
14579             this.checkbox = cs[3];
14580             index++;
14581         }
14582         this.anchor = cs[index];
14583         
14584         this.textNode = cs[index].firstChild;
14585         
14586         //el.on("click", this.onClick, this);
14587         //el.on("dblclick", this.onDblClick, this);
14588         
14589         
14590        // console.log(this);
14591     },
14592     initEvents : function(){
14593         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14594         
14595             
14596         var a = this.ranchor;
14597
14598         var el = Roo.get(a);
14599
14600         if(Roo.isOpera){ // opera render bug ignores the CSS
14601             el.setStyle("text-decoration", "none");
14602         }
14603
14604         el.on("click", this.onClick, this);
14605         el.on("dblclick", this.onDblClick, this);
14606         el.on("contextmenu", this.onContextMenu, this);
14607         
14608     },
14609     
14610     /*onSelectedChange : function(state){
14611         if(state){
14612             this.focus();
14613             this.addClass("x-tree-selected");
14614         }else{
14615             //this.blur();
14616             this.removeClass("x-tree-selected");
14617         }
14618     },*/
14619     addClass : function(cls){
14620         if(this.elRow){
14621             Roo.fly(this.elRow).addClass(cls);
14622         }
14623         
14624     },
14625     
14626     
14627     removeClass : function(cls){
14628         if(this.elRow){
14629             Roo.fly(this.elRow).removeClass(cls);
14630         }
14631     }
14632
14633     
14634     
14635 });//<Script type="text/javascript">
14636
14637 /*
14638  * Based on:
14639  * Ext JS Library 1.1.1
14640  * Copyright(c) 2006-2007, Ext JS, LLC.
14641  *
14642  * Originally Released Under LGPL - original licence link has changed is not relivant.
14643  *
14644  * Fork - LGPL
14645  * <script type="text/javascript">
14646  */
14647  
14648
14649 /**
14650  * @class Roo.tree.ColumnTree
14651  * @extends Roo.data.TreePanel
14652  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14653  * @cfg {int} borderWidth  compined right/left border allowance
14654  * @constructor
14655  * @param {String/HTMLElement/Element} el The container element
14656  * @param {Object} config
14657  */
14658 Roo.tree.ColumnTree =  function(el, config)
14659 {
14660    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14661    this.addEvents({
14662         /**
14663         * @event resize
14664         * Fire this event on a container when it resizes
14665         * @param {int} w Width
14666         * @param {int} h Height
14667         */
14668        "resize" : true
14669     });
14670     this.on('resize', this.onResize, this);
14671 };
14672
14673 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14674     //lines:false,
14675     
14676     
14677     borderWidth: Roo.isBorderBox ? 0 : 2, 
14678     headEls : false,
14679     
14680     render : function(){
14681         // add the header.....
14682        
14683         Roo.tree.ColumnTree.superclass.render.apply(this);
14684         
14685         this.el.addClass('x-column-tree');
14686         
14687         this.headers = this.el.createChild(
14688             {cls:'x-tree-headers'},this.innerCt.dom);
14689    
14690         var cols = this.columns, c;
14691         var totalWidth = 0;
14692         this.headEls = [];
14693         var  len = cols.length;
14694         for(var i = 0; i < len; i++){
14695              c = cols[i];
14696              totalWidth += c.width;
14697             this.headEls.push(this.headers.createChild({
14698                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14699                  cn: {
14700                      cls:'x-tree-hd-text',
14701                      html: c.header
14702                  },
14703                  style:'width:'+(c.width-this.borderWidth)+'px;'
14704              }));
14705         }
14706         this.headers.createChild({cls:'x-clear'});
14707         // prevent floats from wrapping when clipped
14708         this.headers.setWidth(totalWidth);
14709         //this.innerCt.setWidth(totalWidth);
14710         this.innerCt.setStyle({ overflow: 'auto' });
14711         this.onResize(this.width, this.height);
14712              
14713         
14714     },
14715     onResize : function(w,h)
14716     {
14717         this.height = h;
14718         this.width = w;
14719         // resize cols..
14720         this.innerCt.setWidth(this.width);
14721         this.innerCt.setHeight(this.height-20);
14722         
14723         // headers...
14724         var cols = this.columns, c;
14725         var totalWidth = 0;
14726         var expEl = false;
14727         var len = cols.length;
14728         for(var i = 0; i < len; i++){
14729             c = cols[i];
14730             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14731                 // it's the expander..
14732                 expEl  = this.headEls[i];
14733                 continue;
14734             }
14735             totalWidth += c.width;
14736             
14737         }
14738         if (expEl) {
14739             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14740         }
14741         this.headers.setWidth(w-20);
14742
14743         
14744         
14745         
14746     }
14747 });
14748 /*
14749  * Based on:
14750  * Ext JS Library 1.1.1
14751  * Copyright(c) 2006-2007, Ext JS, LLC.
14752  *
14753  * Originally Released Under LGPL - original licence link has changed is not relivant.
14754  *
14755  * Fork - LGPL
14756  * <script type="text/javascript">
14757  */
14758  
14759 /**
14760  * @class Roo.menu.Menu
14761  * @extends Roo.util.Observable
14762  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14763  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14764  * @constructor
14765  * Creates a new Menu
14766  * @param {Object} config Configuration options
14767  */
14768 Roo.menu.Menu = function(config){
14769     Roo.apply(this, config);
14770     this.id = this.id || Roo.id();
14771     this.addEvents({
14772         /**
14773          * @event beforeshow
14774          * Fires before this menu is displayed
14775          * @param {Roo.menu.Menu} this
14776          */
14777         beforeshow : true,
14778         /**
14779          * @event beforehide
14780          * Fires before this menu is hidden
14781          * @param {Roo.menu.Menu} this
14782          */
14783         beforehide : true,
14784         /**
14785          * @event show
14786          * Fires after this menu is displayed
14787          * @param {Roo.menu.Menu} this
14788          */
14789         show : true,
14790         /**
14791          * @event hide
14792          * Fires after this menu is hidden
14793          * @param {Roo.menu.Menu} this
14794          */
14795         hide : true,
14796         /**
14797          * @event click
14798          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14799          * @param {Roo.menu.Menu} this
14800          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14801          * @param {Roo.EventObject} e
14802          */
14803         click : true,
14804         /**
14805          * @event mouseover
14806          * Fires when the mouse is hovering over this menu
14807          * @param {Roo.menu.Menu} this
14808          * @param {Roo.EventObject} e
14809          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14810          */
14811         mouseover : true,
14812         /**
14813          * @event mouseout
14814          * Fires when the mouse exits this menu
14815          * @param {Roo.menu.Menu} this
14816          * @param {Roo.EventObject} e
14817          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14818          */
14819         mouseout : true,
14820         /**
14821          * @event itemclick
14822          * Fires when a menu item contained in this menu is clicked
14823          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14824          * @param {Roo.EventObject} e
14825          */
14826         itemclick: true
14827     });
14828     if (this.registerMenu) {
14829         Roo.menu.MenuMgr.register(this);
14830     }
14831     
14832     var mis = this.items;
14833     this.items = new Roo.util.MixedCollection();
14834     if(mis){
14835         this.add.apply(this, mis);
14836     }
14837 };
14838
14839 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14840     /**
14841      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14842      */
14843     minWidth : 120,
14844     /**
14845      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14846      * for bottom-right shadow (defaults to "sides")
14847      */
14848     shadow : "sides",
14849     /**
14850      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14851      * this menu (defaults to "tl-tr?")
14852      */
14853     subMenuAlign : "tl-tr?",
14854     /**
14855      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14856      * relative to its element of origin (defaults to "tl-bl?")
14857      */
14858     defaultAlign : "tl-bl?",
14859     /**
14860      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14861      */
14862     allowOtherMenus : false,
14863     /**
14864      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14865      */
14866     registerMenu : true,
14867
14868     hidden:true,
14869
14870     // private
14871     render : function(){
14872         if(this.el){
14873             return;
14874         }
14875         var el = this.el = new Roo.Layer({
14876             cls: "x-menu",
14877             shadow:this.shadow,
14878             constrain: false,
14879             parentEl: this.parentEl || document.body,
14880             zindex:15000
14881         });
14882
14883         this.keyNav = new Roo.menu.MenuNav(this);
14884
14885         if(this.plain){
14886             el.addClass("x-menu-plain");
14887         }
14888         if(this.cls){
14889             el.addClass(this.cls);
14890         }
14891         // generic focus element
14892         this.focusEl = el.createChild({
14893             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14894         });
14895         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14896         //disabling touch- as it's causing issues ..
14897         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14898         ul.on('click'   , this.onClick, this);
14899         
14900         
14901         ul.on("mouseover", this.onMouseOver, this);
14902         ul.on("mouseout", this.onMouseOut, this);
14903         this.items.each(function(item){
14904             if (item.hidden) {
14905                 return;
14906             }
14907             
14908             var li = document.createElement("li");
14909             li.className = "x-menu-list-item";
14910             ul.dom.appendChild(li);
14911             item.render(li, this);
14912         }, this);
14913         this.ul = ul;
14914         this.autoWidth();
14915     },
14916
14917     // private
14918     autoWidth : function(){
14919         var el = this.el, ul = this.ul;
14920         if(!el){
14921             return;
14922         }
14923         var w = this.width;
14924         if(w){
14925             el.setWidth(w);
14926         }else if(Roo.isIE){
14927             el.setWidth(this.minWidth);
14928             var t = el.dom.offsetWidth; // force recalc
14929             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14930         }
14931     },
14932
14933     // private
14934     delayAutoWidth : function(){
14935         if(this.rendered){
14936             if(!this.awTask){
14937                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14938             }
14939             this.awTask.delay(20);
14940         }
14941     },
14942
14943     // private
14944     findTargetItem : function(e){
14945         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14946         if(t && t.menuItemId){
14947             return this.items.get(t.menuItemId);
14948         }
14949     },
14950
14951     // private
14952     onClick : function(e){
14953         Roo.log("menu.onClick");
14954         var t = this.findTargetItem(e);
14955         if(!t){
14956             return;
14957         }
14958         Roo.log(e);
14959         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14960             if(t == this.activeItem && t.shouldDeactivate(e)){
14961                 this.activeItem.deactivate();
14962                 delete this.activeItem;
14963                 return;
14964             }
14965             if(t.canActivate){
14966                 this.setActiveItem(t, true);
14967             }
14968             return;
14969             
14970             
14971         }
14972         
14973         t.onClick(e);
14974         this.fireEvent("click", this, t, e);
14975     },
14976
14977     // private
14978     setActiveItem : function(item, autoExpand){
14979         if(item != this.activeItem){
14980             if(this.activeItem){
14981                 this.activeItem.deactivate();
14982             }
14983             this.activeItem = item;
14984             item.activate(autoExpand);
14985         }else if(autoExpand){
14986             item.expandMenu();
14987         }
14988     },
14989
14990     // private
14991     tryActivate : function(start, step){
14992         var items = this.items;
14993         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14994             var item = items.get(i);
14995             if(!item.disabled && item.canActivate){
14996                 this.setActiveItem(item, false);
14997                 return item;
14998             }
14999         }
15000         return false;
15001     },
15002
15003     // private
15004     onMouseOver : function(e){
15005         var t;
15006         if(t = this.findTargetItem(e)){
15007             if(t.canActivate && !t.disabled){
15008                 this.setActiveItem(t, true);
15009             }
15010         }
15011         this.fireEvent("mouseover", this, e, t);
15012     },
15013
15014     // private
15015     onMouseOut : function(e){
15016         var t;
15017         if(t = this.findTargetItem(e)){
15018             if(t == this.activeItem && t.shouldDeactivate(e)){
15019                 this.activeItem.deactivate();
15020                 delete this.activeItem;
15021             }
15022         }
15023         this.fireEvent("mouseout", this, e, t);
15024     },
15025
15026     /**
15027      * Read-only.  Returns true if the menu is currently displayed, else false.
15028      * @type Boolean
15029      */
15030     isVisible : function(){
15031         return this.el && !this.hidden;
15032     },
15033
15034     /**
15035      * Displays this menu relative to another element
15036      * @param {String/HTMLElement/Roo.Element} element The element to align to
15037      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15038      * the element (defaults to this.defaultAlign)
15039      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15040      */
15041     show : function(el, pos, parentMenu){
15042         this.parentMenu = parentMenu;
15043         if(!this.el){
15044             this.render();
15045         }
15046         this.fireEvent("beforeshow", this);
15047         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15048     },
15049
15050     /**
15051      * Displays this menu at a specific xy position
15052      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15053      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15054      */
15055     showAt : function(xy, parentMenu, /* private: */_e){
15056         this.parentMenu = parentMenu;
15057         if(!this.el){
15058             this.render();
15059         }
15060         if(_e !== false){
15061             this.fireEvent("beforeshow", this);
15062             xy = this.el.adjustForConstraints(xy);
15063         }
15064         this.el.setXY(xy);
15065         this.el.show();
15066         this.hidden = false;
15067         this.focus();
15068         this.fireEvent("show", this);
15069     },
15070
15071     focus : function(){
15072         if(!this.hidden){
15073             this.doFocus.defer(50, this);
15074         }
15075     },
15076
15077     doFocus : function(){
15078         if(!this.hidden){
15079             this.focusEl.focus();
15080         }
15081     },
15082
15083     /**
15084      * Hides this menu and optionally all parent menus
15085      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15086      */
15087     hide : function(deep){
15088         if(this.el && this.isVisible()){
15089             this.fireEvent("beforehide", this);
15090             if(this.activeItem){
15091                 this.activeItem.deactivate();
15092                 this.activeItem = null;
15093             }
15094             this.el.hide();
15095             this.hidden = true;
15096             this.fireEvent("hide", this);
15097         }
15098         if(deep === true && this.parentMenu){
15099             this.parentMenu.hide(true);
15100         }
15101     },
15102
15103     /**
15104      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15105      * Any of the following are valid:
15106      * <ul>
15107      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15108      * <li>An HTMLElement object which will be converted to a menu item</li>
15109      * <li>A menu item config object that will be created as a new menu item</li>
15110      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15111      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15112      * </ul>
15113      * Usage:
15114      * <pre><code>
15115 // Create the menu
15116 var menu = new Roo.menu.Menu();
15117
15118 // Create a menu item to add by reference
15119 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15120
15121 // Add a bunch of items at once using different methods.
15122 // Only the last item added will be returned.
15123 var item = menu.add(
15124     menuItem,                // add existing item by ref
15125     'Dynamic Item',          // new TextItem
15126     '-',                     // new separator
15127     { text: 'Config Item' }  // new item by config
15128 );
15129 </code></pre>
15130      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15131      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15132      */
15133     add : function(){
15134         var a = arguments, l = a.length, item;
15135         for(var i = 0; i < l; i++){
15136             var el = a[i];
15137             if ((typeof(el) == "object") && el.xtype && el.xns) {
15138                 el = Roo.factory(el, Roo.menu);
15139             }
15140             
15141             if(el.render){ // some kind of Item
15142                 item = this.addItem(el);
15143             }else if(typeof el == "string"){ // string
15144                 if(el == "separator" || el == "-"){
15145                     item = this.addSeparator();
15146                 }else{
15147                     item = this.addText(el);
15148                 }
15149             }else if(el.tagName || el.el){ // element
15150                 item = this.addElement(el);
15151             }else if(typeof el == "object"){ // must be menu item config?
15152                 item = this.addMenuItem(el);
15153             }
15154         }
15155         return item;
15156     },
15157
15158     /**
15159      * Returns this menu's underlying {@link Roo.Element} object
15160      * @return {Roo.Element} The element
15161      */
15162     getEl : function(){
15163         if(!this.el){
15164             this.render();
15165         }
15166         return this.el;
15167     },
15168
15169     /**
15170      * Adds a separator bar to the menu
15171      * @return {Roo.menu.Item} The menu item that was added
15172      */
15173     addSeparator : function(){
15174         return this.addItem(new Roo.menu.Separator());
15175     },
15176
15177     /**
15178      * Adds an {@link Roo.Element} object to the menu
15179      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15180      * @return {Roo.menu.Item} The menu item that was added
15181      */
15182     addElement : function(el){
15183         return this.addItem(new Roo.menu.BaseItem(el));
15184     },
15185
15186     /**
15187      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15188      * @param {Roo.menu.Item} item The menu item to add
15189      * @return {Roo.menu.Item} The menu item that was added
15190      */
15191     addItem : function(item){
15192         this.items.add(item);
15193         if(this.ul){
15194             var li = document.createElement("li");
15195             li.className = "x-menu-list-item";
15196             this.ul.dom.appendChild(li);
15197             item.render(li, this);
15198             this.delayAutoWidth();
15199         }
15200         return item;
15201     },
15202
15203     /**
15204      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15205      * @param {Object} config A MenuItem config object
15206      * @return {Roo.menu.Item} The menu item that was added
15207      */
15208     addMenuItem : function(config){
15209         if(!(config instanceof Roo.menu.Item)){
15210             if(typeof config.checked == "boolean"){ // must be check menu item config?
15211                 config = new Roo.menu.CheckItem(config);
15212             }else{
15213                 config = new Roo.menu.Item(config);
15214             }
15215         }
15216         return this.addItem(config);
15217     },
15218
15219     /**
15220      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15221      * @param {String} text The text to display in the menu item
15222      * @return {Roo.menu.Item} The menu item that was added
15223      */
15224     addText : function(text){
15225         return this.addItem(new Roo.menu.TextItem({ text : text }));
15226     },
15227
15228     /**
15229      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15230      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15231      * @param {Roo.menu.Item} item The menu item to add
15232      * @return {Roo.menu.Item} The menu item that was added
15233      */
15234     insert : function(index, item){
15235         this.items.insert(index, item);
15236         if(this.ul){
15237             var li = document.createElement("li");
15238             li.className = "x-menu-list-item";
15239             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15240             item.render(li, this);
15241             this.delayAutoWidth();
15242         }
15243         return item;
15244     },
15245
15246     /**
15247      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15248      * @param {Roo.menu.Item} item The menu item to remove
15249      */
15250     remove : function(item){
15251         this.items.removeKey(item.id);
15252         item.destroy();
15253     },
15254
15255     /**
15256      * Removes and destroys all items in the menu
15257      */
15258     removeAll : function(){
15259         var f;
15260         while(f = this.items.first()){
15261             this.remove(f);
15262         }
15263     }
15264 });
15265
15266 // MenuNav is a private utility class used internally by the Menu
15267 Roo.menu.MenuNav = function(menu){
15268     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15269     this.scope = this.menu = menu;
15270 };
15271
15272 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15273     doRelay : function(e, h){
15274         var k = e.getKey();
15275         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15276             this.menu.tryActivate(0, 1);
15277             return false;
15278         }
15279         return h.call(this.scope || this, e, this.menu);
15280     },
15281
15282     up : function(e, m){
15283         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15284             m.tryActivate(m.items.length-1, -1);
15285         }
15286     },
15287
15288     down : function(e, m){
15289         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15290             m.tryActivate(0, 1);
15291         }
15292     },
15293
15294     right : function(e, m){
15295         if(m.activeItem){
15296             m.activeItem.expandMenu(true);
15297         }
15298     },
15299
15300     left : function(e, m){
15301         m.hide();
15302         if(m.parentMenu && m.parentMenu.activeItem){
15303             m.parentMenu.activeItem.activate();
15304         }
15305     },
15306
15307     enter : function(e, m){
15308         if(m.activeItem){
15309             e.stopPropagation();
15310             m.activeItem.onClick(e);
15311             m.fireEvent("click", this, m.activeItem);
15312             return true;
15313         }
15314     }
15315 });/*
15316  * Based on:
15317  * Ext JS Library 1.1.1
15318  * Copyright(c) 2006-2007, Ext JS, LLC.
15319  *
15320  * Originally Released Under LGPL - original licence link has changed is not relivant.
15321  *
15322  * Fork - LGPL
15323  * <script type="text/javascript">
15324  */
15325  
15326 /**
15327  * @class Roo.menu.MenuMgr
15328  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15329  * @singleton
15330  */
15331 Roo.menu.MenuMgr = function(){
15332    var menus, active, groups = {}, attached = false, lastShow = new Date();
15333
15334    // private - called when first menu is created
15335    function init(){
15336        menus = {};
15337        active = new Roo.util.MixedCollection();
15338        Roo.get(document).addKeyListener(27, function(){
15339            if(active.length > 0){
15340                hideAll();
15341            }
15342        });
15343    }
15344
15345    // private
15346    function hideAll(){
15347        if(active && active.length > 0){
15348            var c = active.clone();
15349            c.each(function(m){
15350                m.hide();
15351            });
15352        }
15353    }
15354
15355    // private
15356    function onHide(m){
15357        active.remove(m);
15358        if(active.length < 1){
15359            Roo.get(document).un("mousedown", onMouseDown);
15360            attached = false;
15361        }
15362    }
15363
15364    // private
15365    function onShow(m){
15366        var last = active.last();
15367        lastShow = new Date();
15368        active.add(m);
15369        if(!attached){
15370            Roo.get(document).on("mousedown", onMouseDown);
15371            attached = true;
15372        }
15373        if(m.parentMenu){
15374           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15375           m.parentMenu.activeChild = m;
15376        }else if(last && last.isVisible()){
15377           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15378        }
15379    }
15380
15381    // private
15382    function onBeforeHide(m){
15383        if(m.activeChild){
15384            m.activeChild.hide();
15385        }
15386        if(m.autoHideTimer){
15387            clearTimeout(m.autoHideTimer);
15388            delete m.autoHideTimer;
15389        }
15390    }
15391
15392    // private
15393    function onBeforeShow(m){
15394        var pm = m.parentMenu;
15395        if(!pm && !m.allowOtherMenus){
15396            hideAll();
15397        }else if(pm && pm.activeChild && active != m){
15398            pm.activeChild.hide();
15399        }
15400    }
15401
15402    // private
15403    function onMouseDown(e){
15404        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15405            hideAll();
15406        }
15407    }
15408
15409    // private
15410    function onBeforeCheck(mi, state){
15411        if(state){
15412            var g = groups[mi.group];
15413            for(var i = 0, l = g.length; i < l; i++){
15414                if(g[i] != mi){
15415                    g[i].setChecked(false);
15416                }
15417            }
15418        }
15419    }
15420
15421    return {
15422
15423        /**
15424         * Hides all menus that are currently visible
15425         */
15426        hideAll : function(){
15427             hideAll();  
15428        },
15429
15430        // private
15431        register : function(menu){
15432            if(!menus){
15433                init();
15434            }
15435            menus[menu.id] = menu;
15436            menu.on("beforehide", onBeforeHide);
15437            menu.on("hide", onHide);
15438            menu.on("beforeshow", onBeforeShow);
15439            menu.on("show", onShow);
15440            var g = menu.group;
15441            if(g && menu.events["checkchange"]){
15442                if(!groups[g]){
15443                    groups[g] = [];
15444                }
15445                groups[g].push(menu);
15446                menu.on("checkchange", onCheck);
15447            }
15448        },
15449
15450         /**
15451          * Returns a {@link Roo.menu.Menu} object
15452          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15453          * be used to generate and return a new Menu instance.
15454          */
15455        get : function(menu){
15456            if(typeof menu == "string"){ // menu id
15457                return menus[menu];
15458            }else if(menu.events){  // menu instance
15459                return menu;
15460            }else if(typeof menu.length == 'number'){ // array of menu items?
15461                return new Roo.menu.Menu({items:menu});
15462            }else{ // otherwise, must be a config
15463                return new Roo.menu.Menu(menu);
15464            }
15465        },
15466
15467        // private
15468        unregister : function(menu){
15469            delete menus[menu.id];
15470            menu.un("beforehide", onBeforeHide);
15471            menu.un("hide", onHide);
15472            menu.un("beforeshow", onBeforeShow);
15473            menu.un("show", onShow);
15474            var g = menu.group;
15475            if(g && menu.events["checkchange"]){
15476                groups[g].remove(menu);
15477                menu.un("checkchange", onCheck);
15478            }
15479        },
15480
15481        // private
15482        registerCheckable : function(menuItem){
15483            var g = menuItem.group;
15484            if(g){
15485                if(!groups[g]){
15486                    groups[g] = [];
15487                }
15488                groups[g].push(menuItem);
15489                menuItem.on("beforecheckchange", onBeforeCheck);
15490            }
15491        },
15492
15493        // private
15494        unregisterCheckable : function(menuItem){
15495            var g = menuItem.group;
15496            if(g){
15497                groups[g].remove(menuItem);
15498                menuItem.un("beforecheckchange", onBeforeCheck);
15499            }
15500        }
15501    };
15502 }();/*
15503  * Based on:
15504  * Ext JS Library 1.1.1
15505  * Copyright(c) 2006-2007, Ext JS, LLC.
15506  *
15507  * Originally Released Under LGPL - original licence link has changed is not relivant.
15508  *
15509  * Fork - LGPL
15510  * <script type="text/javascript">
15511  */
15512  
15513
15514 /**
15515  * @class Roo.menu.BaseItem
15516  * @extends Roo.Component
15517  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15518  * management and base configuration options shared by all menu components.
15519  * @constructor
15520  * Creates a new BaseItem
15521  * @param {Object} config Configuration options
15522  */
15523 Roo.menu.BaseItem = function(config){
15524     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15525
15526     this.addEvents({
15527         /**
15528          * @event click
15529          * Fires when this item is clicked
15530          * @param {Roo.menu.BaseItem} this
15531          * @param {Roo.EventObject} e
15532          */
15533         click: true,
15534         /**
15535          * @event activate
15536          * Fires when this item is activated
15537          * @param {Roo.menu.BaseItem} this
15538          */
15539         activate : true,
15540         /**
15541          * @event deactivate
15542          * Fires when this item is deactivated
15543          * @param {Roo.menu.BaseItem} this
15544          */
15545         deactivate : true
15546     });
15547
15548     if(this.handler){
15549         this.on("click", this.handler, this.scope, true);
15550     }
15551 };
15552
15553 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15554     /**
15555      * @cfg {Function} handler
15556      * A function that will handle the click event of this menu item (defaults to undefined)
15557      */
15558     /**
15559      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15560      */
15561     canActivate : false,
15562     
15563      /**
15564      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15565      */
15566     hidden: false,
15567     
15568     /**
15569      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15570      */
15571     activeClass : "x-menu-item-active",
15572     /**
15573      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15574      */
15575     hideOnClick : true,
15576     /**
15577      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15578      */
15579     hideDelay : 100,
15580
15581     // private
15582     ctype: "Roo.menu.BaseItem",
15583
15584     // private
15585     actionMode : "container",
15586
15587     // private
15588     render : function(container, parentMenu){
15589         this.parentMenu = parentMenu;
15590         Roo.menu.BaseItem.superclass.render.call(this, container);
15591         this.container.menuItemId = this.id;
15592     },
15593
15594     // private
15595     onRender : function(container, position){
15596         this.el = Roo.get(this.el);
15597         container.dom.appendChild(this.el.dom);
15598     },
15599
15600     // private
15601     onClick : function(e){
15602         if(!this.disabled && this.fireEvent("click", this, e) !== false
15603                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15604             this.handleClick(e);
15605         }else{
15606             e.stopEvent();
15607         }
15608     },
15609
15610     // private
15611     activate : function(){
15612         if(this.disabled){
15613             return false;
15614         }
15615         var li = this.container;
15616         li.addClass(this.activeClass);
15617         this.region = li.getRegion().adjust(2, 2, -2, -2);
15618         this.fireEvent("activate", this);
15619         return true;
15620     },
15621
15622     // private
15623     deactivate : function(){
15624         this.container.removeClass(this.activeClass);
15625         this.fireEvent("deactivate", this);
15626     },
15627
15628     // private
15629     shouldDeactivate : function(e){
15630         return !this.region || !this.region.contains(e.getPoint());
15631     },
15632
15633     // private
15634     handleClick : function(e){
15635         if(this.hideOnClick){
15636             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15637         }
15638     },
15639
15640     // private
15641     expandMenu : function(autoActivate){
15642         // do nothing
15643     },
15644
15645     // private
15646     hideMenu : function(){
15647         // do nothing
15648     }
15649 });/*
15650  * Based on:
15651  * Ext JS Library 1.1.1
15652  * Copyright(c) 2006-2007, Ext JS, LLC.
15653  *
15654  * Originally Released Under LGPL - original licence link has changed is not relivant.
15655  *
15656  * Fork - LGPL
15657  * <script type="text/javascript">
15658  */
15659  
15660 /**
15661  * @class Roo.menu.Adapter
15662  * @extends Roo.menu.BaseItem
15663  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
15664  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15665  * @constructor
15666  * Creates a new Adapter
15667  * @param {Object} config Configuration options
15668  */
15669 Roo.menu.Adapter = function(component, config){
15670     Roo.menu.Adapter.superclass.constructor.call(this, config);
15671     this.component = component;
15672 };
15673 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15674     // private
15675     canActivate : true,
15676
15677     // private
15678     onRender : function(container, position){
15679         this.component.render(container);
15680         this.el = this.component.getEl();
15681     },
15682
15683     // private
15684     activate : function(){
15685         if(this.disabled){
15686             return false;
15687         }
15688         this.component.focus();
15689         this.fireEvent("activate", this);
15690         return true;
15691     },
15692
15693     // private
15694     deactivate : function(){
15695         this.fireEvent("deactivate", this);
15696     },
15697
15698     // private
15699     disable : function(){
15700         this.component.disable();
15701         Roo.menu.Adapter.superclass.disable.call(this);
15702     },
15703
15704     // private
15705     enable : function(){
15706         this.component.enable();
15707         Roo.menu.Adapter.superclass.enable.call(this);
15708     }
15709 });/*
15710  * Based on:
15711  * Ext JS Library 1.1.1
15712  * Copyright(c) 2006-2007, Ext JS, LLC.
15713  *
15714  * Originally Released Under LGPL - original licence link has changed is not relivant.
15715  *
15716  * Fork - LGPL
15717  * <script type="text/javascript">
15718  */
15719
15720 /**
15721  * @class Roo.menu.TextItem
15722  * @extends Roo.menu.BaseItem
15723  * Adds a static text string to a menu, usually used as either a heading or group separator.
15724  * Note: old style constructor with text is still supported.
15725  * 
15726  * @constructor
15727  * Creates a new TextItem
15728  * @param {Object} cfg Configuration
15729  */
15730 Roo.menu.TextItem = function(cfg){
15731     if (typeof(cfg) == 'string') {
15732         this.text = cfg;
15733     } else {
15734         Roo.apply(this,cfg);
15735     }
15736     
15737     Roo.menu.TextItem.superclass.constructor.call(this);
15738 };
15739
15740 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15741     /**
15742      * @cfg {Boolean} text Text to show on item.
15743      */
15744     text : '',
15745     
15746     /**
15747      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15748      */
15749     hideOnClick : false,
15750     /**
15751      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15752      */
15753     itemCls : "x-menu-text",
15754
15755     // private
15756     onRender : function(){
15757         var s = document.createElement("span");
15758         s.className = this.itemCls;
15759         s.innerHTML = this.text;
15760         this.el = s;
15761         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15762     }
15763 });/*
15764  * Based on:
15765  * Ext JS Library 1.1.1
15766  * Copyright(c) 2006-2007, Ext JS, LLC.
15767  *
15768  * Originally Released Under LGPL - original licence link has changed is not relivant.
15769  *
15770  * Fork - LGPL
15771  * <script type="text/javascript">
15772  */
15773
15774 /**
15775  * @class Roo.menu.Separator
15776  * @extends Roo.menu.BaseItem
15777  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15778  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15779  * @constructor
15780  * @param {Object} config Configuration options
15781  */
15782 Roo.menu.Separator = function(config){
15783     Roo.menu.Separator.superclass.constructor.call(this, config);
15784 };
15785
15786 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15787     /**
15788      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15789      */
15790     itemCls : "x-menu-sep",
15791     /**
15792      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15793      */
15794     hideOnClick : false,
15795
15796     // private
15797     onRender : function(li){
15798         var s = document.createElement("span");
15799         s.className = this.itemCls;
15800         s.innerHTML = "&#160;";
15801         this.el = s;
15802         li.addClass("x-menu-sep-li");
15803         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15804     }
15805 });/*
15806  * Based on:
15807  * Ext JS Library 1.1.1
15808  * Copyright(c) 2006-2007, Ext JS, LLC.
15809  *
15810  * Originally Released Under LGPL - original licence link has changed is not relivant.
15811  *
15812  * Fork - LGPL
15813  * <script type="text/javascript">
15814  */
15815 /**
15816  * @class Roo.menu.Item
15817  * @extends Roo.menu.BaseItem
15818  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15819  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15820  * activation and click handling.
15821  * @constructor
15822  * Creates a new Item
15823  * @param {Object} config Configuration options
15824  */
15825 Roo.menu.Item = function(config){
15826     Roo.menu.Item.superclass.constructor.call(this, config);
15827     if(this.menu){
15828         this.menu = Roo.menu.MenuMgr.get(this.menu);
15829     }
15830 };
15831 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15832     
15833     /**
15834      * @cfg {String} text
15835      * The text to show on the menu item.
15836      */
15837     text: '',
15838      /**
15839      * @cfg {String} HTML to render in menu
15840      * The text to show on the menu item (HTML version).
15841      */
15842     html: '',
15843     /**
15844      * @cfg {String} icon
15845      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15846      */
15847     icon: undefined,
15848     /**
15849      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15850      */
15851     itemCls : "x-menu-item",
15852     /**
15853      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15854      */
15855     canActivate : true,
15856     /**
15857      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15858      */
15859     showDelay: 200,
15860     // doc'd in BaseItem
15861     hideDelay: 200,
15862
15863     // private
15864     ctype: "Roo.menu.Item",
15865     
15866     // private
15867     onRender : function(container, position){
15868         var el = document.createElement("a");
15869         el.hideFocus = true;
15870         el.unselectable = "on";
15871         el.href = this.href || "#";
15872         if(this.hrefTarget){
15873             el.target = this.hrefTarget;
15874         }
15875         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15876         
15877         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15878         
15879         el.innerHTML = String.format(
15880                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15881                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15882         this.el = el;
15883         Roo.menu.Item.superclass.onRender.call(this, container, position);
15884     },
15885
15886     /**
15887      * Sets the text to display in this menu item
15888      * @param {String} text The text to display
15889      * @param {Boolean} isHTML true to indicate text is pure html.
15890      */
15891     setText : function(text, isHTML){
15892         if (isHTML) {
15893             this.html = text;
15894         } else {
15895             this.text = text;
15896             this.html = '';
15897         }
15898         if(this.rendered){
15899             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15900      
15901             this.el.update(String.format(
15902                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15903                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15904             this.parentMenu.autoWidth();
15905         }
15906     },
15907
15908     // private
15909     handleClick : function(e){
15910         if(!this.href){ // if no link defined, stop the event automatically
15911             e.stopEvent();
15912         }
15913         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15914     },
15915
15916     // private
15917     activate : function(autoExpand){
15918         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15919             this.focus();
15920             if(autoExpand){
15921                 this.expandMenu();
15922             }
15923         }
15924         return true;
15925     },
15926
15927     // private
15928     shouldDeactivate : function(e){
15929         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15930             if(this.menu && this.menu.isVisible()){
15931                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15932             }
15933             return true;
15934         }
15935         return false;
15936     },
15937
15938     // private
15939     deactivate : function(){
15940         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15941         this.hideMenu();
15942     },
15943
15944     // private
15945     expandMenu : function(autoActivate){
15946         if(!this.disabled && this.menu){
15947             clearTimeout(this.hideTimer);
15948             delete this.hideTimer;
15949             if(!this.menu.isVisible() && !this.showTimer){
15950                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15951             }else if (this.menu.isVisible() && autoActivate){
15952                 this.menu.tryActivate(0, 1);
15953             }
15954         }
15955     },
15956
15957     // private
15958     deferExpand : function(autoActivate){
15959         delete this.showTimer;
15960         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15961         if(autoActivate){
15962             this.menu.tryActivate(0, 1);
15963         }
15964     },
15965
15966     // private
15967     hideMenu : function(){
15968         clearTimeout(this.showTimer);
15969         delete this.showTimer;
15970         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15971             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15972         }
15973     },
15974
15975     // private
15976     deferHide : function(){
15977         delete this.hideTimer;
15978         this.menu.hide();
15979     }
15980 });/*
15981  * Based on:
15982  * Ext JS Library 1.1.1
15983  * Copyright(c) 2006-2007, Ext JS, LLC.
15984  *
15985  * Originally Released Under LGPL - original licence link has changed is not relivant.
15986  *
15987  * Fork - LGPL
15988  * <script type="text/javascript">
15989  */
15990  
15991 /**
15992  * @class Roo.menu.CheckItem
15993  * @extends Roo.menu.Item
15994  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15995  * @constructor
15996  * Creates a new CheckItem
15997  * @param {Object} config Configuration options
15998  */
15999 Roo.menu.CheckItem = function(config){
16000     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16001     this.addEvents({
16002         /**
16003          * @event beforecheckchange
16004          * Fires before the checked value is set, providing an opportunity to cancel if needed
16005          * @param {Roo.menu.CheckItem} this
16006          * @param {Boolean} checked The new checked value that will be set
16007          */
16008         "beforecheckchange" : true,
16009         /**
16010          * @event checkchange
16011          * Fires after the checked value has been set
16012          * @param {Roo.menu.CheckItem} this
16013          * @param {Boolean} checked The checked value that was set
16014          */
16015         "checkchange" : true
16016     });
16017     if(this.checkHandler){
16018         this.on('checkchange', this.checkHandler, this.scope);
16019     }
16020 };
16021 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16022     /**
16023      * @cfg {String} group
16024      * All check items with the same group name will automatically be grouped into a single-select
16025      * radio button group (defaults to '')
16026      */
16027     /**
16028      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16029      */
16030     itemCls : "x-menu-item x-menu-check-item",
16031     /**
16032      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16033      */
16034     groupClass : "x-menu-group-item",
16035
16036     /**
16037      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16038      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16039      * initialized with checked = true will be rendered as checked.
16040      */
16041     checked: false,
16042
16043     // private
16044     ctype: "Roo.menu.CheckItem",
16045
16046     // private
16047     onRender : function(c){
16048         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16049         if(this.group){
16050             this.el.addClass(this.groupClass);
16051         }
16052         Roo.menu.MenuMgr.registerCheckable(this);
16053         if(this.checked){
16054             this.checked = false;
16055             this.setChecked(true, true);
16056         }
16057     },
16058
16059     // private
16060     destroy : function(){
16061         if(this.rendered){
16062             Roo.menu.MenuMgr.unregisterCheckable(this);
16063         }
16064         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16065     },
16066
16067     /**
16068      * Set the checked state of this item
16069      * @param {Boolean} checked The new checked value
16070      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16071      */
16072     setChecked : function(state, suppressEvent){
16073         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16074             if(this.container){
16075                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16076             }
16077             this.checked = state;
16078             if(suppressEvent !== true){
16079                 this.fireEvent("checkchange", this, state);
16080             }
16081         }
16082     },
16083
16084     // private
16085     handleClick : function(e){
16086        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16087            this.setChecked(!this.checked);
16088        }
16089        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16090     }
16091 });/*
16092  * Based on:
16093  * Ext JS Library 1.1.1
16094  * Copyright(c) 2006-2007, Ext JS, LLC.
16095  *
16096  * Originally Released Under LGPL - original licence link has changed is not relivant.
16097  *
16098  * Fork - LGPL
16099  * <script type="text/javascript">
16100  */
16101  
16102 /**
16103  * @class Roo.menu.DateItem
16104  * @extends Roo.menu.Adapter
16105  * A menu item that wraps the {@link Roo.DatPicker} component.
16106  * @constructor
16107  * Creates a new DateItem
16108  * @param {Object} config Configuration options
16109  */
16110 Roo.menu.DateItem = function(config){
16111     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16112     /** The Roo.DatePicker object @type Roo.DatePicker */
16113     this.picker = this.component;
16114     this.addEvents({select: true});
16115     
16116     this.picker.on("render", function(picker){
16117         picker.getEl().swallowEvent("click");
16118         picker.container.addClass("x-menu-date-item");
16119     });
16120
16121     this.picker.on("select", this.onSelect, this);
16122 };
16123
16124 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16125     // private
16126     onSelect : function(picker, date){
16127         this.fireEvent("select", this, date, picker);
16128         Roo.menu.DateItem.superclass.handleClick.call(this);
16129     }
16130 });/*
16131  * Based on:
16132  * Ext JS Library 1.1.1
16133  * Copyright(c) 2006-2007, Ext JS, LLC.
16134  *
16135  * Originally Released Under LGPL - original licence link has changed is not relivant.
16136  *
16137  * Fork - LGPL
16138  * <script type="text/javascript">
16139  */
16140  
16141 /**
16142  * @class Roo.menu.ColorItem
16143  * @extends Roo.menu.Adapter
16144  * A menu item that wraps the {@link Roo.ColorPalette} component.
16145  * @constructor
16146  * Creates a new ColorItem
16147  * @param {Object} config Configuration options
16148  */
16149 Roo.menu.ColorItem = function(config){
16150     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16151     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16152     this.palette = this.component;
16153     this.relayEvents(this.palette, ["select"]);
16154     if(this.selectHandler){
16155         this.on('select', this.selectHandler, this.scope);
16156     }
16157 };
16158 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16159  * Based on:
16160  * Ext JS Library 1.1.1
16161  * Copyright(c) 2006-2007, Ext JS, LLC.
16162  *
16163  * Originally Released Under LGPL - original licence link has changed is not relivant.
16164  *
16165  * Fork - LGPL
16166  * <script type="text/javascript">
16167  */
16168  
16169
16170 /**
16171  * @class Roo.menu.DateMenu
16172  * @extends Roo.menu.Menu
16173  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16174  * @constructor
16175  * Creates a new DateMenu
16176  * @param {Object} config Configuration options
16177  */
16178 Roo.menu.DateMenu = function(config){
16179     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16180     this.plain = true;
16181     var di = new Roo.menu.DateItem(config);
16182     this.add(di);
16183     /**
16184      * The {@link Roo.DatePicker} instance for this DateMenu
16185      * @type DatePicker
16186      */
16187     this.picker = di.picker;
16188     /**
16189      * @event select
16190      * @param {DatePicker} picker
16191      * @param {Date} date
16192      */
16193     this.relayEvents(di, ["select"]);
16194     this.on('beforeshow', function(){
16195         if(this.picker){
16196             this.picker.hideMonthPicker(false);
16197         }
16198     }, this);
16199 };
16200 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16201     cls:'x-date-menu'
16202 });/*
16203  * Based on:
16204  * Ext JS Library 1.1.1
16205  * Copyright(c) 2006-2007, Ext JS, LLC.
16206  *
16207  * Originally Released Under LGPL - original licence link has changed is not relivant.
16208  *
16209  * Fork - LGPL
16210  * <script type="text/javascript">
16211  */
16212  
16213
16214 /**
16215  * @class Roo.menu.ColorMenu
16216  * @extends Roo.menu.Menu
16217  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16218  * @constructor
16219  * Creates a new ColorMenu
16220  * @param {Object} config Configuration options
16221  */
16222 Roo.menu.ColorMenu = function(config){
16223     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16224     this.plain = true;
16225     var ci = new Roo.menu.ColorItem(config);
16226     this.add(ci);
16227     /**
16228      * The {@link Roo.ColorPalette} instance for this ColorMenu
16229      * @type ColorPalette
16230      */
16231     this.palette = ci.palette;
16232     /**
16233      * @event select
16234      * @param {ColorPalette} palette
16235      * @param {String} color
16236      */
16237     this.relayEvents(ci, ["select"]);
16238 };
16239 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16240  * Based on:
16241  * Ext JS Library 1.1.1
16242  * Copyright(c) 2006-2007, Ext JS, LLC.
16243  *
16244  * Originally Released Under LGPL - original licence link has changed is not relivant.
16245  *
16246  * Fork - LGPL
16247  * <script type="text/javascript">
16248  */
16249  
16250 /**
16251  * @class Roo.form.Field
16252  * @extends Roo.BoxComponent
16253  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16254  * @constructor
16255  * Creates a new Field
16256  * @param {Object} config Configuration options
16257  */
16258 Roo.form.Field = function(config){
16259     Roo.form.Field.superclass.constructor.call(this, config);
16260 };
16261
16262 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16263     /**
16264      * @cfg {String} fieldLabel Label to use when rendering a form.
16265      */
16266        /**
16267      * @cfg {String} qtip Mouse over tip
16268      */
16269      
16270     /**
16271      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16272      */
16273     invalidClass : "x-form-invalid",
16274     /**
16275      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
16276      */
16277     invalidText : "The value in this field is invalid",
16278     /**
16279      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16280      */
16281     focusClass : "x-form-focus",
16282     /**
16283      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16284       automatic validation (defaults to "keyup").
16285      */
16286     validationEvent : "keyup",
16287     /**
16288      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16289      */
16290     validateOnBlur : true,
16291     /**
16292      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16293      */
16294     validationDelay : 250,
16295     /**
16296      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16297      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16298      */
16299     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16300     /**
16301      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16302      */
16303     fieldClass : "x-form-field",
16304     /**
16305      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16306      *<pre>
16307 Value         Description
16308 -----------   ----------------------------------------------------------------------
16309 qtip          Display a quick tip when the user hovers over the field
16310 title         Display a default browser title attribute popup
16311 under         Add a block div beneath the field containing the error text
16312 side          Add an error icon to the right of the field with a popup on hover
16313 [element id]  Add the error text directly to the innerHTML of the specified element
16314 </pre>
16315      */
16316     msgTarget : 'qtip',
16317     /**
16318      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16319      */
16320     msgFx : 'normal',
16321
16322     /**
16323      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
16324      */
16325     readOnly : false,
16326
16327     /**
16328      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16329      */
16330     disabled : false,
16331
16332     /**
16333      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16334      */
16335     inputType : undefined,
16336     
16337     /**
16338      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
16339          */
16340         tabIndex : undefined,
16341         
16342     // private
16343     isFormField : true,
16344
16345     // private
16346     hasFocus : false,
16347     /**
16348      * @property {Roo.Element} fieldEl
16349      * Element Containing the rendered Field (with label etc.)
16350      */
16351     /**
16352      * @cfg {Mixed} value A value to initialize this field with.
16353      */
16354     value : undefined,
16355
16356     /**
16357      * @cfg {String} name The field's HTML name attribute.
16358      */
16359     /**
16360      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16361      */
16362     // private
16363     loadedValue : false,
16364      
16365      
16366         // private ??
16367         initComponent : function(){
16368         Roo.form.Field.superclass.initComponent.call(this);
16369         this.addEvents({
16370             /**
16371              * @event focus
16372              * Fires when this field receives input focus.
16373              * @param {Roo.form.Field} this
16374              */
16375             focus : true,
16376             /**
16377              * @event blur
16378              * Fires when this field loses input focus.
16379              * @param {Roo.form.Field} this
16380              */
16381             blur : true,
16382             /**
16383              * @event specialkey
16384              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16385              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16386              * @param {Roo.form.Field} this
16387              * @param {Roo.EventObject} e The event object
16388              */
16389             specialkey : true,
16390             /**
16391              * @event change
16392              * Fires just before the field blurs if the field value has changed.
16393              * @param {Roo.form.Field} this
16394              * @param {Mixed} newValue The new value
16395              * @param {Mixed} oldValue The original value
16396              */
16397             change : true,
16398             /**
16399              * @event invalid
16400              * Fires after the field has been marked as invalid.
16401              * @param {Roo.form.Field} this
16402              * @param {String} msg The validation message
16403              */
16404             invalid : true,
16405             /**
16406              * @event valid
16407              * Fires after the field has been validated with no errors.
16408              * @param {Roo.form.Field} this
16409              */
16410             valid : true,
16411              /**
16412              * @event keyup
16413              * Fires after the key up
16414              * @param {Roo.form.Field} this
16415              * @param {Roo.EventObject}  e The event Object
16416              */
16417             keyup : true
16418         });
16419     },
16420
16421     /**
16422      * Returns the name attribute of the field if available
16423      * @return {String} name The field name
16424      */
16425     getName: function(){
16426          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16427     },
16428
16429     // private
16430     onRender : function(ct, position){
16431         Roo.form.Field.superclass.onRender.call(this, ct, position);
16432         if(!this.el){
16433             var cfg = this.getAutoCreate();
16434             if(!cfg.name){
16435                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16436             }
16437             if (!cfg.name.length) {
16438                 delete cfg.name;
16439             }
16440             if(this.inputType){
16441                 cfg.type = this.inputType;
16442             }
16443             this.el = ct.createChild(cfg, position);
16444         }
16445         var type = this.el.dom.type;
16446         if(type){
16447             if(type == 'password'){
16448                 type = 'text';
16449             }
16450             this.el.addClass('x-form-'+type);
16451         }
16452         if(this.readOnly){
16453             this.el.dom.readOnly = true;
16454         }
16455         if(this.tabIndex !== undefined){
16456             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16457         }
16458
16459         this.el.addClass([this.fieldClass, this.cls]);
16460         this.initValue();
16461     },
16462
16463     /**
16464      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16465      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16466      * @return {Roo.form.Field} this
16467      */
16468     applyTo : function(target){
16469         this.allowDomMove = false;
16470         this.el = Roo.get(target);
16471         this.render(this.el.dom.parentNode);
16472         return this;
16473     },
16474
16475     // private
16476     initValue : function(){
16477         if(this.value !== undefined){
16478             this.setValue(this.value);
16479         }else if(this.el.dom.value.length > 0){
16480             this.setValue(this.el.dom.value);
16481         }
16482     },
16483
16484     /**
16485      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16486      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16487      */
16488     isDirty : function() {
16489         if(this.disabled) {
16490             return false;
16491         }
16492         return String(this.getValue()) !== String(this.originalValue);
16493     },
16494
16495     /**
16496      * stores the current value in loadedValue
16497      */
16498     resetHasChanged : function()
16499     {
16500         this.loadedValue = String(this.getValue());
16501     },
16502     /**
16503      * checks the current value against the 'loaded' value.
16504      * Note - will return false if 'resetHasChanged' has not been called first.
16505      */
16506     hasChanged : function()
16507     {
16508         if(this.disabled || this.readOnly) {
16509             return false;
16510         }
16511         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16512     },
16513     
16514     
16515     
16516     // private
16517     afterRender : function(){
16518         Roo.form.Field.superclass.afterRender.call(this);
16519         this.initEvents();
16520     },
16521
16522     // private
16523     fireKey : function(e){
16524         //Roo.log('field ' + e.getKey());
16525         if(e.isNavKeyPress()){
16526             this.fireEvent("specialkey", this, e);
16527         }
16528     },
16529
16530     /**
16531      * Resets the current field value to the originally loaded value and clears any validation messages
16532      */
16533     reset : function(){
16534         this.setValue(this.resetValue);
16535         this.originalValue = this.getValue();
16536         this.clearInvalid();
16537     },
16538
16539     // private
16540     initEvents : function(){
16541         // safari killled keypress - so keydown is now used..
16542         this.el.on("keydown" , this.fireKey,  this);
16543         this.el.on("focus", this.onFocus,  this);
16544         this.el.on("blur", this.onBlur,  this);
16545         this.el.relayEvent('keyup', this);
16546
16547         // reference to original value for reset
16548         this.originalValue = this.getValue();
16549         this.resetValue =  this.getValue();
16550     },
16551
16552     // private
16553     onFocus : function(){
16554         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16555             this.el.addClass(this.focusClass);
16556         }
16557         if(!this.hasFocus){
16558             this.hasFocus = true;
16559             this.startValue = this.getValue();
16560             this.fireEvent("focus", this);
16561         }
16562     },
16563
16564     beforeBlur : Roo.emptyFn,
16565
16566     // private
16567     onBlur : function(){
16568         this.beforeBlur();
16569         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16570             this.el.removeClass(this.focusClass);
16571         }
16572         this.hasFocus = false;
16573         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16574             this.validate();
16575         }
16576         var v = this.getValue();
16577         if(String(v) !== String(this.startValue)){
16578             this.fireEvent('change', this, v, this.startValue);
16579         }
16580         this.fireEvent("blur", this);
16581     },
16582
16583     /**
16584      * Returns whether or not the field value is currently valid
16585      * @param {Boolean} preventMark True to disable marking the field invalid
16586      * @return {Boolean} True if the value is valid, else false
16587      */
16588     isValid : function(preventMark){
16589         if(this.disabled){
16590             return true;
16591         }
16592         var restore = this.preventMark;
16593         this.preventMark = preventMark === true;
16594         var v = this.validateValue(this.processValue(this.getRawValue()));
16595         this.preventMark = restore;
16596         return v;
16597     },
16598
16599     /**
16600      * Validates the field value
16601      * @return {Boolean} True if the value is valid, else false
16602      */
16603     validate : function(){
16604         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16605             this.clearInvalid();
16606             return true;
16607         }
16608         return false;
16609     },
16610
16611     processValue : function(value){
16612         return value;
16613     },
16614
16615     // private
16616     // Subclasses should provide the validation implementation by overriding this
16617     validateValue : function(value){
16618         return true;
16619     },
16620
16621     /**
16622      * Mark this field as invalid
16623      * @param {String} msg The validation message
16624      */
16625     markInvalid : function(msg){
16626         if(!this.rendered || this.preventMark){ // not rendered
16627             return;
16628         }
16629         
16630         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16631         
16632         obj.el.addClass(this.invalidClass);
16633         msg = msg || this.invalidText;
16634         switch(this.msgTarget){
16635             case 'qtip':
16636                 obj.el.dom.qtip = msg;
16637                 obj.el.dom.qclass = 'x-form-invalid-tip';
16638                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16639                     Roo.QuickTips.enable();
16640                 }
16641                 break;
16642             case 'title':
16643                 this.el.dom.title = msg;
16644                 break;
16645             case 'under':
16646                 if(!this.errorEl){
16647                     var elp = this.el.findParent('.x-form-element', 5, true);
16648                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16649                     this.errorEl.setWidth(elp.getWidth(true)-20);
16650                 }
16651                 this.errorEl.update(msg);
16652                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16653                 break;
16654             case 'side':
16655                 if(!this.errorIcon){
16656                     var elp = this.el.findParent('.x-form-element', 5, true);
16657                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16658                 }
16659                 this.alignErrorIcon();
16660                 this.errorIcon.dom.qtip = msg;
16661                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16662                 this.errorIcon.show();
16663                 this.on('resize', this.alignErrorIcon, this);
16664                 break;
16665             default:
16666                 var t = Roo.getDom(this.msgTarget);
16667                 t.innerHTML = msg;
16668                 t.style.display = this.msgDisplay;
16669                 break;
16670         }
16671         this.fireEvent('invalid', this, msg);
16672     },
16673
16674     // private
16675     alignErrorIcon : function(){
16676         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16677     },
16678
16679     /**
16680      * Clear any invalid styles/messages for this field
16681      */
16682     clearInvalid : function(){
16683         if(!this.rendered || this.preventMark){ // not rendered
16684             return;
16685         }
16686         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16687         
16688         obj.el.removeClass(this.invalidClass);
16689         switch(this.msgTarget){
16690             case 'qtip':
16691                 obj.el.dom.qtip = '';
16692                 break;
16693             case 'title':
16694                 this.el.dom.title = '';
16695                 break;
16696             case 'under':
16697                 if(this.errorEl){
16698                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16699                 }
16700                 break;
16701             case 'side':
16702                 if(this.errorIcon){
16703                     this.errorIcon.dom.qtip = '';
16704                     this.errorIcon.hide();
16705                     this.un('resize', this.alignErrorIcon, this);
16706                 }
16707                 break;
16708             default:
16709                 var t = Roo.getDom(this.msgTarget);
16710                 t.innerHTML = '';
16711                 t.style.display = 'none';
16712                 break;
16713         }
16714         this.fireEvent('valid', this);
16715     },
16716
16717     /**
16718      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16719      * @return {Mixed} value The field value
16720      */
16721     getRawValue : function(){
16722         var v = this.el.getValue();
16723         
16724         return v;
16725     },
16726
16727     /**
16728      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16729      * @return {Mixed} value The field value
16730      */
16731     getValue : function(){
16732         var v = this.el.getValue();
16733          
16734         return v;
16735     },
16736
16737     /**
16738      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16739      * @param {Mixed} value The value to set
16740      */
16741     setRawValue : function(v){
16742         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16743     },
16744
16745     /**
16746      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16747      * @param {Mixed} value The value to set
16748      */
16749     setValue : function(v){
16750         this.value = v;
16751         if(this.rendered){
16752             this.el.dom.value = (v === null || v === undefined ? '' : v);
16753              this.validate();
16754         }
16755     },
16756
16757     adjustSize : function(w, h){
16758         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16759         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16760         return s;
16761     },
16762
16763     adjustWidth : function(tag, w){
16764         tag = tag.toLowerCase();
16765         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16766             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16767                 if(tag == 'input'){
16768                     return w + 2;
16769                 }
16770                 if(tag == 'textarea'){
16771                     return w-2;
16772                 }
16773             }else if(Roo.isOpera){
16774                 if(tag == 'input'){
16775                     return w + 2;
16776                 }
16777                 if(tag == 'textarea'){
16778                     return w-2;
16779                 }
16780             }
16781         }
16782         return w;
16783     }
16784 });
16785
16786
16787 // anything other than normal should be considered experimental
16788 Roo.form.Field.msgFx = {
16789     normal : {
16790         show: function(msgEl, f){
16791             msgEl.setDisplayed('block');
16792         },
16793
16794         hide : function(msgEl, f){
16795             msgEl.setDisplayed(false).update('');
16796         }
16797     },
16798
16799     slide : {
16800         show: function(msgEl, f){
16801             msgEl.slideIn('t', {stopFx:true});
16802         },
16803
16804         hide : function(msgEl, f){
16805             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16806         }
16807     },
16808
16809     slideRight : {
16810         show: function(msgEl, f){
16811             msgEl.fixDisplay();
16812             msgEl.alignTo(f.el, 'tl-tr');
16813             msgEl.slideIn('l', {stopFx:true});
16814         },
16815
16816         hide : function(msgEl, f){
16817             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16818         }
16819     }
16820 };/*
16821  * Based on:
16822  * Ext JS Library 1.1.1
16823  * Copyright(c) 2006-2007, Ext JS, LLC.
16824  *
16825  * Originally Released Under LGPL - original licence link has changed is not relivant.
16826  *
16827  * Fork - LGPL
16828  * <script type="text/javascript">
16829  */
16830  
16831
16832 /**
16833  * @class Roo.form.TextField
16834  * @extends Roo.form.Field
16835  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16836  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16837  * @constructor
16838  * Creates a new TextField
16839  * @param {Object} config Configuration options
16840  */
16841 Roo.form.TextField = function(config){
16842     Roo.form.TextField.superclass.constructor.call(this, config);
16843     this.addEvents({
16844         /**
16845          * @event autosize
16846          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16847          * according to the default logic, but this event provides a hook for the developer to apply additional
16848          * logic at runtime to resize the field if needed.
16849              * @param {Roo.form.Field} this This text field
16850              * @param {Number} width The new field width
16851              */
16852         autosize : true
16853     });
16854 };
16855
16856 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16857     /**
16858      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16859      */
16860     grow : false,
16861     /**
16862      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16863      */
16864     growMin : 30,
16865     /**
16866      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16867      */
16868     growMax : 800,
16869     /**
16870      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16871      */
16872     vtype : null,
16873     /**
16874      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16875      */
16876     maskRe : null,
16877     /**
16878      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16879      */
16880     disableKeyFilter : false,
16881     /**
16882      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16883      */
16884     allowBlank : true,
16885     /**
16886      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16887      */
16888     minLength : 0,
16889     /**
16890      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16891      */
16892     maxLength : Number.MAX_VALUE,
16893     /**
16894      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16895      */
16896     minLengthText : "The minimum length for this field is {0}",
16897     /**
16898      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16899      */
16900     maxLengthText : "The maximum length for this field is {0}",
16901     /**
16902      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16903      */
16904     selectOnFocus : false,
16905     /**
16906      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16907      */
16908     blankText : "This field is required",
16909     /**
16910      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16911      * If available, this function will be called only after the basic validators all return true, and will be passed the
16912      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16913      */
16914     validator : null,
16915     /**
16916      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16917      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16918      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16919      */
16920     regex : null,
16921     /**
16922      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16923      */
16924     regexText : "",
16925     /**
16926      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16927      */
16928     emptyText : null,
16929    
16930
16931     // private
16932     initEvents : function()
16933     {
16934         if (this.emptyText) {
16935             this.el.attr('placeholder', this.emptyText);
16936         }
16937         
16938         Roo.form.TextField.superclass.initEvents.call(this);
16939         if(this.validationEvent == 'keyup'){
16940             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16941             this.el.on('keyup', this.filterValidation, this);
16942         }
16943         else if(this.validationEvent !== false){
16944             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16945         }
16946         
16947         if(this.selectOnFocus){
16948             this.on("focus", this.preFocus, this);
16949             
16950         }
16951         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16952             this.el.on("keypress", this.filterKeys, this);
16953         }
16954         if(this.grow){
16955             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16956             this.el.on("click", this.autoSize,  this);
16957         }
16958         if(this.el.is('input[type=password]') && Roo.isSafari){
16959             this.el.on('keydown', this.SafariOnKeyDown, this);
16960         }
16961     },
16962
16963     processValue : function(value){
16964         if(this.stripCharsRe){
16965             var newValue = value.replace(this.stripCharsRe, '');
16966             if(newValue !== value){
16967                 this.setRawValue(newValue);
16968                 return newValue;
16969             }
16970         }
16971         return value;
16972     },
16973
16974     filterValidation : function(e){
16975         if(!e.isNavKeyPress()){
16976             this.validationTask.delay(this.validationDelay);
16977         }
16978     },
16979
16980     // private
16981     onKeyUp : function(e){
16982         if(!e.isNavKeyPress()){
16983             this.autoSize();
16984         }
16985     },
16986
16987     /**
16988      * Resets the current field value to the originally-loaded value and clears any validation messages.
16989      *  
16990      */
16991     reset : function(){
16992         Roo.form.TextField.superclass.reset.call(this);
16993        
16994     },
16995
16996     
16997     // private
16998     preFocus : function(){
16999         
17000         if(this.selectOnFocus){
17001             this.el.dom.select();
17002         }
17003     },
17004
17005     
17006     // private
17007     filterKeys : function(e){
17008         var k = e.getKey();
17009         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17010             return;
17011         }
17012         var c = e.getCharCode(), cc = String.fromCharCode(c);
17013         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17014             return;
17015         }
17016         if(!this.maskRe.test(cc)){
17017             e.stopEvent();
17018         }
17019     },
17020
17021     setValue : function(v){
17022         
17023         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17024         
17025         this.autoSize();
17026     },
17027
17028     /**
17029      * Validates a value according to the field's validation rules and marks the field as invalid
17030      * if the validation fails
17031      * @param {Mixed} value The value to validate
17032      * @return {Boolean} True if the value is valid, else false
17033      */
17034     validateValue : function(value){
17035         if(value.length < 1)  { // if it's blank
17036              if(this.allowBlank){
17037                 this.clearInvalid();
17038                 return true;
17039              }else{
17040                 this.markInvalid(this.blankText);
17041                 return false;
17042              }
17043         }
17044         if(value.length < this.minLength){
17045             this.markInvalid(String.format(this.minLengthText, this.minLength));
17046             return false;
17047         }
17048         if(value.length > this.maxLength){
17049             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17050             return false;
17051         }
17052         if(this.vtype){
17053             var vt = Roo.form.VTypes;
17054             if(!vt[this.vtype](value, this)){
17055                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17056                 return false;
17057             }
17058         }
17059         if(typeof this.validator == "function"){
17060             var msg = this.validator(value);
17061             if(msg !== true){
17062                 this.markInvalid(msg);
17063                 return false;
17064             }
17065         }
17066         if(this.regex && !this.regex.test(value)){
17067             this.markInvalid(this.regexText);
17068             return false;
17069         }
17070         return true;
17071     },
17072
17073     /**
17074      * Selects text in this field
17075      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17076      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17077      */
17078     selectText : function(start, end){
17079         var v = this.getRawValue();
17080         if(v.length > 0){
17081             start = start === undefined ? 0 : start;
17082             end = end === undefined ? v.length : end;
17083             var d = this.el.dom;
17084             if(d.setSelectionRange){
17085                 d.setSelectionRange(start, end);
17086             }else if(d.createTextRange){
17087                 var range = d.createTextRange();
17088                 range.moveStart("character", start);
17089                 range.moveEnd("character", v.length-end);
17090                 range.select();
17091             }
17092         }
17093     },
17094
17095     /**
17096      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17097      * This only takes effect if grow = true, and fires the autosize event.
17098      */
17099     autoSize : function(){
17100         if(!this.grow || !this.rendered){
17101             return;
17102         }
17103         if(!this.metrics){
17104             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17105         }
17106         var el = this.el;
17107         var v = el.dom.value;
17108         var d = document.createElement('div');
17109         d.appendChild(document.createTextNode(v));
17110         v = d.innerHTML;
17111         d = null;
17112         v += "&#160;";
17113         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17114         this.el.setWidth(w);
17115         this.fireEvent("autosize", this, w);
17116     },
17117     
17118     // private
17119     SafariOnKeyDown : function(event)
17120     {
17121         // this is a workaround for a password hang bug on chrome/ webkit.
17122         
17123         var isSelectAll = false;
17124         
17125         if(this.el.dom.selectionEnd > 0){
17126             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17127         }
17128         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17129             event.preventDefault();
17130             this.setValue('');
17131             return;
17132         }
17133         
17134         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17135             
17136             event.preventDefault();
17137             // this is very hacky as keydown always get's upper case.
17138             
17139             var cc = String.fromCharCode(event.getCharCode());
17140             
17141             
17142             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17143             
17144         }
17145         
17146         
17147     }
17148 });/*
17149  * Based on:
17150  * Ext JS Library 1.1.1
17151  * Copyright(c) 2006-2007, Ext JS, LLC.
17152  *
17153  * Originally Released Under LGPL - original licence link has changed is not relivant.
17154  *
17155  * Fork - LGPL
17156  * <script type="text/javascript">
17157  */
17158  
17159 /**
17160  * @class Roo.form.Hidden
17161  * @extends Roo.form.TextField
17162  * Simple Hidden element used on forms 
17163  * 
17164  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17165  * 
17166  * @constructor
17167  * Creates a new Hidden form element.
17168  * @param {Object} config Configuration options
17169  */
17170
17171
17172
17173 // easy hidden field...
17174 Roo.form.Hidden = function(config){
17175     Roo.form.Hidden.superclass.constructor.call(this, config);
17176 };
17177   
17178 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17179     fieldLabel:      '',
17180     inputType:      'hidden',
17181     width:          50,
17182     allowBlank:     true,
17183     labelSeparator: '',
17184     hidden:         true,
17185     itemCls :       'x-form-item-display-none'
17186
17187
17188 });
17189
17190
17191 /*
17192  * Based on:
17193  * Ext JS Library 1.1.1
17194  * Copyright(c) 2006-2007, Ext JS, LLC.
17195  *
17196  * Originally Released Under LGPL - original licence link has changed is not relivant.
17197  *
17198  * Fork - LGPL
17199  * <script type="text/javascript">
17200  */
17201  
17202 /**
17203  * @class Roo.form.TriggerField
17204  * @extends Roo.form.TextField
17205  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17206  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17207  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17208  * for which you can provide a custom implementation.  For example:
17209  * <pre><code>
17210 var trigger = new Roo.form.TriggerField();
17211 trigger.onTriggerClick = myTriggerFn;
17212 trigger.applyTo('my-field');
17213 </code></pre>
17214  *
17215  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17216  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17217  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17218  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17219  * @constructor
17220  * Create a new TriggerField.
17221  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17222  * to the base TextField)
17223  */
17224 Roo.form.TriggerField = function(config){
17225     this.mimicing = false;
17226     Roo.form.TriggerField.superclass.constructor.call(this, config);
17227 };
17228
17229 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17230     /**
17231      * @cfg {String} triggerClass A CSS class to apply to the trigger
17232      */
17233     /**
17234      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17235      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17236      */
17237     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17238     /**
17239      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17240      */
17241     hideTrigger:false,
17242
17243     /** @cfg {Boolean} grow @hide */
17244     /** @cfg {Number} growMin @hide */
17245     /** @cfg {Number} growMax @hide */
17246
17247     /**
17248      * @hide 
17249      * @method
17250      */
17251     autoSize: Roo.emptyFn,
17252     // private
17253     monitorTab : true,
17254     // private
17255     deferHeight : true,
17256
17257     
17258     actionMode : 'wrap',
17259     // private
17260     onResize : function(w, h){
17261         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17262         if(typeof w == 'number'){
17263             var x = w - this.trigger.getWidth();
17264             this.el.setWidth(this.adjustWidth('input', x));
17265             this.trigger.setStyle('left', x+'px');
17266         }
17267     },
17268
17269     // private
17270     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17271
17272     // private
17273     getResizeEl : function(){
17274         return this.wrap;
17275     },
17276
17277     // private
17278     getPositionEl : function(){
17279         return this.wrap;
17280     },
17281
17282     // private
17283     alignErrorIcon : function(){
17284         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17285     },
17286
17287     // private
17288     onRender : function(ct, position){
17289         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17290         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17291         this.trigger = this.wrap.createChild(this.triggerConfig ||
17292                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17293         if(this.hideTrigger){
17294             this.trigger.setDisplayed(false);
17295         }
17296         this.initTrigger();
17297         if(!this.width){
17298             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17299         }
17300     },
17301
17302     // private
17303     initTrigger : function(){
17304         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17305         this.trigger.addClassOnOver('x-form-trigger-over');
17306         this.trigger.addClassOnClick('x-form-trigger-click');
17307     },
17308
17309     // private
17310     onDestroy : function(){
17311         if(this.trigger){
17312             this.trigger.removeAllListeners();
17313             this.trigger.remove();
17314         }
17315         if(this.wrap){
17316             this.wrap.remove();
17317         }
17318         Roo.form.TriggerField.superclass.onDestroy.call(this);
17319     },
17320
17321     // private
17322     onFocus : function(){
17323         Roo.form.TriggerField.superclass.onFocus.call(this);
17324         if(!this.mimicing){
17325             this.wrap.addClass('x-trigger-wrap-focus');
17326             this.mimicing = true;
17327             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17328             if(this.monitorTab){
17329                 this.el.on("keydown", this.checkTab, this);
17330             }
17331         }
17332     },
17333
17334     // private
17335     checkTab : function(e){
17336         if(e.getKey() == e.TAB){
17337             this.triggerBlur();
17338         }
17339     },
17340
17341     // private
17342     onBlur : function(){
17343         // do nothing
17344     },
17345
17346     // private
17347     mimicBlur : function(e, t){
17348         if(!this.wrap.contains(t) && this.validateBlur()){
17349             this.triggerBlur();
17350         }
17351     },
17352
17353     // private
17354     triggerBlur : function(){
17355         this.mimicing = false;
17356         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17357         if(this.monitorTab){
17358             this.el.un("keydown", this.checkTab, this);
17359         }
17360         this.wrap.removeClass('x-trigger-wrap-focus');
17361         Roo.form.TriggerField.superclass.onBlur.call(this);
17362     },
17363
17364     // private
17365     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17366     validateBlur : function(e, t){
17367         return true;
17368     },
17369
17370     // private
17371     onDisable : function(){
17372         Roo.form.TriggerField.superclass.onDisable.call(this);
17373         if(this.wrap){
17374             this.wrap.addClass('x-item-disabled');
17375         }
17376     },
17377
17378     // private
17379     onEnable : function(){
17380         Roo.form.TriggerField.superclass.onEnable.call(this);
17381         if(this.wrap){
17382             this.wrap.removeClass('x-item-disabled');
17383         }
17384     },
17385
17386     // private
17387     onShow : function(){
17388         var ae = this.getActionEl();
17389         
17390         if(ae){
17391             ae.dom.style.display = '';
17392             ae.dom.style.visibility = 'visible';
17393         }
17394     },
17395
17396     // private
17397     
17398     onHide : function(){
17399         var ae = this.getActionEl();
17400         ae.dom.style.display = 'none';
17401     },
17402
17403     /**
17404      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17405      * by an implementing function.
17406      * @method
17407      * @param {EventObject} e
17408      */
17409     onTriggerClick : Roo.emptyFn
17410 });
17411
17412 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17413 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17414 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17415 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17416     initComponent : function(){
17417         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17418
17419         this.triggerConfig = {
17420             tag:'span', cls:'x-form-twin-triggers', cn:[
17421             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17422             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17423         ]};
17424     },
17425
17426     getTrigger : function(index){
17427         return this.triggers[index];
17428     },
17429
17430     initTrigger : function(){
17431         var ts = this.trigger.select('.x-form-trigger', true);
17432         this.wrap.setStyle('overflow', 'hidden');
17433         var triggerField = this;
17434         ts.each(function(t, all, index){
17435             t.hide = function(){
17436                 var w = triggerField.wrap.getWidth();
17437                 this.dom.style.display = 'none';
17438                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17439             };
17440             t.show = function(){
17441                 var w = triggerField.wrap.getWidth();
17442                 this.dom.style.display = '';
17443                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17444             };
17445             var triggerIndex = 'Trigger'+(index+1);
17446
17447             if(this['hide'+triggerIndex]){
17448                 t.dom.style.display = 'none';
17449             }
17450             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17451             t.addClassOnOver('x-form-trigger-over');
17452             t.addClassOnClick('x-form-trigger-click');
17453         }, this);
17454         this.triggers = ts.elements;
17455     },
17456
17457     onTrigger1Click : Roo.emptyFn,
17458     onTrigger2Click : Roo.emptyFn
17459 });/*
17460  * Based on:
17461  * Ext JS Library 1.1.1
17462  * Copyright(c) 2006-2007, Ext JS, LLC.
17463  *
17464  * Originally Released Under LGPL - original licence link has changed is not relivant.
17465  *
17466  * Fork - LGPL
17467  * <script type="text/javascript">
17468  */
17469  
17470 /**
17471  * @class Roo.form.TextArea
17472  * @extends Roo.form.TextField
17473  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17474  * support for auto-sizing.
17475  * @constructor
17476  * Creates a new TextArea
17477  * @param {Object} config Configuration options
17478  */
17479 Roo.form.TextArea = function(config){
17480     Roo.form.TextArea.superclass.constructor.call(this, config);
17481     // these are provided exchanges for backwards compat
17482     // minHeight/maxHeight were replaced by growMin/growMax to be
17483     // compatible with TextField growing config values
17484     if(this.minHeight !== undefined){
17485         this.growMin = this.minHeight;
17486     }
17487     if(this.maxHeight !== undefined){
17488         this.growMax = this.maxHeight;
17489     }
17490 };
17491
17492 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17493     /**
17494      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17495      */
17496     growMin : 60,
17497     /**
17498      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17499      */
17500     growMax: 1000,
17501     /**
17502      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17503      * in the field (equivalent to setting overflow: hidden, defaults to false)
17504      */
17505     preventScrollbars: false,
17506     /**
17507      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17508      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17509      */
17510
17511     // private
17512     onRender : function(ct, position){
17513         if(!this.el){
17514             this.defaultAutoCreate = {
17515                 tag: "textarea",
17516                 style:"width:300px;height:60px;",
17517                 autocomplete: "new-password"
17518             };
17519         }
17520         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17521         if(this.grow){
17522             this.textSizeEl = Roo.DomHelper.append(document.body, {
17523                 tag: "pre", cls: "x-form-grow-sizer"
17524             });
17525             if(this.preventScrollbars){
17526                 this.el.setStyle("overflow", "hidden");
17527             }
17528             this.el.setHeight(this.growMin);
17529         }
17530     },
17531
17532     onDestroy : function(){
17533         if(this.textSizeEl){
17534             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17535         }
17536         Roo.form.TextArea.superclass.onDestroy.call(this);
17537     },
17538
17539     // private
17540     onKeyUp : function(e){
17541         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17542             this.autoSize();
17543         }
17544     },
17545
17546     /**
17547      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17548      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17549      */
17550     autoSize : function(){
17551         if(!this.grow || !this.textSizeEl){
17552             return;
17553         }
17554         var el = this.el;
17555         var v = el.dom.value;
17556         var ts = this.textSizeEl;
17557
17558         ts.innerHTML = '';
17559         ts.appendChild(document.createTextNode(v));
17560         v = ts.innerHTML;
17561
17562         Roo.fly(ts).setWidth(this.el.getWidth());
17563         if(v.length < 1){
17564             v = "&#160;&#160;";
17565         }else{
17566             if(Roo.isIE){
17567                 v = v.replace(/\n/g, '<p>&#160;</p>');
17568             }
17569             v += "&#160;\n&#160;";
17570         }
17571         ts.innerHTML = v;
17572         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17573         if(h != this.lastHeight){
17574             this.lastHeight = h;
17575             this.el.setHeight(h);
17576             this.fireEvent("autosize", this, h);
17577         }
17578     }
17579 });/*
17580  * Based on:
17581  * Ext JS Library 1.1.1
17582  * Copyright(c) 2006-2007, Ext JS, LLC.
17583  *
17584  * Originally Released Under LGPL - original licence link has changed is not relivant.
17585  *
17586  * Fork - LGPL
17587  * <script type="text/javascript">
17588  */
17589  
17590
17591 /**
17592  * @class Roo.form.NumberField
17593  * @extends Roo.form.TextField
17594  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17595  * @constructor
17596  * Creates a new NumberField
17597  * @param {Object} config Configuration options
17598  */
17599 Roo.form.NumberField = function(config){
17600     Roo.form.NumberField.superclass.constructor.call(this, config);
17601 };
17602
17603 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17604     /**
17605      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17606      */
17607     fieldClass: "x-form-field x-form-num-field",
17608     /**
17609      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17610      */
17611     allowDecimals : true,
17612     /**
17613      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17614      */
17615     decimalSeparator : ".",
17616     /**
17617      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17618      */
17619     decimalPrecision : 2,
17620     /**
17621      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17622      */
17623     allowNegative : true,
17624     /**
17625      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17626      */
17627     minValue : Number.NEGATIVE_INFINITY,
17628     /**
17629      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17630      */
17631     maxValue : Number.MAX_VALUE,
17632     /**
17633      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17634      */
17635     minText : "The minimum value for this field is {0}",
17636     /**
17637      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17638      */
17639     maxText : "The maximum value for this field is {0}",
17640     /**
17641      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17642      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17643      */
17644     nanText : "{0} is not a valid number",
17645
17646     // private
17647     initEvents : function(){
17648         Roo.form.NumberField.superclass.initEvents.call(this);
17649         var allowed = "0123456789";
17650         if(this.allowDecimals){
17651             allowed += this.decimalSeparator;
17652         }
17653         if(this.allowNegative){
17654             allowed += "-";
17655         }
17656         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17657         var keyPress = function(e){
17658             var k = e.getKey();
17659             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17660                 return;
17661             }
17662             var c = e.getCharCode();
17663             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17664                 e.stopEvent();
17665             }
17666         };
17667         this.el.on("keypress", keyPress, this);
17668     },
17669
17670     // private
17671     validateValue : function(value){
17672         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17673             return false;
17674         }
17675         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17676              return true;
17677         }
17678         var num = this.parseValue(value);
17679         if(isNaN(num)){
17680             this.markInvalid(String.format(this.nanText, value));
17681             return false;
17682         }
17683         if(num < this.minValue){
17684             this.markInvalid(String.format(this.minText, this.minValue));
17685             return false;
17686         }
17687         if(num > this.maxValue){
17688             this.markInvalid(String.format(this.maxText, this.maxValue));
17689             return false;
17690         }
17691         return true;
17692     },
17693
17694     getValue : function(){
17695         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17696     },
17697
17698     // private
17699     parseValue : function(value){
17700         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17701         return isNaN(value) ? '' : value;
17702     },
17703
17704     // private
17705     fixPrecision : function(value){
17706         var nan = isNaN(value);
17707         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17708             return nan ? '' : value;
17709         }
17710         return parseFloat(value).toFixed(this.decimalPrecision);
17711     },
17712
17713     setValue : function(v){
17714         v = this.fixPrecision(v);
17715         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17716     },
17717
17718     // private
17719     decimalPrecisionFcn : function(v){
17720         return Math.floor(v);
17721     },
17722
17723     beforeBlur : function(){
17724         var v = this.parseValue(this.getRawValue());
17725         if(v){
17726             this.setValue(v);
17727         }
17728     }
17729 });/*
17730  * Based on:
17731  * Ext JS Library 1.1.1
17732  * Copyright(c) 2006-2007, Ext JS, LLC.
17733  *
17734  * Originally Released Under LGPL - original licence link has changed is not relivant.
17735  *
17736  * Fork - LGPL
17737  * <script type="text/javascript">
17738  */
17739  
17740 /**
17741  * @class Roo.form.DateField
17742  * @extends Roo.form.TriggerField
17743  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17744 * @constructor
17745 * Create a new DateField
17746 * @param {Object} config
17747  */
17748 Roo.form.DateField = function(config){
17749     Roo.form.DateField.superclass.constructor.call(this, config);
17750     
17751       this.addEvents({
17752          
17753         /**
17754          * @event select
17755          * Fires when a date is selected
17756              * @param {Roo.form.DateField} combo This combo box
17757              * @param {Date} date The date selected
17758              */
17759         'select' : true
17760          
17761     });
17762     
17763     
17764     if(typeof this.minValue == "string") {
17765         this.minValue = this.parseDate(this.minValue);
17766     }
17767     if(typeof this.maxValue == "string") {
17768         this.maxValue = this.parseDate(this.maxValue);
17769     }
17770     this.ddMatch = null;
17771     if(this.disabledDates){
17772         var dd = this.disabledDates;
17773         var re = "(?:";
17774         for(var i = 0; i < dd.length; i++){
17775             re += dd[i];
17776             if(i != dd.length-1) {
17777                 re += "|";
17778             }
17779         }
17780         this.ddMatch = new RegExp(re + ")");
17781     }
17782 };
17783
17784 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17785     /**
17786      * @cfg {String} format
17787      * The default date format string which can be overriden for localization support.  The format must be
17788      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17789      */
17790     format : "m/d/y",
17791     /**
17792      * @cfg {String} altFormats
17793      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17794      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17795      */
17796     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17797     /**
17798      * @cfg {Array} disabledDays
17799      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17800      */
17801     disabledDays : null,
17802     /**
17803      * @cfg {String} disabledDaysText
17804      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17805      */
17806     disabledDaysText : "Disabled",
17807     /**
17808      * @cfg {Array} disabledDates
17809      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17810      * expression so they are very powerful. Some examples:
17811      * <ul>
17812      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17813      * <li>["03/08", "09/16"] would disable those days for every year</li>
17814      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17815      * <li>["03/../2006"] would disable every day in March 2006</li>
17816      * <li>["^03"] would disable every day in every March</li>
17817      * </ul>
17818      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17819      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17820      */
17821     disabledDates : null,
17822     /**
17823      * @cfg {String} disabledDatesText
17824      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17825      */
17826     disabledDatesText : "Disabled",
17827     /**
17828      * @cfg {Date/String} minValue
17829      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17830      * valid format (defaults to null).
17831      */
17832     minValue : null,
17833     /**
17834      * @cfg {Date/String} maxValue
17835      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17836      * valid format (defaults to null).
17837      */
17838     maxValue : null,
17839     /**
17840      * @cfg {String} minText
17841      * The error text to display when the date in the cell is before minValue (defaults to
17842      * 'The date in this field must be after {minValue}').
17843      */
17844     minText : "The date in this field must be equal to or after {0}",
17845     /**
17846      * @cfg {String} maxText
17847      * The error text to display when the date in the cell is after maxValue (defaults to
17848      * 'The date in this field must be before {maxValue}').
17849      */
17850     maxText : "The date in this field must be equal to or before {0}",
17851     /**
17852      * @cfg {String} invalidText
17853      * The error text to display when the date in the field is invalid (defaults to
17854      * '{value} is not a valid date - it must be in the format {format}').
17855      */
17856     invalidText : "{0} is not a valid date - it must be in the format {1}",
17857     /**
17858      * @cfg {String} triggerClass
17859      * An additional CSS class used to style the trigger button.  The trigger will always get the
17860      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17861      * which displays a calendar icon).
17862      */
17863     triggerClass : 'x-form-date-trigger',
17864     
17865
17866     /**
17867      * @cfg {Boolean} useIso
17868      * if enabled, then the date field will use a hidden field to store the 
17869      * real value as iso formated date. default (false)
17870      */ 
17871     useIso : false,
17872     /**
17873      * @cfg {String/Object} autoCreate
17874      * A DomHelper element spec, or true for a default element spec (defaults to
17875      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17876      */ 
17877     // private
17878     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17879     
17880     // private
17881     hiddenField: false,
17882     
17883     onRender : function(ct, position)
17884     {
17885         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17886         if (this.useIso) {
17887             //this.el.dom.removeAttribute('name'); 
17888             Roo.log("Changing name?");
17889             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17890             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17891                     'before', true);
17892             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17893             // prevent input submission
17894             this.hiddenName = this.name;
17895         }
17896             
17897             
17898     },
17899     
17900     // private
17901     validateValue : function(value)
17902     {
17903         value = this.formatDate(value);
17904         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17905             Roo.log('super failed');
17906             return false;
17907         }
17908         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17909              return true;
17910         }
17911         var svalue = value;
17912         value = this.parseDate(value);
17913         if(!value){
17914             Roo.log('parse date failed' + svalue);
17915             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17916             return false;
17917         }
17918         var time = value.getTime();
17919         if(this.minValue && time < this.minValue.getTime()){
17920             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17921             return false;
17922         }
17923         if(this.maxValue && time > this.maxValue.getTime()){
17924             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17925             return false;
17926         }
17927         if(this.disabledDays){
17928             var day = value.getDay();
17929             for(var i = 0; i < this.disabledDays.length; i++) {
17930                 if(day === this.disabledDays[i]){
17931                     this.markInvalid(this.disabledDaysText);
17932                     return false;
17933                 }
17934             }
17935         }
17936         var fvalue = this.formatDate(value);
17937         if(this.ddMatch && this.ddMatch.test(fvalue)){
17938             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17939             return false;
17940         }
17941         return true;
17942     },
17943
17944     // private
17945     // Provides logic to override the default TriggerField.validateBlur which just returns true
17946     validateBlur : function(){
17947         return !this.menu || !this.menu.isVisible();
17948     },
17949     
17950     getName: function()
17951     {
17952         // returns hidden if it's set..
17953         if (!this.rendered) {return ''};
17954         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17955         
17956     },
17957
17958     /**
17959      * Returns the current date value of the date field.
17960      * @return {Date} The date value
17961      */
17962     getValue : function(){
17963         
17964         return  this.hiddenField ?
17965                 this.hiddenField.value :
17966                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17967     },
17968
17969     /**
17970      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17971      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17972      * (the default format used is "m/d/y").
17973      * <br />Usage:
17974      * <pre><code>
17975 //All of these calls set the same date value (May 4, 2006)
17976
17977 //Pass a date object:
17978 var dt = new Date('5/4/06');
17979 dateField.setValue(dt);
17980
17981 //Pass a date string (default format):
17982 dateField.setValue('5/4/06');
17983
17984 //Pass a date string (custom format):
17985 dateField.format = 'Y-m-d';
17986 dateField.setValue('2006-5-4');
17987 </code></pre>
17988      * @param {String/Date} date The date or valid date string
17989      */
17990     setValue : function(date){
17991         if (this.hiddenField) {
17992             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17993         }
17994         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17995         // make sure the value field is always stored as a date..
17996         this.value = this.parseDate(date);
17997         
17998         
17999     },
18000
18001     // private
18002     parseDate : function(value){
18003         if(!value || value instanceof Date){
18004             return value;
18005         }
18006         var v = Date.parseDate(value, this.format);
18007          if (!v && this.useIso) {
18008             v = Date.parseDate(value, 'Y-m-d');
18009         }
18010         if(!v && this.altFormats){
18011             if(!this.altFormatsArray){
18012                 this.altFormatsArray = this.altFormats.split("|");
18013             }
18014             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18015                 v = Date.parseDate(value, this.altFormatsArray[i]);
18016             }
18017         }
18018         return v;
18019     },
18020
18021     // private
18022     formatDate : function(date, fmt){
18023         return (!date || !(date instanceof Date)) ?
18024                date : date.dateFormat(fmt || this.format);
18025     },
18026
18027     // private
18028     menuListeners : {
18029         select: function(m, d){
18030             
18031             this.setValue(d);
18032             this.fireEvent('select', this, d);
18033         },
18034         show : function(){ // retain focus styling
18035             this.onFocus();
18036         },
18037         hide : function(){
18038             this.focus.defer(10, this);
18039             var ml = this.menuListeners;
18040             this.menu.un("select", ml.select,  this);
18041             this.menu.un("show", ml.show,  this);
18042             this.menu.un("hide", ml.hide,  this);
18043         }
18044     },
18045
18046     // private
18047     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18048     onTriggerClick : function(){
18049         if(this.disabled){
18050             return;
18051         }
18052         if(this.menu == null){
18053             this.menu = new Roo.menu.DateMenu();
18054         }
18055         Roo.apply(this.menu.picker,  {
18056             showClear: this.allowBlank,
18057             minDate : this.minValue,
18058             maxDate : this.maxValue,
18059             disabledDatesRE : this.ddMatch,
18060             disabledDatesText : this.disabledDatesText,
18061             disabledDays : this.disabledDays,
18062             disabledDaysText : this.disabledDaysText,
18063             format : this.useIso ? 'Y-m-d' : this.format,
18064             minText : String.format(this.minText, this.formatDate(this.minValue)),
18065             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18066         });
18067         this.menu.on(Roo.apply({}, this.menuListeners, {
18068             scope:this
18069         }));
18070         this.menu.picker.setValue(this.getValue() || new Date());
18071         this.menu.show(this.el, "tl-bl?");
18072     },
18073
18074     beforeBlur : function(){
18075         var v = this.parseDate(this.getRawValue());
18076         if(v){
18077             this.setValue(v);
18078         }
18079     },
18080
18081     /*@
18082      * overide
18083      * 
18084      */
18085     isDirty : function() {
18086         if(this.disabled) {
18087             return false;
18088         }
18089         
18090         if(typeof(this.startValue) === 'undefined'){
18091             return false;
18092         }
18093         
18094         return String(this.getValue()) !== String(this.startValue);
18095         
18096     }
18097 });/*
18098  * Based on:
18099  * Ext JS Library 1.1.1
18100  * Copyright(c) 2006-2007, Ext JS, LLC.
18101  *
18102  * Originally Released Under LGPL - original licence link has changed is not relivant.
18103  *
18104  * Fork - LGPL
18105  * <script type="text/javascript">
18106  */
18107  
18108 /**
18109  * @class Roo.form.MonthField
18110  * @extends Roo.form.TriggerField
18111  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18112 * @constructor
18113 * Create a new MonthField
18114 * @param {Object} config
18115  */
18116 Roo.form.MonthField = function(config){
18117     
18118     Roo.form.MonthField.superclass.constructor.call(this, config);
18119     
18120       this.addEvents({
18121          
18122         /**
18123          * @event select
18124          * Fires when a date is selected
18125              * @param {Roo.form.MonthFieeld} combo This combo box
18126              * @param {Date} date The date selected
18127              */
18128         'select' : true
18129          
18130     });
18131     
18132     
18133     if(typeof this.minValue == "string") {
18134         this.minValue = this.parseDate(this.minValue);
18135     }
18136     if(typeof this.maxValue == "string") {
18137         this.maxValue = this.parseDate(this.maxValue);
18138     }
18139     this.ddMatch = null;
18140     if(this.disabledDates){
18141         var dd = this.disabledDates;
18142         var re = "(?:";
18143         for(var i = 0; i < dd.length; i++){
18144             re += dd[i];
18145             if(i != dd.length-1) {
18146                 re += "|";
18147             }
18148         }
18149         this.ddMatch = new RegExp(re + ")");
18150     }
18151 };
18152
18153 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18154     /**
18155      * @cfg {String} format
18156      * The default date format string which can be overriden for localization support.  The format must be
18157      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18158      */
18159     format : "M Y",
18160     /**
18161      * @cfg {String} altFormats
18162      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18163      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18164      */
18165     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18166     /**
18167      * @cfg {Array} disabledDays
18168      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18169      */
18170     disabledDays : [0,1,2,3,4,5,6],
18171     /**
18172      * @cfg {String} disabledDaysText
18173      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18174      */
18175     disabledDaysText : "Disabled",
18176     /**
18177      * @cfg {Array} disabledDates
18178      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18179      * expression so they are very powerful. Some examples:
18180      * <ul>
18181      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18182      * <li>["03/08", "09/16"] would disable those days for every year</li>
18183      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18184      * <li>["03/../2006"] would disable every day in March 2006</li>
18185      * <li>["^03"] would disable every day in every March</li>
18186      * </ul>
18187      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18188      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18189      */
18190     disabledDates : null,
18191     /**
18192      * @cfg {String} disabledDatesText
18193      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18194      */
18195     disabledDatesText : "Disabled",
18196     /**
18197      * @cfg {Date/String} minValue
18198      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18199      * valid format (defaults to null).
18200      */
18201     minValue : null,
18202     /**
18203      * @cfg {Date/String} maxValue
18204      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18205      * valid format (defaults to null).
18206      */
18207     maxValue : null,
18208     /**
18209      * @cfg {String} minText
18210      * The error text to display when the date in the cell is before minValue (defaults to
18211      * 'The date in this field must be after {minValue}').
18212      */
18213     minText : "The date in this field must be equal to or after {0}",
18214     /**
18215      * @cfg {String} maxTextf
18216      * The error text to display when the date in the cell is after maxValue (defaults to
18217      * 'The date in this field must be before {maxValue}').
18218      */
18219     maxText : "The date in this field must be equal to or before {0}",
18220     /**
18221      * @cfg {String} invalidText
18222      * The error text to display when the date in the field is invalid (defaults to
18223      * '{value} is not a valid date - it must be in the format {format}').
18224      */
18225     invalidText : "{0} is not a valid date - it must be in the format {1}",
18226     /**
18227      * @cfg {String} triggerClass
18228      * An additional CSS class used to style the trigger button.  The trigger will always get the
18229      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18230      * which displays a calendar icon).
18231      */
18232     triggerClass : 'x-form-date-trigger',
18233     
18234
18235     /**
18236      * @cfg {Boolean} useIso
18237      * if enabled, then the date field will use a hidden field to store the 
18238      * real value as iso formated date. default (true)
18239      */ 
18240     useIso : true,
18241     /**
18242      * @cfg {String/Object} autoCreate
18243      * A DomHelper element spec, or true for a default element spec (defaults to
18244      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18245      */ 
18246     // private
18247     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18248     
18249     // private
18250     hiddenField: false,
18251     
18252     hideMonthPicker : false,
18253     
18254     onRender : function(ct, position)
18255     {
18256         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18257         if (this.useIso) {
18258             this.el.dom.removeAttribute('name'); 
18259             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18260                     'before', true);
18261             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18262             // prevent input submission
18263             this.hiddenName = this.name;
18264         }
18265             
18266             
18267     },
18268     
18269     // private
18270     validateValue : function(value)
18271     {
18272         value = this.formatDate(value);
18273         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18274             return false;
18275         }
18276         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18277              return true;
18278         }
18279         var svalue = value;
18280         value = this.parseDate(value);
18281         if(!value){
18282             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18283             return false;
18284         }
18285         var time = value.getTime();
18286         if(this.minValue && time < this.minValue.getTime()){
18287             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18288             return false;
18289         }
18290         if(this.maxValue && time > this.maxValue.getTime()){
18291             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18292             return false;
18293         }
18294         /*if(this.disabledDays){
18295             var day = value.getDay();
18296             for(var i = 0; i < this.disabledDays.length; i++) {
18297                 if(day === this.disabledDays[i]){
18298                     this.markInvalid(this.disabledDaysText);
18299                     return false;
18300                 }
18301             }
18302         }
18303         */
18304         var fvalue = this.formatDate(value);
18305         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18306             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18307             return false;
18308         }
18309         */
18310         return true;
18311     },
18312
18313     // private
18314     // Provides logic to override the default TriggerField.validateBlur which just returns true
18315     validateBlur : function(){
18316         return !this.menu || !this.menu.isVisible();
18317     },
18318
18319     /**
18320      * Returns the current date value of the date field.
18321      * @return {Date} The date value
18322      */
18323     getValue : function(){
18324         
18325         
18326         
18327         return  this.hiddenField ?
18328                 this.hiddenField.value :
18329                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18330     },
18331
18332     /**
18333      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18334      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18335      * (the default format used is "m/d/y").
18336      * <br />Usage:
18337      * <pre><code>
18338 //All of these calls set the same date value (May 4, 2006)
18339
18340 //Pass a date object:
18341 var dt = new Date('5/4/06');
18342 monthField.setValue(dt);
18343
18344 //Pass a date string (default format):
18345 monthField.setValue('5/4/06');
18346
18347 //Pass a date string (custom format):
18348 monthField.format = 'Y-m-d';
18349 monthField.setValue('2006-5-4');
18350 </code></pre>
18351      * @param {String/Date} date The date or valid date string
18352      */
18353     setValue : function(date){
18354         Roo.log('month setValue' + date);
18355         // can only be first of month..
18356         
18357         var val = this.parseDate(date);
18358         
18359         if (this.hiddenField) {
18360             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18361         }
18362         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18363         this.value = this.parseDate(date);
18364     },
18365
18366     // private
18367     parseDate : function(value){
18368         if(!value || value instanceof Date){
18369             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18370             return value;
18371         }
18372         var v = Date.parseDate(value, this.format);
18373         if (!v && this.useIso) {
18374             v = Date.parseDate(value, 'Y-m-d');
18375         }
18376         if (v) {
18377             // 
18378             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18379         }
18380         
18381         
18382         if(!v && this.altFormats){
18383             if(!this.altFormatsArray){
18384                 this.altFormatsArray = this.altFormats.split("|");
18385             }
18386             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18387                 v = Date.parseDate(value, this.altFormatsArray[i]);
18388             }
18389         }
18390         return v;
18391     },
18392
18393     // private
18394     formatDate : function(date, fmt){
18395         return (!date || !(date instanceof Date)) ?
18396                date : date.dateFormat(fmt || this.format);
18397     },
18398
18399     // private
18400     menuListeners : {
18401         select: function(m, d){
18402             this.setValue(d);
18403             this.fireEvent('select', this, d);
18404         },
18405         show : function(){ // retain focus styling
18406             this.onFocus();
18407         },
18408         hide : function(){
18409             this.focus.defer(10, this);
18410             var ml = this.menuListeners;
18411             this.menu.un("select", ml.select,  this);
18412             this.menu.un("show", ml.show,  this);
18413             this.menu.un("hide", ml.hide,  this);
18414         }
18415     },
18416     // private
18417     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18418     onTriggerClick : function(){
18419         if(this.disabled){
18420             return;
18421         }
18422         if(this.menu == null){
18423             this.menu = new Roo.menu.DateMenu();
18424            
18425         }
18426         
18427         Roo.apply(this.menu.picker,  {
18428             
18429             showClear: this.allowBlank,
18430             minDate : this.minValue,
18431             maxDate : this.maxValue,
18432             disabledDatesRE : this.ddMatch,
18433             disabledDatesText : this.disabledDatesText,
18434             
18435             format : this.useIso ? 'Y-m-d' : this.format,
18436             minText : String.format(this.minText, this.formatDate(this.minValue)),
18437             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18438             
18439         });
18440          this.menu.on(Roo.apply({}, this.menuListeners, {
18441             scope:this
18442         }));
18443        
18444         
18445         var m = this.menu;
18446         var p = m.picker;
18447         
18448         // hide month picker get's called when we called by 'before hide';
18449         
18450         var ignorehide = true;
18451         p.hideMonthPicker  = function(disableAnim){
18452             if (ignorehide) {
18453                 return;
18454             }
18455              if(this.monthPicker){
18456                 Roo.log("hideMonthPicker called");
18457                 if(disableAnim === true){
18458                     this.monthPicker.hide();
18459                 }else{
18460                     this.monthPicker.slideOut('t', {duration:.2});
18461                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18462                     p.fireEvent("select", this, this.value);
18463                     m.hide();
18464                 }
18465             }
18466         }
18467         
18468         Roo.log('picker set value');
18469         Roo.log(this.getValue());
18470         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18471         m.show(this.el, 'tl-bl?');
18472         ignorehide  = false;
18473         // this will trigger hideMonthPicker..
18474         
18475         
18476         // hidden the day picker
18477         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18478         
18479         
18480         
18481       
18482         
18483         p.showMonthPicker.defer(100, p);
18484     
18485         
18486        
18487     },
18488
18489     beforeBlur : function(){
18490         var v = this.parseDate(this.getRawValue());
18491         if(v){
18492             this.setValue(v);
18493         }
18494     }
18495
18496     /** @cfg {Boolean} grow @hide */
18497     /** @cfg {Number} growMin @hide */
18498     /** @cfg {Number} growMax @hide */
18499     /**
18500      * @hide
18501      * @method autoSize
18502      */
18503 });/*
18504  * Based on:
18505  * Ext JS Library 1.1.1
18506  * Copyright(c) 2006-2007, Ext JS, LLC.
18507  *
18508  * Originally Released Under LGPL - original licence link has changed is not relivant.
18509  *
18510  * Fork - LGPL
18511  * <script type="text/javascript">
18512  */
18513  
18514
18515 /**
18516  * @class Roo.form.ComboBox
18517  * @extends Roo.form.TriggerField
18518  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18519  * @constructor
18520  * Create a new ComboBox.
18521  * @param {Object} config Configuration options
18522  */
18523 Roo.form.ComboBox = function(config){
18524     Roo.form.ComboBox.superclass.constructor.call(this, config);
18525     this.addEvents({
18526         /**
18527          * @event expand
18528          * Fires when the dropdown list is expanded
18529              * @param {Roo.form.ComboBox} combo This combo box
18530              */
18531         'expand' : true,
18532         /**
18533          * @event collapse
18534          * Fires when the dropdown list is collapsed
18535              * @param {Roo.form.ComboBox} combo This combo box
18536              */
18537         'collapse' : true,
18538         /**
18539          * @event beforeselect
18540          * Fires before a list item is selected. Return false to cancel the selection.
18541              * @param {Roo.form.ComboBox} combo This combo box
18542              * @param {Roo.data.Record} record The data record returned from the underlying store
18543              * @param {Number} index The index of the selected item in the dropdown list
18544              */
18545         'beforeselect' : true,
18546         /**
18547          * @event select
18548          * Fires when a list item is selected
18549              * @param {Roo.form.ComboBox} combo This combo box
18550              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18551              * @param {Number} index The index of the selected item in the dropdown list
18552              */
18553         'select' : true,
18554         /**
18555          * @event beforequery
18556          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18557          * The event object passed has these properties:
18558              * @param {Roo.form.ComboBox} combo This combo box
18559              * @param {String} query The query
18560              * @param {Boolean} forceAll true to force "all" query
18561              * @param {Boolean} cancel true to cancel the query
18562              * @param {Object} e The query event object
18563              */
18564         'beforequery': true,
18565          /**
18566          * @event add
18567          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18568              * @param {Roo.form.ComboBox} combo This combo box
18569              */
18570         'add' : true,
18571         /**
18572          * @event edit
18573          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18574              * @param {Roo.form.ComboBox} combo This combo box
18575              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18576              */
18577         'edit' : true
18578         
18579         
18580     });
18581     if(this.transform){
18582         this.allowDomMove = false;
18583         var s = Roo.getDom(this.transform);
18584         if(!this.hiddenName){
18585             this.hiddenName = s.name;
18586         }
18587         if(!this.store){
18588             this.mode = 'local';
18589             var d = [], opts = s.options;
18590             for(var i = 0, len = opts.length;i < len; i++){
18591                 var o = opts[i];
18592                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18593                 if(o.selected) {
18594                     this.value = value;
18595                 }
18596                 d.push([value, o.text]);
18597             }
18598             this.store = new Roo.data.SimpleStore({
18599                 'id': 0,
18600                 fields: ['value', 'text'],
18601                 data : d
18602             });
18603             this.valueField = 'value';
18604             this.displayField = 'text';
18605         }
18606         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18607         if(!this.lazyRender){
18608             this.target = true;
18609             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18610             s.parentNode.removeChild(s); // remove it
18611             this.render(this.el.parentNode);
18612         }else{
18613             s.parentNode.removeChild(s); // remove it
18614         }
18615
18616     }
18617     if (this.store) {
18618         this.store = Roo.factory(this.store, Roo.data);
18619     }
18620     
18621     this.selectedIndex = -1;
18622     if(this.mode == 'local'){
18623         if(config.queryDelay === undefined){
18624             this.queryDelay = 10;
18625         }
18626         if(config.minChars === undefined){
18627             this.minChars = 0;
18628         }
18629     }
18630 };
18631
18632 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18633     /**
18634      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18635      */
18636     /**
18637      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18638      * rendering into an Roo.Editor, defaults to false)
18639      */
18640     /**
18641      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18642      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18643      */
18644     /**
18645      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18646      */
18647     /**
18648      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18649      * the dropdown list (defaults to undefined, with no header element)
18650      */
18651
18652      /**
18653      * @cfg {String/Roo.Template} tpl The template to use to render the output
18654      */
18655      
18656     // private
18657     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18658     /**
18659      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18660      */
18661     listWidth: undefined,
18662     /**
18663      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18664      * mode = 'remote' or 'text' if mode = 'local')
18665      */
18666     displayField: undefined,
18667     /**
18668      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18669      * mode = 'remote' or 'value' if mode = 'local'). 
18670      * Note: use of a valueField requires the user make a selection
18671      * in order for a value to be mapped.
18672      */
18673     valueField: undefined,
18674     
18675     
18676     /**
18677      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18678      * field's data value (defaults to the underlying DOM element's name)
18679      */
18680     hiddenName: undefined,
18681     /**
18682      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18683      */
18684     listClass: '',
18685     /**
18686      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18687      */
18688     selectedClass: 'x-combo-selected',
18689     /**
18690      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18691      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18692      * which displays a downward arrow icon).
18693      */
18694     triggerClass : 'x-form-arrow-trigger',
18695     /**
18696      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18697      */
18698     shadow:'sides',
18699     /**
18700      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18701      * anchor positions (defaults to 'tl-bl')
18702      */
18703     listAlign: 'tl-bl?',
18704     /**
18705      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18706      */
18707     maxHeight: 300,
18708     /**
18709      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18710      * query specified by the allQuery config option (defaults to 'query')
18711      */
18712     triggerAction: 'query',
18713     /**
18714      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18715      * (defaults to 4, does not apply if editable = false)
18716      */
18717     minChars : 4,
18718     /**
18719      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18720      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18721      */
18722     typeAhead: false,
18723     /**
18724      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18725      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18726      */
18727     queryDelay: 500,
18728     /**
18729      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18730      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18731      */
18732     pageSize: 0,
18733     /**
18734      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18735      * when editable = true (defaults to false)
18736      */
18737     selectOnFocus:false,
18738     /**
18739      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18740      */
18741     queryParam: 'query',
18742     /**
18743      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18744      * when mode = 'remote' (defaults to 'Loading...')
18745      */
18746     loadingText: 'Loading...',
18747     /**
18748      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18749      */
18750     resizable: false,
18751     /**
18752      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18753      */
18754     handleHeight : 8,
18755     /**
18756      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18757      * traditional select (defaults to true)
18758      */
18759     editable: true,
18760     /**
18761      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18762      */
18763     allQuery: '',
18764     /**
18765      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18766      */
18767     mode: 'remote',
18768     /**
18769      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18770      * listWidth has a higher value)
18771      */
18772     minListWidth : 70,
18773     /**
18774      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18775      * allow the user to set arbitrary text into the field (defaults to false)
18776      */
18777     forceSelection:false,
18778     /**
18779      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18780      * if typeAhead = true (defaults to 250)
18781      */
18782     typeAheadDelay : 250,
18783     /**
18784      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18785      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18786      */
18787     valueNotFoundText : undefined,
18788     /**
18789      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18790      */
18791     blockFocus : false,
18792     
18793     /**
18794      * @cfg {Boolean} disableClear Disable showing of clear button.
18795      */
18796     disableClear : false,
18797     /**
18798      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18799      */
18800     alwaysQuery : false,
18801     
18802     //private
18803     addicon : false,
18804     editicon: false,
18805     
18806     // element that contains real text value.. (when hidden is used..)
18807      
18808     // private
18809     onRender : function(ct, position){
18810         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18811         if(this.hiddenName){
18812             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18813                     'before', true);
18814             this.hiddenField.value =
18815                 this.hiddenValue !== undefined ? this.hiddenValue :
18816                 this.value !== undefined ? this.value : '';
18817
18818             // prevent input submission
18819             this.el.dom.removeAttribute('name');
18820              
18821              
18822         }
18823         if(Roo.isGecko){
18824             this.el.dom.setAttribute('autocomplete', 'off');
18825         }
18826
18827         var cls = 'x-combo-list';
18828
18829         this.list = new Roo.Layer({
18830             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18831         });
18832
18833         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18834         this.list.setWidth(lw);
18835         this.list.swallowEvent('mousewheel');
18836         this.assetHeight = 0;
18837
18838         if(this.title){
18839             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18840             this.assetHeight += this.header.getHeight();
18841         }
18842
18843         this.innerList = this.list.createChild({cls:cls+'-inner'});
18844         this.innerList.on('mouseover', this.onViewOver, this);
18845         this.innerList.on('mousemove', this.onViewMove, this);
18846         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18847         
18848         if(this.allowBlank && !this.pageSize && !this.disableClear){
18849             this.footer = this.list.createChild({cls:cls+'-ft'});
18850             this.pageTb = new Roo.Toolbar(this.footer);
18851            
18852         }
18853         if(this.pageSize){
18854             this.footer = this.list.createChild({cls:cls+'-ft'});
18855             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18856                     {pageSize: this.pageSize});
18857             
18858         }
18859         
18860         if (this.pageTb && this.allowBlank && !this.disableClear) {
18861             var _this = this;
18862             this.pageTb.add(new Roo.Toolbar.Fill(), {
18863                 cls: 'x-btn-icon x-btn-clear',
18864                 text: '&#160;',
18865                 handler: function()
18866                 {
18867                     _this.collapse();
18868                     _this.clearValue();
18869                     _this.onSelect(false, -1);
18870                 }
18871             });
18872         }
18873         if (this.footer) {
18874             this.assetHeight += this.footer.getHeight();
18875         }
18876         
18877
18878         if(!this.tpl){
18879             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18880         }
18881
18882         this.view = new Roo.View(this.innerList, this.tpl, {
18883             singleSelect:true, store: this.store, selectedClass: this.selectedClass
18884         });
18885
18886         this.view.on('click', this.onViewClick, this);
18887
18888         this.store.on('beforeload', this.onBeforeLoad, this);
18889         this.store.on('load', this.onLoad, this);
18890         this.store.on('loadexception', this.onLoadException, this);
18891
18892         if(this.resizable){
18893             this.resizer = new Roo.Resizable(this.list,  {
18894                pinned:true, handles:'se'
18895             });
18896             this.resizer.on('resize', function(r, w, h){
18897                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18898                 this.listWidth = w;
18899                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18900                 this.restrictHeight();
18901             }, this);
18902             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18903         }
18904         if(!this.editable){
18905             this.editable = true;
18906             this.setEditable(false);
18907         }  
18908         
18909         
18910         if (typeof(this.events.add.listeners) != 'undefined') {
18911             
18912             this.addicon = this.wrap.createChild(
18913                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18914        
18915             this.addicon.on('click', function(e) {
18916                 this.fireEvent('add', this);
18917             }, this);
18918         }
18919         if (typeof(this.events.edit.listeners) != 'undefined') {
18920             
18921             this.editicon = this.wrap.createChild(
18922                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18923             if (this.addicon) {
18924                 this.editicon.setStyle('margin-left', '40px');
18925             }
18926             this.editicon.on('click', function(e) {
18927                 
18928                 // we fire even  if inothing is selected..
18929                 this.fireEvent('edit', this, this.lastData );
18930                 
18931             }, this);
18932         }
18933         
18934         
18935         
18936     },
18937
18938     // private
18939     initEvents : function(){
18940         Roo.form.ComboBox.superclass.initEvents.call(this);
18941
18942         this.keyNav = new Roo.KeyNav(this.el, {
18943             "up" : function(e){
18944                 this.inKeyMode = true;
18945                 this.selectPrev();
18946             },
18947
18948             "down" : function(e){
18949                 if(!this.isExpanded()){
18950                     this.onTriggerClick();
18951                 }else{
18952                     this.inKeyMode = true;
18953                     this.selectNext();
18954                 }
18955             },
18956
18957             "enter" : function(e){
18958                 this.onViewClick();
18959                 //return true;
18960             },
18961
18962             "esc" : function(e){
18963                 this.collapse();
18964             },
18965
18966             "tab" : function(e){
18967                 this.onViewClick(false);
18968                 this.fireEvent("specialkey", this, e);
18969                 return true;
18970             },
18971
18972             scope : this,
18973
18974             doRelay : function(foo, bar, hname){
18975                 if(hname == 'down' || this.scope.isExpanded()){
18976                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18977                 }
18978                 return true;
18979             },
18980
18981             forceKeyDown: true
18982         });
18983         this.queryDelay = Math.max(this.queryDelay || 10,
18984                 this.mode == 'local' ? 10 : 250);
18985         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18986         if(this.typeAhead){
18987             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18988         }
18989         if(this.editable !== false){
18990             this.el.on("keyup", this.onKeyUp, this);
18991         }
18992         if(this.forceSelection){
18993             this.on('blur', this.doForce, this);
18994         }
18995     },
18996
18997     onDestroy : function(){
18998         if(this.view){
18999             this.view.setStore(null);
19000             this.view.el.removeAllListeners();
19001             this.view.el.remove();
19002             this.view.purgeListeners();
19003         }
19004         if(this.list){
19005             this.list.destroy();
19006         }
19007         if(this.store){
19008             this.store.un('beforeload', this.onBeforeLoad, this);
19009             this.store.un('load', this.onLoad, this);
19010             this.store.un('loadexception', this.onLoadException, this);
19011         }
19012         Roo.form.ComboBox.superclass.onDestroy.call(this);
19013     },
19014
19015     // private
19016     fireKey : function(e){
19017         if(e.isNavKeyPress() && !this.list.isVisible()){
19018             this.fireEvent("specialkey", this, e);
19019         }
19020     },
19021
19022     // private
19023     onResize: function(w, h){
19024         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19025         
19026         if(typeof w != 'number'){
19027             // we do not handle it!?!?
19028             return;
19029         }
19030         var tw = this.trigger.getWidth();
19031         tw += this.addicon ? this.addicon.getWidth() : 0;
19032         tw += this.editicon ? this.editicon.getWidth() : 0;
19033         var x = w - tw;
19034         this.el.setWidth( this.adjustWidth('input', x));
19035             
19036         this.trigger.setStyle('left', x+'px');
19037         
19038         if(this.list && this.listWidth === undefined){
19039             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19040             this.list.setWidth(lw);
19041             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19042         }
19043         
19044     
19045         
19046     },
19047
19048     /**
19049      * Allow or prevent the user from directly editing the field text.  If false is passed,
19050      * the user will only be able to select from the items defined in the dropdown list.  This method
19051      * is the runtime equivalent of setting the 'editable' config option at config time.
19052      * @param {Boolean} value True to allow the user to directly edit the field text
19053      */
19054     setEditable : function(value){
19055         if(value == this.editable){
19056             return;
19057         }
19058         this.editable = value;
19059         if(!value){
19060             this.el.dom.setAttribute('readOnly', true);
19061             this.el.on('mousedown', this.onTriggerClick,  this);
19062             this.el.addClass('x-combo-noedit');
19063         }else{
19064             this.el.dom.setAttribute('readOnly', false);
19065             this.el.un('mousedown', this.onTriggerClick,  this);
19066             this.el.removeClass('x-combo-noedit');
19067         }
19068     },
19069
19070     // private
19071     onBeforeLoad : function(){
19072         if(!this.hasFocus){
19073             return;
19074         }
19075         this.innerList.update(this.loadingText ?
19076                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19077         this.restrictHeight();
19078         this.selectedIndex = -1;
19079     },
19080
19081     // private
19082     onLoad : function(){
19083         if(!this.hasFocus){
19084             return;
19085         }
19086         if(this.store.getCount() > 0){
19087             this.expand();
19088             this.restrictHeight();
19089             if(this.lastQuery == this.allQuery){
19090                 if(this.editable){
19091                     this.el.dom.select();
19092                 }
19093                 if(!this.selectByValue(this.value, true)){
19094                     this.select(0, true);
19095                 }
19096             }else{
19097                 this.selectNext();
19098                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19099                     this.taTask.delay(this.typeAheadDelay);
19100                 }
19101             }
19102         }else{
19103             this.onEmptyResults();
19104         }
19105         //this.el.focus();
19106     },
19107     // private
19108     onLoadException : function()
19109     {
19110         this.collapse();
19111         Roo.log(this.store.reader.jsonData);
19112         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19113             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19114         }
19115         
19116         
19117     },
19118     // private
19119     onTypeAhead : function(){
19120         if(this.store.getCount() > 0){
19121             var r = this.store.getAt(0);
19122             var newValue = r.data[this.displayField];
19123             var len = newValue.length;
19124             var selStart = this.getRawValue().length;
19125             if(selStart != len){
19126                 this.setRawValue(newValue);
19127                 this.selectText(selStart, newValue.length);
19128             }
19129         }
19130     },
19131
19132     // private
19133     onSelect : function(record, index){
19134         if(this.fireEvent('beforeselect', this, record, index) !== false){
19135             this.setFromData(index > -1 ? record.data : false);
19136             this.collapse();
19137             this.fireEvent('select', this, record, index);
19138         }
19139     },
19140
19141     /**
19142      * Returns the currently selected field value or empty string if no value is set.
19143      * @return {String} value The selected value
19144      */
19145     getValue : function(){
19146         if(this.valueField){
19147             return typeof this.value != 'undefined' ? this.value : '';
19148         }
19149         return Roo.form.ComboBox.superclass.getValue.call(this);
19150     },
19151
19152     /**
19153      * Clears any text/value currently set in the field
19154      */
19155     clearValue : function(){
19156         if(this.hiddenField){
19157             this.hiddenField.value = '';
19158         }
19159         this.value = '';
19160         this.setRawValue('');
19161         this.lastSelectionText = '';
19162         
19163     },
19164
19165     /**
19166      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19167      * will be displayed in the field.  If the value does not match the data value of an existing item,
19168      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19169      * Otherwise the field will be blank (although the value will still be set).
19170      * @param {String} value The value to match
19171      */
19172     setValue : function(v){
19173         var text = v;
19174         if(this.valueField){
19175             var r = this.findRecord(this.valueField, v);
19176             if(r){
19177                 text = r.data[this.displayField];
19178             }else if(this.valueNotFoundText !== undefined){
19179                 text = this.valueNotFoundText;
19180             }
19181         }
19182         this.lastSelectionText = text;
19183         if(this.hiddenField){
19184             this.hiddenField.value = v;
19185         }
19186         Roo.form.ComboBox.superclass.setValue.call(this, text);
19187         this.value = v;
19188     },
19189     /**
19190      * @property {Object} the last set data for the element
19191      */
19192     
19193     lastData : false,
19194     /**
19195      * Sets the value of the field based on a object which is related to the record format for the store.
19196      * @param {Object} value the value to set as. or false on reset?
19197      */
19198     setFromData : function(o){
19199         var dv = ''; // display value
19200         var vv = ''; // value value..
19201         this.lastData = o;
19202         if (this.displayField) {
19203             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19204         } else {
19205             // this is an error condition!!!
19206             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19207         }
19208         
19209         if(this.valueField){
19210             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19211         }
19212         if(this.hiddenField){
19213             this.hiddenField.value = vv;
19214             
19215             this.lastSelectionText = dv;
19216             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19217             this.value = vv;
19218             return;
19219         }
19220         // no hidden field.. - we store the value in 'value', but still display
19221         // display field!!!!
19222         this.lastSelectionText = dv;
19223         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19224         this.value = vv;
19225         
19226         
19227     },
19228     // private
19229     reset : function(){
19230         // overridden so that last data is reset..
19231         this.setValue(this.resetValue);
19232         this.originalValue = this.getValue();
19233         this.clearInvalid();
19234         this.lastData = false;
19235         if (this.view) {
19236             this.view.clearSelections();
19237         }
19238     },
19239     // private
19240     findRecord : function(prop, value){
19241         var record;
19242         if(this.store.getCount() > 0){
19243             this.store.each(function(r){
19244                 if(r.data[prop] == value){
19245                     record = r;
19246                     return false;
19247                 }
19248                 return true;
19249             });
19250         }
19251         return record;
19252     },
19253     
19254     getName: function()
19255     {
19256         // returns hidden if it's set..
19257         if (!this.rendered) {return ''};
19258         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19259         
19260     },
19261     // private
19262     onViewMove : function(e, t){
19263         this.inKeyMode = false;
19264     },
19265
19266     // private
19267     onViewOver : function(e, t){
19268         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19269             return;
19270         }
19271         var item = this.view.findItemFromChild(t);
19272         if(item){
19273             var index = this.view.indexOf(item);
19274             this.select(index, false);
19275         }
19276     },
19277
19278     // private
19279     onViewClick : function(doFocus)
19280     {
19281         var index = this.view.getSelectedIndexes()[0];
19282         var r = this.store.getAt(index);
19283         if(r){
19284             this.onSelect(r, index);
19285         }
19286         if(doFocus !== false && !this.blockFocus){
19287             this.el.focus();
19288         }
19289     },
19290
19291     // private
19292     restrictHeight : function(){
19293         this.innerList.dom.style.height = '';
19294         var inner = this.innerList.dom;
19295         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19296         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19297         this.list.beginUpdate();
19298         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19299         this.list.alignTo(this.el, this.listAlign);
19300         this.list.endUpdate();
19301     },
19302
19303     // private
19304     onEmptyResults : function(){
19305         this.collapse();
19306     },
19307
19308     /**
19309      * Returns true if the dropdown list is expanded, else false.
19310      */
19311     isExpanded : function(){
19312         return this.list.isVisible();
19313     },
19314
19315     /**
19316      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19317      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19318      * @param {String} value The data value of the item to select
19319      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19320      * selected item if it is not currently in view (defaults to true)
19321      * @return {Boolean} True if the value matched an item in the list, else false
19322      */
19323     selectByValue : function(v, scrollIntoView){
19324         if(v !== undefined && v !== null){
19325             var r = this.findRecord(this.valueField || this.displayField, v);
19326             if(r){
19327                 this.select(this.store.indexOf(r), scrollIntoView);
19328                 return true;
19329             }
19330         }
19331         return false;
19332     },
19333
19334     /**
19335      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19336      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19337      * @param {Number} index The zero-based index of the list item to select
19338      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19339      * selected item if it is not currently in view (defaults to true)
19340      */
19341     select : function(index, scrollIntoView){
19342         this.selectedIndex = index;
19343         this.view.select(index);
19344         if(scrollIntoView !== false){
19345             var el = this.view.getNode(index);
19346             if(el){
19347                 this.innerList.scrollChildIntoView(el, false);
19348             }
19349         }
19350     },
19351
19352     // private
19353     selectNext : function(){
19354         var ct = this.store.getCount();
19355         if(ct > 0){
19356             if(this.selectedIndex == -1){
19357                 this.select(0);
19358             }else if(this.selectedIndex < ct-1){
19359                 this.select(this.selectedIndex+1);
19360             }
19361         }
19362     },
19363
19364     // private
19365     selectPrev : function(){
19366         var ct = this.store.getCount();
19367         if(ct > 0){
19368             if(this.selectedIndex == -1){
19369                 this.select(0);
19370             }else if(this.selectedIndex != 0){
19371                 this.select(this.selectedIndex-1);
19372             }
19373         }
19374     },
19375
19376     // private
19377     onKeyUp : function(e){
19378         if(this.editable !== false && !e.isSpecialKey()){
19379             this.lastKey = e.getKey();
19380             this.dqTask.delay(this.queryDelay);
19381         }
19382     },
19383
19384     // private
19385     validateBlur : function(){
19386         return !this.list || !this.list.isVisible();   
19387     },
19388
19389     // private
19390     initQuery : function(){
19391         this.doQuery(this.getRawValue());
19392     },
19393
19394     // private
19395     doForce : function(){
19396         if(this.el.dom.value.length > 0){
19397             this.el.dom.value =
19398                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19399              
19400         }
19401     },
19402
19403     /**
19404      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19405      * query allowing the query action to be canceled if needed.
19406      * @param {String} query The SQL query to execute
19407      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19408      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19409      * saved in the current store (defaults to false)
19410      */
19411     doQuery : function(q, forceAll){
19412         if(q === undefined || q === null){
19413             q = '';
19414         }
19415         var qe = {
19416             query: q,
19417             forceAll: forceAll,
19418             combo: this,
19419             cancel:false
19420         };
19421         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19422             return false;
19423         }
19424         q = qe.query;
19425         forceAll = qe.forceAll;
19426         if(forceAll === true || (q.length >= this.minChars)){
19427             if(this.lastQuery != q || this.alwaysQuery){
19428                 this.lastQuery = q;
19429                 if(this.mode == 'local'){
19430                     this.selectedIndex = -1;
19431                     if(forceAll){
19432                         this.store.clearFilter();
19433                     }else{
19434                         this.store.filter(this.displayField, q);
19435                     }
19436                     this.onLoad();
19437                 }else{
19438                     this.store.baseParams[this.queryParam] = q;
19439                     this.store.load({
19440                         params: this.getParams(q)
19441                     });
19442                     this.expand();
19443                 }
19444             }else{
19445                 this.selectedIndex = -1;
19446                 this.onLoad();   
19447             }
19448         }
19449     },
19450
19451     // private
19452     getParams : function(q){
19453         var p = {};
19454         //p[this.queryParam] = q;
19455         if(this.pageSize){
19456             p.start = 0;
19457             p.limit = this.pageSize;
19458         }
19459         return p;
19460     },
19461
19462     /**
19463      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19464      */
19465     collapse : function(){
19466         if(!this.isExpanded()){
19467             return;
19468         }
19469         this.list.hide();
19470         Roo.get(document).un('mousedown', this.collapseIf, this);
19471         Roo.get(document).un('mousewheel', this.collapseIf, this);
19472         if (!this.editable) {
19473             Roo.get(document).un('keydown', this.listKeyPress, this);
19474         }
19475         this.fireEvent('collapse', this);
19476     },
19477
19478     // private
19479     collapseIf : function(e){
19480         if(!e.within(this.wrap) && !e.within(this.list)){
19481             this.collapse();
19482         }
19483     },
19484
19485     /**
19486      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19487      */
19488     expand : function(){
19489         if(this.isExpanded() || !this.hasFocus){
19490             return;
19491         }
19492         this.list.alignTo(this.el, this.listAlign);
19493         this.list.show();
19494         Roo.get(document).on('mousedown', this.collapseIf, this);
19495         Roo.get(document).on('mousewheel', this.collapseIf, this);
19496         if (!this.editable) {
19497             Roo.get(document).on('keydown', this.listKeyPress, this);
19498         }
19499         
19500         this.fireEvent('expand', this);
19501     },
19502
19503     // private
19504     // Implements the default empty TriggerField.onTriggerClick function
19505     onTriggerClick : function(){
19506         if(this.disabled){
19507             return;
19508         }
19509         if(this.isExpanded()){
19510             this.collapse();
19511             if (!this.blockFocus) {
19512                 this.el.focus();
19513             }
19514             
19515         }else {
19516             this.hasFocus = true;
19517             if(this.triggerAction == 'all') {
19518                 this.doQuery(this.allQuery, true);
19519             } else {
19520                 this.doQuery(this.getRawValue());
19521             }
19522             if (!this.blockFocus) {
19523                 this.el.focus();
19524             }
19525         }
19526     },
19527     listKeyPress : function(e)
19528     {
19529         //Roo.log('listkeypress');
19530         // scroll to first matching element based on key pres..
19531         if (e.isSpecialKey()) {
19532             return false;
19533         }
19534         var k = String.fromCharCode(e.getKey()).toUpperCase();
19535         //Roo.log(k);
19536         var match  = false;
19537         var csel = this.view.getSelectedNodes();
19538         var cselitem = false;
19539         if (csel.length) {
19540             var ix = this.view.indexOf(csel[0]);
19541             cselitem  = this.store.getAt(ix);
19542             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19543                 cselitem = false;
19544             }
19545             
19546         }
19547         
19548         this.store.each(function(v) { 
19549             if (cselitem) {
19550                 // start at existing selection.
19551                 if (cselitem.id == v.id) {
19552                     cselitem = false;
19553                 }
19554                 return;
19555             }
19556                 
19557             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19558                 match = this.store.indexOf(v);
19559                 return false;
19560             }
19561         }, this);
19562         
19563         if (match === false) {
19564             return true; // no more action?
19565         }
19566         // scroll to?
19567         this.view.select(match);
19568         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19569         sn.scrollIntoView(sn.dom.parentNode, false);
19570     }
19571
19572     /** 
19573     * @cfg {Boolean} grow 
19574     * @hide 
19575     */
19576     /** 
19577     * @cfg {Number} growMin 
19578     * @hide 
19579     */
19580     /** 
19581     * @cfg {Number} growMax 
19582     * @hide 
19583     */
19584     /**
19585      * @hide
19586      * @method autoSize
19587      */
19588 });/*
19589  * Copyright(c) 2010-2012, Roo J Solutions Limited
19590  *
19591  * Licence LGPL
19592  *
19593  */
19594
19595 /**
19596  * @class Roo.form.ComboBoxArray
19597  * @extends Roo.form.TextField
19598  * A facebook style adder... for lists of email / people / countries  etc...
19599  * pick multiple items from a combo box, and shows each one.
19600  *
19601  *  Fred [x]  Brian [x]  [Pick another |v]
19602  *
19603  *
19604  *  For this to work: it needs various extra information
19605  *    - normal combo problay has
19606  *      name, hiddenName
19607  *    + displayField, valueField
19608  *
19609  *    For our purpose...
19610  *
19611  *
19612  *   If we change from 'extends' to wrapping...
19613  *   
19614  *  
19615  *
19616  
19617  
19618  * @constructor
19619  * Create a new ComboBoxArray.
19620  * @param {Object} config Configuration options
19621  */
19622  
19623
19624 Roo.form.ComboBoxArray = function(config)
19625 {
19626     this.addEvents({
19627         /**
19628          * @event beforeremove
19629          * Fires before remove the value from the list
19630              * @param {Roo.form.ComboBoxArray} _self This combo box array
19631              * @param {Roo.form.ComboBoxArray.Item} item removed item
19632              */
19633         'beforeremove' : true,
19634         /**
19635          * @event remove
19636          * Fires when remove the value from the list
19637              * @param {Roo.form.ComboBoxArray} _self This combo box array
19638              * @param {Roo.form.ComboBoxArray.Item} item removed item
19639              */
19640         'remove' : true
19641         
19642         
19643     });
19644     
19645     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19646     
19647     this.items = new Roo.util.MixedCollection(false);
19648     
19649     // construct the child combo...
19650     
19651     
19652     
19653     
19654    
19655     
19656 }
19657
19658  
19659 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19660
19661     /**
19662      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19663      */
19664     
19665     lastData : false,
19666     
19667     // behavies liek a hiddne field
19668     inputType:      'hidden',
19669     /**
19670      * @cfg {Number} width The width of the box that displays the selected element
19671      */ 
19672     width:          300,
19673
19674     
19675     
19676     /**
19677      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19678      */
19679     name : false,
19680     /**
19681      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19682      */
19683     hiddenName : false,
19684     
19685     
19686     // private the array of items that are displayed..
19687     items  : false,
19688     // private - the hidden field el.
19689     hiddenEl : false,
19690     // private - the filed el..
19691     el : false,
19692     
19693     //validateValue : function() { return true; }, // all values are ok!
19694     //onAddClick: function() { },
19695     
19696     onRender : function(ct, position) 
19697     {
19698         
19699         // create the standard hidden element
19700         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19701         
19702         
19703         // give fake names to child combo;
19704         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19705         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
19706         
19707         this.combo = Roo.factory(this.combo, Roo.form);
19708         this.combo.onRender(ct, position);
19709         if (typeof(this.combo.width) != 'undefined') {
19710             this.combo.onResize(this.combo.width,0);
19711         }
19712         
19713         this.combo.initEvents();
19714         
19715         // assigned so form know we need to do this..
19716         this.store          = this.combo.store;
19717         this.valueField     = this.combo.valueField;
19718         this.displayField   = this.combo.displayField ;
19719         
19720         
19721         this.combo.wrap.addClass('x-cbarray-grp');
19722         
19723         var cbwrap = this.combo.wrap.createChild(
19724             {tag: 'div', cls: 'x-cbarray-cb'},
19725             this.combo.el.dom
19726         );
19727         
19728              
19729         this.hiddenEl = this.combo.wrap.createChild({
19730             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19731         });
19732         this.el = this.combo.wrap.createChild({
19733             tag: 'input',  type:'hidden' , name: this.name, value : ''
19734         });
19735          //   this.el.dom.removeAttribute("name");
19736         
19737         
19738         this.outerWrap = this.combo.wrap;
19739         this.wrap = cbwrap;
19740         
19741         this.outerWrap.setWidth(this.width);
19742         this.outerWrap.dom.removeChild(this.el.dom);
19743         
19744         this.wrap.dom.appendChild(this.el.dom);
19745         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19746         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19747         
19748         this.combo.trigger.setStyle('position','relative');
19749         this.combo.trigger.setStyle('left', '0px');
19750         this.combo.trigger.setStyle('top', '2px');
19751         
19752         this.combo.el.setStyle('vertical-align', 'text-bottom');
19753         
19754         //this.trigger.setStyle('vertical-align', 'top');
19755         
19756         // this should use the code from combo really... on('add' ....)
19757         if (this.adder) {
19758             
19759         
19760             this.adder = this.outerWrap.createChild(
19761                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19762             var _t = this;
19763             this.adder.on('click', function(e) {
19764                 _t.fireEvent('adderclick', this, e);
19765             }, _t);
19766         }
19767         //var _t = this;
19768         //this.adder.on('click', this.onAddClick, _t);
19769         
19770         
19771         this.combo.on('select', function(cb, rec, ix) {
19772             this.addItem(rec.data);
19773             
19774             cb.setValue('');
19775             cb.el.dom.value = '';
19776             //cb.lastData = rec.data;
19777             // add to list
19778             
19779         }, this);
19780         
19781         
19782     },
19783     
19784     
19785     getName: function()
19786     {
19787         // returns hidden if it's set..
19788         if (!this.rendered) {return ''};
19789         return  this.hiddenName ? this.hiddenName : this.name;
19790         
19791     },
19792     
19793     
19794     onResize: function(w, h){
19795         
19796         return;
19797         // not sure if this is needed..
19798         //this.combo.onResize(w,h);
19799         
19800         if(typeof w != 'number'){
19801             // we do not handle it!?!?
19802             return;
19803         }
19804         var tw = this.combo.trigger.getWidth();
19805         tw += this.addicon ? this.addicon.getWidth() : 0;
19806         tw += this.editicon ? this.editicon.getWidth() : 0;
19807         var x = w - tw;
19808         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19809             
19810         this.combo.trigger.setStyle('left', '0px');
19811         
19812         if(this.list && this.listWidth === undefined){
19813             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19814             this.list.setWidth(lw);
19815             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19816         }
19817         
19818     
19819         
19820     },
19821     
19822     addItem: function(rec)
19823     {
19824         var valueField = this.combo.valueField;
19825         var displayField = this.combo.displayField;
19826         if (this.items.indexOfKey(rec[valueField]) > -1) {
19827             //console.log("GOT " + rec.data.id);
19828             return;
19829         }
19830         
19831         var x = new Roo.form.ComboBoxArray.Item({
19832             //id : rec[this.idField],
19833             data : rec,
19834             displayField : displayField ,
19835             tipField : displayField ,
19836             cb : this
19837         });
19838         // use the 
19839         this.items.add(rec[valueField],x);
19840         // add it before the element..
19841         this.updateHiddenEl();
19842         x.render(this.outerWrap, this.wrap.dom);
19843         // add the image handler..
19844     },
19845     
19846     updateHiddenEl : function()
19847     {
19848         this.validate();
19849         if (!this.hiddenEl) {
19850             return;
19851         }
19852         var ar = [];
19853         var idField = this.combo.valueField;
19854         
19855         this.items.each(function(f) {
19856             ar.push(f.data[idField]);
19857            
19858         });
19859         this.hiddenEl.dom.value = ar.join(',');
19860         this.validate();
19861     },
19862     
19863     reset : function()
19864     {
19865         this.items.clear();
19866         
19867         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19868            el.remove();
19869         });
19870         
19871         this.el.dom.value = '';
19872         if (this.hiddenEl) {
19873             this.hiddenEl.dom.value = '';
19874         }
19875         
19876     },
19877     getValue: function()
19878     {
19879         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19880     },
19881     setValue: function(v) // not a valid action - must use addItems..
19882     {
19883          
19884         this.reset();
19885         
19886         
19887         
19888         if (this.store.isLocal && (typeof(v) == 'string')) {
19889             // then we can use the store to find the values..
19890             // comma seperated at present.. this needs to allow JSON based encoding..
19891             this.hiddenEl.value  = v;
19892             var v_ar = [];
19893             Roo.each(v.split(','), function(k) {
19894                 Roo.log("CHECK " + this.valueField + ',' + k);
19895                 var li = this.store.query(this.valueField, k);
19896                 if (!li.length) {
19897                     return;
19898                 }
19899                 var add = {};
19900                 add[this.valueField] = k;
19901                 add[this.displayField] = li.item(0).data[this.displayField];
19902                 
19903                 this.addItem(add);
19904             }, this) 
19905              
19906         }
19907         if (typeof(v) == 'object' ) {
19908             // then let's assume it's an array of objects..
19909             Roo.each(v, function(l) {
19910                 this.addItem(l);
19911             }, this);
19912              
19913         }
19914         
19915         
19916     },
19917     setFromData: function(v)
19918     {
19919         // this recieves an object, if setValues is called.
19920         this.reset();
19921         this.el.dom.value = v[this.displayField];
19922         this.hiddenEl.dom.value = v[this.valueField];
19923         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19924             return;
19925         }
19926         var kv = v[this.valueField];
19927         var dv = v[this.displayField];
19928         kv = typeof(kv) != 'string' ? '' : kv;
19929         dv = typeof(dv) != 'string' ? '' : dv;
19930         
19931         
19932         var keys = kv.split(',');
19933         var display = dv.split(',');
19934         for (var i = 0 ; i < keys.length; i++) {
19935             
19936             add = {};
19937             add[this.valueField] = keys[i];
19938             add[this.displayField] = display[i];
19939             this.addItem(add);
19940         }
19941       
19942         
19943     },
19944     
19945     /**
19946      * Validates the combox array value
19947      * @return {Boolean} True if the value is valid, else false
19948      */
19949     validate : function(){
19950         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19951             this.clearInvalid();
19952             return true;
19953         }
19954         return false;
19955     },
19956     
19957     validateValue : function(value){
19958         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19959         
19960     },
19961     
19962     /*@
19963      * overide
19964      * 
19965      */
19966     isDirty : function() {
19967         if(this.disabled) {
19968             return false;
19969         }
19970         
19971         try {
19972             var d = Roo.decode(String(this.originalValue));
19973         } catch (e) {
19974             return String(this.getValue()) !== String(this.originalValue);
19975         }
19976         
19977         var originalValue = [];
19978         
19979         for (var i = 0; i < d.length; i++){
19980             originalValue.push(d[i][this.valueField]);
19981         }
19982         
19983         return String(this.getValue()) !== String(originalValue.join(','));
19984         
19985     }
19986     
19987 });
19988
19989
19990
19991 /**
19992  * @class Roo.form.ComboBoxArray.Item
19993  * @extends Roo.BoxComponent
19994  * A selected item in the list
19995  *  Fred [x]  Brian [x]  [Pick another |v]
19996  * 
19997  * @constructor
19998  * Create a new item.
19999  * @param {Object} config Configuration options
20000  */
20001  
20002 Roo.form.ComboBoxArray.Item = function(config) {
20003     config.id = Roo.id();
20004     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20005 }
20006
20007 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20008     data : {},
20009     cb: false,
20010     displayField : false,
20011     tipField : false,
20012     
20013     
20014     defaultAutoCreate : {
20015         tag: 'div',
20016         cls: 'x-cbarray-item',
20017         cn : [ 
20018             { tag: 'div' },
20019             {
20020                 tag: 'img',
20021                 width:16,
20022                 height : 16,
20023                 src : Roo.BLANK_IMAGE_URL ,
20024                 align: 'center'
20025             }
20026         ]
20027         
20028     },
20029     
20030  
20031     onRender : function(ct, position)
20032     {
20033         Roo.form.Field.superclass.onRender.call(this, ct, position);
20034         
20035         if(!this.el){
20036             var cfg = this.getAutoCreate();
20037             this.el = ct.createChild(cfg, position);
20038         }
20039         
20040         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20041         
20042         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20043             this.cb.renderer(this.data) :
20044             String.format('{0}',this.data[this.displayField]);
20045         
20046             
20047         this.el.child('div').dom.setAttribute('qtip',
20048                         String.format('{0}',this.data[this.tipField])
20049         );
20050         
20051         this.el.child('img').on('click', this.remove, this);
20052         
20053     },
20054    
20055     remove : function()
20056     {
20057         if(this.cb.disabled){
20058             return;
20059         }
20060         
20061         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20062             this.cb.items.remove(this);
20063             this.el.child('img').un('click', this.remove, this);
20064             this.el.remove();
20065             this.cb.updateHiddenEl();
20066
20067             this.cb.fireEvent('remove', this.cb, this);
20068         }
20069         
20070     }
20071 });/*
20072  * Based on:
20073  * Ext JS Library 1.1.1
20074  * Copyright(c) 2006-2007, Ext JS, LLC.
20075  *
20076  * Originally Released Under LGPL - original licence link has changed is not relivant.
20077  *
20078  * Fork - LGPL
20079  * <script type="text/javascript">
20080  */
20081 /**
20082  * @class Roo.form.Checkbox
20083  * @extends Roo.form.Field
20084  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20085  * @constructor
20086  * Creates a new Checkbox
20087  * @param {Object} config Configuration options
20088  */
20089 Roo.form.Checkbox = function(config){
20090     Roo.form.Checkbox.superclass.constructor.call(this, config);
20091     this.addEvents({
20092         /**
20093          * @event check
20094          * Fires when the checkbox is checked or unchecked.
20095              * @param {Roo.form.Checkbox} this This checkbox
20096              * @param {Boolean} checked The new checked value
20097              */
20098         check : true
20099     });
20100 };
20101
20102 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20103     /**
20104      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20105      */
20106     focusClass : undefined,
20107     /**
20108      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20109      */
20110     fieldClass: "x-form-field",
20111     /**
20112      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20113      */
20114     checked: false,
20115     /**
20116      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20117      * {tag: "input", type: "checkbox", autocomplete: "off"})
20118      */
20119     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20120     /**
20121      * @cfg {String} boxLabel The text that appears beside the checkbox
20122      */
20123     boxLabel : "",
20124     /**
20125      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20126      */  
20127     inputValue : '1',
20128     /**
20129      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20130      */
20131      valueOff: '0', // value when not checked..
20132
20133     actionMode : 'viewEl', 
20134     //
20135     // private
20136     itemCls : 'x-menu-check-item x-form-item',
20137     groupClass : 'x-menu-group-item',
20138     inputType : 'hidden',
20139     
20140     
20141     inSetChecked: false, // check that we are not calling self...
20142     
20143     inputElement: false, // real input element?
20144     basedOn: false, // ????
20145     
20146     isFormField: true, // not sure where this is needed!!!!
20147
20148     onResize : function(){
20149         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20150         if(!this.boxLabel){
20151             this.el.alignTo(this.wrap, 'c-c');
20152         }
20153     },
20154
20155     initEvents : function(){
20156         Roo.form.Checkbox.superclass.initEvents.call(this);
20157         this.el.on("click", this.onClick,  this);
20158         this.el.on("change", this.onClick,  this);
20159     },
20160
20161
20162     getResizeEl : function(){
20163         return this.wrap;
20164     },
20165
20166     getPositionEl : function(){
20167         return this.wrap;
20168     },
20169
20170     // private
20171     onRender : function(ct, position){
20172         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20173         /*
20174         if(this.inputValue !== undefined){
20175             this.el.dom.value = this.inputValue;
20176         }
20177         */
20178         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20179         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20180         var viewEl = this.wrap.createChild({ 
20181             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20182         this.viewEl = viewEl;   
20183         this.wrap.on('click', this.onClick,  this); 
20184         
20185         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20186         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20187         
20188         
20189         
20190         if(this.boxLabel){
20191             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20192         //    viewEl.on('click', this.onClick,  this); 
20193         }
20194         //if(this.checked){
20195             this.setChecked(this.checked);
20196         //}else{
20197             //this.checked = this.el.dom;
20198         //}
20199
20200     },
20201
20202     // private
20203     initValue : Roo.emptyFn,
20204
20205     /**
20206      * Returns the checked state of the checkbox.
20207      * @return {Boolean} True if checked, else false
20208      */
20209     getValue : function(){
20210         if(this.el){
20211             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20212         }
20213         return this.valueOff;
20214         
20215     },
20216
20217         // private
20218     onClick : function(){ 
20219         if (this.disabled) {
20220             return;
20221         }
20222         this.setChecked(!this.checked);
20223
20224         //if(this.el.dom.checked != this.checked){
20225         //    this.setValue(this.el.dom.checked);
20226        // }
20227     },
20228
20229     /**
20230      * Sets the checked state of the checkbox.
20231      * On is always based on a string comparison between inputValue and the param.
20232      * @param {Boolean/String} value - the value to set 
20233      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20234      */
20235     setValue : function(v,suppressEvent){
20236         
20237         
20238         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20239         //if(this.el && this.el.dom){
20240         //    this.el.dom.checked = this.checked;
20241         //    this.el.dom.defaultChecked = this.checked;
20242         //}
20243         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20244         //this.fireEvent("check", this, this.checked);
20245     },
20246     // private..
20247     setChecked : function(state,suppressEvent)
20248     {
20249         if (this.inSetChecked) {
20250             this.checked = state;
20251             return;
20252         }
20253         
20254     
20255         if(this.wrap){
20256             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20257         }
20258         this.checked = state;
20259         if(suppressEvent !== true){
20260             this.fireEvent('check', this, state);
20261         }
20262         this.inSetChecked = true;
20263         this.el.dom.value = state ? this.inputValue : this.valueOff;
20264         this.inSetChecked = false;
20265         
20266     },
20267     // handle setting of hidden value by some other method!!?!?
20268     setFromHidden: function()
20269     {
20270         if(!this.el){
20271             return;
20272         }
20273         //console.log("SET FROM HIDDEN");
20274         //alert('setFrom hidden');
20275         this.setValue(this.el.dom.value);
20276     },
20277     
20278     onDestroy : function()
20279     {
20280         if(this.viewEl){
20281             Roo.get(this.viewEl).remove();
20282         }
20283          
20284         Roo.form.Checkbox.superclass.onDestroy.call(this);
20285     },
20286     
20287     setBoxLabel : function(str)
20288     {
20289         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20290     }
20291
20292 });/*
20293  * Based on:
20294  * Ext JS Library 1.1.1
20295  * Copyright(c) 2006-2007, Ext JS, LLC.
20296  *
20297  * Originally Released Under LGPL - original licence link has changed is not relivant.
20298  *
20299  * Fork - LGPL
20300  * <script type="text/javascript">
20301  */
20302  
20303 /**
20304  * @class Roo.form.Radio
20305  * @extends Roo.form.Checkbox
20306  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20307  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20308  * @constructor
20309  * Creates a new Radio
20310  * @param {Object} config Configuration options
20311  */
20312 Roo.form.Radio = function(){
20313     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20314 };
20315 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20316     inputType: 'radio',
20317
20318     /**
20319      * If this radio is part of a group, it will return the selected value
20320      * @return {String}
20321      */
20322     getGroupValue : function(){
20323         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20324     },
20325     
20326     
20327     onRender : function(ct, position){
20328         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20329         
20330         if(this.inputValue !== undefined){
20331             this.el.dom.value = this.inputValue;
20332         }
20333          
20334         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20335         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20336         //var viewEl = this.wrap.createChild({ 
20337         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20338         //this.viewEl = viewEl;   
20339         //this.wrap.on('click', this.onClick,  this); 
20340         
20341         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20342         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20343         
20344         
20345         
20346         if(this.boxLabel){
20347             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20348         //    viewEl.on('click', this.onClick,  this); 
20349         }
20350          if(this.checked){
20351             this.el.dom.checked =   'checked' ;
20352         }
20353          
20354     } 
20355     
20356     
20357 });//<script type="text/javascript">
20358
20359 /*
20360  * Based  Ext JS Library 1.1.1
20361  * Copyright(c) 2006-2007, Ext JS, LLC.
20362  * LGPL
20363  *
20364  */
20365  
20366 /**
20367  * @class Roo.HtmlEditorCore
20368  * @extends Roo.Component
20369  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20370  *
20371  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20372  */
20373
20374 Roo.HtmlEditorCore = function(config){
20375     
20376     
20377     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20378     
20379     
20380     this.addEvents({
20381         /**
20382          * @event initialize
20383          * Fires when the editor is fully initialized (including the iframe)
20384          * @param {Roo.HtmlEditorCore} this
20385          */
20386         initialize: true,
20387         /**
20388          * @event activate
20389          * Fires when the editor is first receives the focus. Any insertion must wait
20390          * until after this event.
20391          * @param {Roo.HtmlEditorCore} this
20392          */
20393         activate: true,
20394          /**
20395          * @event beforesync
20396          * Fires before the textarea is updated with content from the editor iframe. Return false
20397          * to cancel the sync.
20398          * @param {Roo.HtmlEditorCore} this
20399          * @param {String} html
20400          */
20401         beforesync: true,
20402          /**
20403          * @event beforepush
20404          * Fires before the iframe editor is updated with content from the textarea. Return false
20405          * to cancel the push.
20406          * @param {Roo.HtmlEditorCore} this
20407          * @param {String} html
20408          */
20409         beforepush: true,
20410          /**
20411          * @event sync
20412          * Fires when the textarea is updated with content from the editor iframe.
20413          * @param {Roo.HtmlEditorCore} this
20414          * @param {String} html
20415          */
20416         sync: true,
20417          /**
20418          * @event push
20419          * Fires when the iframe editor is updated with content from the textarea.
20420          * @param {Roo.HtmlEditorCore} this
20421          * @param {String} html
20422          */
20423         push: true,
20424         
20425         /**
20426          * @event editorevent
20427          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20428          * @param {Roo.HtmlEditorCore} this
20429          */
20430         editorevent: true
20431         
20432     });
20433     
20434     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20435     
20436     // defaults : white / black...
20437     this.applyBlacklists();
20438     
20439     
20440     
20441 };
20442
20443
20444 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20445
20446
20447      /**
20448      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20449      */
20450     
20451     owner : false,
20452     
20453      /**
20454      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20455      *                        Roo.resizable.
20456      */
20457     resizable : false,
20458      /**
20459      * @cfg {Number} height (in pixels)
20460      */   
20461     height: 300,
20462    /**
20463      * @cfg {Number} width (in pixels)
20464      */   
20465     width: 500,
20466     
20467     /**
20468      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20469      * 
20470      */
20471     stylesheets: false,
20472     
20473     // id of frame..
20474     frameId: false,
20475     
20476     // private properties
20477     validationEvent : false,
20478     deferHeight: true,
20479     initialized : false,
20480     activated : false,
20481     sourceEditMode : false,
20482     onFocus : Roo.emptyFn,
20483     iframePad:3,
20484     hideMode:'offsets',
20485     
20486     clearUp: true,
20487     
20488     // blacklist + whitelisted elements..
20489     black: false,
20490     white: false,
20491      
20492     bodyCls : '',
20493
20494     /**
20495      * Protected method that will not generally be called directly. It
20496      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20497      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20498      */
20499     getDocMarkup : function(){
20500         // body styles..
20501         var st = '';
20502         
20503         // inherit styels from page...?? 
20504         if (this.stylesheets === false) {
20505             
20506             Roo.get(document.head).select('style').each(function(node) {
20507                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20508             });
20509             
20510             Roo.get(document.head).select('link').each(function(node) { 
20511                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20512             });
20513             
20514         } else if (!this.stylesheets.length) {
20515                 // simple..
20516                 st = '<style type="text/css">' +
20517                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20518                    '</style>';
20519         } else { 
20520             st = '<style type="text/css">' +
20521                     this.stylesheets +
20522                 '</style>';
20523         }
20524         
20525         st +=  '<style type="text/css">' +
20526             'IMG { cursor: pointer } ' +
20527         '</style>';
20528
20529         var cls = 'roo-htmleditor-body';
20530         
20531         if(this.bodyCls.length){
20532             cls += ' ' + this.bodyCls;
20533         }
20534         
20535         return '<html><head>' + st  +
20536             //<style type="text/css">' +
20537             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20538             //'</style>' +
20539             ' </head><body class="' +  cls + '"></body></html>';
20540     },
20541
20542     // private
20543     onRender : function(ct, position)
20544     {
20545         var _t = this;
20546         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20547         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20548         
20549         
20550         this.el.dom.style.border = '0 none';
20551         this.el.dom.setAttribute('tabIndex', -1);
20552         this.el.addClass('x-hidden hide');
20553         
20554         
20555         
20556         if(Roo.isIE){ // fix IE 1px bogus margin
20557             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20558         }
20559        
20560         
20561         this.frameId = Roo.id();
20562         
20563          
20564         
20565         var iframe = this.owner.wrap.createChild({
20566             tag: 'iframe',
20567             cls: 'form-control', // bootstrap..
20568             id: this.frameId,
20569             name: this.frameId,
20570             frameBorder : 'no',
20571             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20572         }, this.el
20573         );
20574         
20575         
20576         this.iframe = iframe.dom;
20577
20578          this.assignDocWin();
20579         
20580         this.doc.designMode = 'on';
20581        
20582         this.doc.open();
20583         this.doc.write(this.getDocMarkup());
20584         this.doc.close();
20585
20586         
20587         var task = { // must defer to wait for browser to be ready
20588             run : function(){
20589                 //console.log("run task?" + this.doc.readyState);
20590                 this.assignDocWin();
20591                 if(this.doc.body || this.doc.readyState == 'complete'){
20592                     try {
20593                         this.doc.designMode="on";
20594                     } catch (e) {
20595                         return;
20596                     }
20597                     Roo.TaskMgr.stop(task);
20598                     this.initEditor.defer(10, this);
20599                 }
20600             },
20601             interval : 10,
20602             duration: 10000,
20603             scope: this
20604         };
20605         Roo.TaskMgr.start(task);
20606
20607     },
20608
20609     // private
20610     onResize : function(w, h)
20611     {
20612          Roo.log('resize: ' +w + ',' + h );
20613         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20614         if(!this.iframe){
20615             return;
20616         }
20617         if(typeof w == 'number'){
20618             
20619             this.iframe.style.width = w + 'px';
20620         }
20621         if(typeof h == 'number'){
20622             
20623             this.iframe.style.height = h + 'px';
20624             if(this.doc){
20625                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20626             }
20627         }
20628         
20629     },
20630
20631     /**
20632      * Toggles the editor between standard and source edit mode.
20633      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20634      */
20635     toggleSourceEdit : function(sourceEditMode){
20636         
20637         this.sourceEditMode = sourceEditMode === true;
20638         
20639         if(this.sourceEditMode){
20640  
20641             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20642             
20643         }else{
20644             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20645             //this.iframe.className = '';
20646             this.deferFocus();
20647         }
20648         //this.setSize(this.owner.wrap.getSize());
20649         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20650     },
20651
20652     
20653   
20654
20655     /**
20656      * Protected method that will not generally be called directly. If you need/want
20657      * custom HTML cleanup, this is the method you should override.
20658      * @param {String} html The HTML to be cleaned
20659      * return {String} The cleaned HTML
20660      */
20661     cleanHtml : function(html){
20662         html = String(html);
20663         if(html.length > 5){
20664             if(Roo.isSafari){ // strip safari nonsense
20665                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20666             }
20667         }
20668         if(html == '&nbsp;'){
20669             html = '';
20670         }
20671         return html;
20672     },
20673
20674     /**
20675      * HTML Editor -> Textarea
20676      * Protected method that will not generally be called directly. Syncs the contents
20677      * of the editor iframe with the textarea.
20678      */
20679     syncValue : function(){
20680         if(this.initialized){
20681             var bd = (this.doc.body || this.doc.documentElement);
20682             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20683             var html = bd.innerHTML;
20684             if(Roo.isSafari){
20685                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20686                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20687                 if(m && m[1]){
20688                     html = '<div style="'+m[0]+'">' + html + '</div>';
20689                 }
20690             }
20691             html = this.cleanHtml(html);
20692             // fix up the special chars.. normaly like back quotes in word...
20693             // however we do not want to do this with chinese..
20694             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20695                 var cc = b.charCodeAt();
20696                 if (
20697                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20698                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20699                     (cc >= 0xf900 && cc < 0xfb00 )
20700                 ) {
20701                         return b;
20702                 }
20703                 return "&#"+cc+";" 
20704             });
20705             if(this.owner.fireEvent('beforesync', this, html) !== false){
20706                 this.el.dom.value = html;
20707                 this.owner.fireEvent('sync', this, html);
20708             }
20709         }
20710     },
20711
20712     /**
20713      * Protected method that will not generally be called directly. Pushes the value of the textarea
20714      * into the iframe editor.
20715      */
20716     pushValue : function(){
20717         if(this.initialized){
20718             var v = this.el.dom.value.trim();
20719             
20720 //            if(v.length < 1){
20721 //                v = '&#160;';
20722 //            }
20723             
20724             if(this.owner.fireEvent('beforepush', this, v) !== false){
20725                 var d = (this.doc.body || this.doc.documentElement);
20726                 d.innerHTML = v;
20727                 this.cleanUpPaste();
20728                 this.el.dom.value = d.innerHTML;
20729                 this.owner.fireEvent('push', this, v);
20730             }
20731         }
20732     },
20733
20734     // private
20735     deferFocus : function(){
20736         this.focus.defer(10, this);
20737     },
20738
20739     // doc'ed in Field
20740     focus : function(){
20741         if(this.win && !this.sourceEditMode){
20742             this.win.focus();
20743         }else{
20744             this.el.focus();
20745         }
20746     },
20747     
20748     assignDocWin: function()
20749     {
20750         var iframe = this.iframe;
20751         
20752          if(Roo.isIE){
20753             this.doc = iframe.contentWindow.document;
20754             this.win = iframe.contentWindow;
20755         } else {
20756 //            if (!Roo.get(this.frameId)) {
20757 //                return;
20758 //            }
20759 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20760 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20761             
20762             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20763                 return;
20764             }
20765             
20766             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20767             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20768         }
20769     },
20770     
20771     // private
20772     initEditor : function(){
20773         //console.log("INIT EDITOR");
20774         this.assignDocWin();
20775         
20776         
20777         
20778         this.doc.designMode="on";
20779         this.doc.open();
20780         this.doc.write(this.getDocMarkup());
20781         this.doc.close();
20782         
20783         var dbody = (this.doc.body || this.doc.documentElement);
20784         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20785         // this copies styles from the containing element into thsi one..
20786         // not sure why we need all of this..
20787         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20788         
20789         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20790         //ss['background-attachment'] = 'fixed'; // w3c
20791         dbody.bgProperties = 'fixed'; // ie
20792         //Roo.DomHelper.applyStyles(dbody, ss);
20793         Roo.EventManager.on(this.doc, {
20794             //'mousedown': this.onEditorEvent,
20795             'mouseup': this.onEditorEvent,
20796             'dblclick': this.onEditorEvent,
20797             'click': this.onEditorEvent,
20798             'keyup': this.onEditorEvent,
20799             buffer:100,
20800             scope: this
20801         });
20802         if(Roo.isGecko){
20803             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20804         }
20805         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20806             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20807         }
20808         this.initialized = true;
20809
20810         this.owner.fireEvent('initialize', this);
20811         this.pushValue();
20812     },
20813
20814     // private
20815     onDestroy : function(){
20816         
20817         
20818         
20819         if(this.rendered){
20820             
20821             //for (var i =0; i < this.toolbars.length;i++) {
20822             //    // fixme - ask toolbars for heights?
20823             //    this.toolbars[i].onDestroy();
20824            // }
20825             
20826             //this.wrap.dom.innerHTML = '';
20827             //this.wrap.remove();
20828         }
20829     },
20830
20831     // private
20832     onFirstFocus : function(){
20833         
20834         this.assignDocWin();
20835         
20836         
20837         this.activated = true;
20838          
20839     
20840         if(Roo.isGecko){ // prevent silly gecko errors
20841             this.win.focus();
20842             var s = this.win.getSelection();
20843             if(!s.focusNode || s.focusNode.nodeType != 3){
20844                 var r = s.getRangeAt(0);
20845                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20846                 r.collapse(true);
20847                 this.deferFocus();
20848             }
20849             try{
20850                 this.execCmd('useCSS', true);
20851                 this.execCmd('styleWithCSS', false);
20852             }catch(e){}
20853         }
20854         this.owner.fireEvent('activate', this);
20855     },
20856
20857     // private
20858     adjustFont: function(btn){
20859         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20860         //if(Roo.isSafari){ // safari
20861         //    adjust *= 2;
20862        // }
20863         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20864         if(Roo.isSafari){ // safari
20865             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20866             v =  (v < 10) ? 10 : v;
20867             v =  (v > 48) ? 48 : v;
20868             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20869             
20870         }
20871         
20872         
20873         v = Math.max(1, v+adjust);
20874         
20875         this.execCmd('FontSize', v  );
20876     },
20877
20878     onEditorEvent : function(e)
20879     {
20880         this.owner.fireEvent('editorevent', this, e);
20881       //  this.updateToolbar();
20882         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20883     },
20884
20885     insertTag : function(tg)
20886     {
20887         // could be a bit smarter... -> wrap the current selected tRoo..
20888         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20889             
20890             range = this.createRange(this.getSelection());
20891             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20892             wrappingNode.appendChild(range.extractContents());
20893             range.insertNode(wrappingNode);
20894
20895             return;
20896             
20897             
20898             
20899         }
20900         this.execCmd("formatblock",   tg);
20901         
20902     },
20903     
20904     insertText : function(txt)
20905     {
20906         
20907         
20908         var range = this.createRange();
20909         range.deleteContents();
20910                //alert(Sender.getAttribute('label'));
20911                
20912         range.insertNode(this.doc.createTextNode(txt));
20913     } ,
20914     
20915      
20916
20917     /**
20918      * Executes a Midas editor command on the editor document and performs necessary focus and
20919      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20920      * @param {String} cmd The Midas command
20921      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20922      */
20923     relayCmd : function(cmd, value){
20924         this.win.focus();
20925         this.execCmd(cmd, value);
20926         this.owner.fireEvent('editorevent', this);
20927         //this.updateToolbar();
20928         this.owner.deferFocus();
20929     },
20930
20931     /**
20932      * Executes a Midas editor command directly on the editor document.
20933      * For visual commands, you should use {@link #relayCmd} instead.
20934      * <b>This should only be called after the editor is initialized.</b>
20935      * @param {String} cmd The Midas command
20936      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20937      */
20938     execCmd : function(cmd, value){
20939         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20940         this.syncValue();
20941     },
20942  
20943  
20944    
20945     /**
20946      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20947      * to insert tRoo.
20948      * @param {String} text | dom node.. 
20949      */
20950     insertAtCursor : function(text)
20951     {
20952         
20953         if(!this.activated){
20954             return;
20955         }
20956         /*
20957         if(Roo.isIE){
20958             this.win.focus();
20959             var r = this.doc.selection.createRange();
20960             if(r){
20961                 r.collapse(true);
20962                 r.pasteHTML(text);
20963                 this.syncValue();
20964                 this.deferFocus();
20965             
20966             }
20967             return;
20968         }
20969         */
20970         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20971             this.win.focus();
20972             
20973             
20974             // from jquery ui (MIT licenced)
20975             var range, node;
20976             var win = this.win;
20977             
20978             if (win.getSelection && win.getSelection().getRangeAt) {
20979                 range = win.getSelection().getRangeAt(0);
20980                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20981                 range.insertNode(node);
20982             } else if (win.document.selection && win.document.selection.createRange) {
20983                 // no firefox support
20984                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20985                 win.document.selection.createRange().pasteHTML(txt);
20986             } else {
20987                 // no firefox support
20988                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20989                 this.execCmd('InsertHTML', txt);
20990             } 
20991             
20992             this.syncValue();
20993             
20994             this.deferFocus();
20995         }
20996     },
20997  // private
20998     mozKeyPress : function(e){
20999         if(e.ctrlKey){
21000             var c = e.getCharCode(), cmd;
21001           
21002             if(c > 0){
21003                 c = String.fromCharCode(c).toLowerCase();
21004                 switch(c){
21005                     case 'b':
21006                         cmd = 'bold';
21007                         break;
21008                     case 'i':
21009                         cmd = 'italic';
21010                         break;
21011                     
21012                     case 'u':
21013                         cmd = 'underline';
21014                         break;
21015                     
21016                     case 'v':
21017                         this.cleanUpPaste.defer(100, this);
21018                         return;
21019                         
21020                 }
21021                 if(cmd){
21022                     this.win.focus();
21023                     this.execCmd(cmd);
21024                     this.deferFocus();
21025                     e.preventDefault();
21026                 }
21027                 
21028             }
21029         }
21030     },
21031
21032     // private
21033     fixKeys : function(){ // load time branching for fastest keydown performance
21034         if(Roo.isIE){
21035             return function(e){
21036                 var k = e.getKey(), r;
21037                 if(k == e.TAB){
21038                     e.stopEvent();
21039                     r = this.doc.selection.createRange();
21040                     if(r){
21041                         r.collapse(true);
21042                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21043                         this.deferFocus();
21044                     }
21045                     return;
21046                 }
21047                 
21048                 if(k == e.ENTER){
21049                     r = this.doc.selection.createRange();
21050                     if(r){
21051                         var target = r.parentElement();
21052                         if(!target || target.tagName.toLowerCase() != 'li'){
21053                             e.stopEvent();
21054                             r.pasteHTML('<br />');
21055                             r.collapse(false);
21056                             r.select();
21057                         }
21058                     }
21059                 }
21060                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21061                     this.cleanUpPaste.defer(100, this);
21062                     return;
21063                 }
21064                 
21065                 
21066             };
21067         }else if(Roo.isOpera){
21068             return function(e){
21069                 var k = e.getKey();
21070                 if(k == e.TAB){
21071                     e.stopEvent();
21072                     this.win.focus();
21073                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21074                     this.deferFocus();
21075                 }
21076                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21077                     this.cleanUpPaste.defer(100, this);
21078                     return;
21079                 }
21080                 
21081             };
21082         }else if(Roo.isSafari){
21083             return function(e){
21084                 var k = e.getKey();
21085                 
21086                 if(k == e.TAB){
21087                     e.stopEvent();
21088                     this.execCmd('InsertText','\t');
21089                     this.deferFocus();
21090                     return;
21091                 }
21092                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21093                     this.cleanUpPaste.defer(100, this);
21094                     return;
21095                 }
21096                 
21097              };
21098         }
21099     }(),
21100     
21101     getAllAncestors: function()
21102     {
21103         var p = this.getSelectedNode();
21104         var a = [];
21105         if (!p) {
21106             a.push(p); // push blank onto stack..
21107             p = this.getParentElement();
21108         }
21109         
21110         
21111         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21112             a.push(p);
21113             p = p.parentNode;
21114         }
21115         a.push(this.doc.body);
21116         return a;
21117     },
21118     lastSel : false,
21119     lastSelNode : false,
21120     
21121     
21122     getSelection : function() 
21123     {
21124         this.assignDocWin();
21125         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21126     },
21127     
21128     getSelectedNode: function() 
21129     {
21130         // this may only work on Gecko!!!
21131         
21132         // should we cache this!!!!
21133         
21134         
21135         
21136          
21137         var range = this.createRange(this.getSelection()).cloneRange();
21138         
21139         if (Roo.isIE) {
21140             var parent = range.parentElement();
21141             while (true) {
21142                 var testRange = range.duplicate();
21143                 testRange.moveToElementText(parent);
21144                 if (testRange.inRange(range)) {
21145                     break;
21146                 }
21147                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21148                     break;
21149                 }
21150                 parent = parent.parentElement;
21151             }
21152             return parent;
21153         }
21154         
21155         // is ancestor a text element.
21156         var ac =  range.commonAncestorContainer;
21157         if (ac.nodeType == 3) {
21158             ac = ac.parentNode;
21159         }
21160         
21161         var ar = ac.childNodes;
21162          
21163         var nodes = [];
21164         var other_nodes = [];
21165         var has_other_nodes = false;
21166         for (var i=0;i<ar.length;i++) {
21167             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21168                 continue;
21169             }
21170             // fullly contained node.
21171             
21172             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21173                 nodes.push(ar[i]);
21174                 continue;
21175             }
21176             
21177             // probably selected..
21178             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21179                 other_nodes.push(ar[i]);
21180                 continue;
21181             }
21182             // outer..
21183             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21184                 continue;
21185             }
21186             
21187             
21188             has_other_nodes = true;
21189         }
21190         if (!nodes.length && other_nodes.length) {
21191             nodes= other_nodes;
21192         }
21193         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21194             return false;
21195         }
21196         
21197         return nodes[0];
21198     },
21199     createRange: function(sel)
21200     {
21201         // this has strange effects when using with 
21202         // top toolbar - not sure if it's a great idea.
21203         //this.editor.contentWindow.focus();
21204         if (typeof sel != "undefined") {
21205             try {
21206                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21207             } catch(e) {
21208                 return this.doc.createRange();
21209             }
21210         } else {
21211             return this.doc.createRange();
21212         }
21213     },
21214     getParentElement: function()
21215     {
21216         
21217         this.assignDocWin();
21218         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21219         
21220         var range = this.createRange(sel);
21221          
21222         try {
21223             var p = range.commonAncestorContainer;
21224             while (p.nodeType == 3) { // text node
21225                 p = p.parentNode;
21226             }
21227             return p;
21228         } catch (e) {
21229             return null;
21230         }
21231     
21232     },
21233     /***
21234      *
21235      * Range intersection.. the hard stuff...
21236      *  '-1' = before
21237      *  '0' = hits..
21238      *  '1' = after.
21239      *         [ -- selected range --- ]
21240      *   [fail]                        [fail]
21241      *
21242      *    basically..
21243      *      if end is before start or  hits it. fail.
21244      *      if start is after end or hits it fail.
21245      *
21246      *   if either hits (but other is outside. - then it's not 
21247      *   
21248      *    
21249      **/
21250     
21251     
21252     // @see http://www.thismuchiknow.co.uk/?p=64.
21253     rangeIntersectsNode : function(range, node)
21254     {
21255         var nodeRange = node.ownerDocument.createRange();
21256         try {
21257             nodeRange.selectNode(node);
21258         } catch (e) {
21259             nodeRange.selectNodeContents(node);
21260         }
21261     
21262         var rangeStartRange = range.cloneRange();
21263         rangeStartRange.collapse(true);
21264     
21265         var rangeEndRange = range.cloneRange();
21266         rangeEndRange.collapse(false);
21267     
21268         var nodeStartRange = nodeRange.cloneRange();
21269         nodeStartRange.collapse(true);
21270     
21271         var nodeEndRange = nodeRange.cloneRange();
21272         nodeEndRange.collapse(false);
21273     
21274         return rangeStartRange.compareBoundaryPoints(
21275                  Range.START_TO_START, nodeEndRange) == -1 &&
21276                rangeEndRange.compareBoundaryPoints(
21277                  Range.START_TO_START, nodeStartRange) == 1;
21278         
21279          
21280     },
21281     rangeCompareNode : function(range, node)
21282     {
21283         var nodeRange = node.ownerDocument.createRange();
21284         try {
21285             nodeRange.selectNode(node);
21286         } catch (e) {
21287             nodeRange.selectNodeContents(node);
21288         }
21289         
21290         
21291         range.collapse(true);
21292     
21293         nodeRange.collapse(true);
21294      
21295         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21296         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21297          
21298         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21299         
21300         var nodeIsBefore   =  ss == 1;
21301         var nodeIsAfter    = ee == -1;
21302         
21303         if (nodeIsBefore && nodeIsAfter) {
21304             return 0; // outer
21305         }
21306         if (!nodeIsBefore && nodeIsAfter) {
21307             return 1; //right trailed.
21308         }
21309         
21310         if (nodeIsBefore && !nodeIsAfter) {
21311             return 2;  // left trailed.
21312         }
21313         // fully contined.
21314         return 3;
21315     },
21316
21317     // private? - in a new class?
21318     cleanUpPaste :  function()
21319     {
21320         // cleans up the whole document..
21321         Roo.log('cleanuppaste');
21322         
21323         this.cleanUpChildren(this.doc.body);
21324         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21325         if (clean != this.doc.body.innerHTML) {
21326             this.doc.body.innerHTML = clean;
21327         }
21328         
21329     },
21330     
21331     cleanWordChars : function(input) {// change the chars to hex code
21332         var he = Roo.HtmlEditorCore;
21333         
21334         var output = input;
21335         Roo.each(he.swapCodes, function(sw) { 
21336             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21337             
21338             output = output.replace(swapper, sw[1]);
21339         });
21340         
21341         return output;
21342     },
21343     
21344     
21345     cleanUpChildren : function (n)
21346     {
21347         if (!n.childNodes.length) {
21348             return;
21349         }
21350         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21351            this.cleanUpChild(n.childNodes[i]);
21352         }
21353     },
21354     
21355     
21356         
21357     
21358     cleanUpChild : function (node)
21359     {
21360         var ed = this;
21361         //console.log(node);
21362         if (node.nodeName == "#text") {
21363             // clean up silly Windows -- stuff?
21364             return; 
21365         }
21366         if (node.nodeName == "#comment") {
21367             node.parentNode.removeChild(node);
21368             // clean up silly Windows -- stuff?
21369             return; 
21370         }
21371         var lcname = node.tagName.toLowerCase();
21372         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21373         // whitelist of tags..
21374         
21375         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21376             // remove node.
21377             node.parentNode.removeChild(node);
21378             return;
21379             
21380         }
21381         
21382         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21383         
21384         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21385         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21386         
21387         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21388         //    remove_keep_children = true;
21389         //}
21390         
21391         if (remove_keep_children) {
21392             this.cleanUpChildren(node);
21393             // inserts everything just before this node...
21394             while (node.childNodes.length) {
21395                 var cn = node.childNodes[0];
21396                 node.removeChild(cn);
21397                 node.parentNode.insertBefore(cn, node);
21398             }
21399             node.parentNode.removeChild(node);
21400             return;
21401         }
21402         
21403         if (!node.attributes || !node.attributes.length) {
21404             this.cleanUpChildren(node);
21405             return;
21406         }
21407         
21408         function cleanAttr(n,v)
21409         {
21410             
21411             if (v.match(/^\./) || v.match(/^\//)) {
21412                 return;
21413             }
21414             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21415                 return;
21416             }
21417             if (v.match(/^#/)) {
21418                 return;
21419             }
21420 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21421             node.removeAttribute(n);
21422             
21423         }
21424         
21425         var cwhite = this.cwhite;
21426         var cblack = this.cblack;
21427             
21428         function cleanStyle(n,v)
21429         {
21430             if (v.match(/expression/)) { //XSS?? should we even bother..
21431                 node.removeAttribute(n);
21432                 return;
21433             }
21434             
21435             var parts = v.split(/;/);
21436             var clean = [];
21437             
21438             Roo.each(parts, function(p) {
21439                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21440                 if (!p.length) {
21441                     return true;
21442                 }
21443                 var l = p.split(':').shift().replace(/\s+/g,'');
21444                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21445                 
21446                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21447 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21448                     //node.removeAttribute(n);
21449                     return true;
21450                 }
21451                 //Roo.log()
21452                 // only allow 'c whitelisted system attributes'
21453                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21454 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21455                     //node.removeAttribute(n);
21456                     return true;
21457                 }
21458                 
21459                 
21460                  
21461                 
21462                 clean.push(p);
21463                 return true;
21464             });
21465             if (clean.length) { 
21466                 node.setAttribute(n, clean.join(';'));
21467             } else {
21468                 node.removeAttribute(n);
21469             }
21470             
21471         }
21472         
21473         
21474         for (var i = node.attributes.length-1; i > -1 ; i--) {
21475             var a = node.attributes[i];
21476             //console.log(a);
21477             
21478             if (a.name.toLowerCase().substr(0,2)=='on')  {
21479                 node.removeAttribute(a.name);
21480                 continue;
21481             }
21482             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21483                 node.removeAttribute(a.name);
21484                 continue;
21485             }
21486             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21487                 cleanAttr(a.name,a.value); // fixme..
21488                 continue;
21489             }
21490             if (a.name == 'style') {
21491                 cleanStyle(a.name,a.value);
21492                 continue;
21493             }
21494             /// clean up MS crap..
21495             // tecnically this should be a list of valid class'es..
21496             
21497             
21498             if (a.name == 'class') {
21499                 if (a.value.match(/^Mso/)) {
21500                     node.className = '';
21501                 }
21502                 
21503                 if (a.value.match(/^body$/)) {
21504                     node.className = '';
21505                 }
21506                 continue;
21507             }
21508             
21509             // style cleanup!?
21510             // class cleanup?
21511             
21512         }
21513         
21514         
21515         this.cleanUpChildren(node);
21516         
21517         
21518     },
21519     
21520     /**
21521      * Clean up MS wordisms...
21522      */
21523     cleanWord : function(node)
21524     {
21525         
21526         
21527         if (!node) {
21528             this.cleanWord(this.doc.body);
21529             return;
21530         }
21531         if (node.nodeName == "#text") {
21532             // clean up silly Windows -- stuff?
21533             return; 
21534         }
21535         if (node.nodeName == "#comment") {
21536             node.parentNode.removeChild(node);
21537             // clean up silly Windows -- stuff?
21538             return; 
21539         }
21540         
21541         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21542             node.parentNode.removeChild(node);
21543             return;
21544         }
21545         
21546         // remove - but keep children..
21547         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21548             while (node.childNodes.length) {
21549                 var cn = node.childNodes[0];
21550                 node.removeChild(cn);
21551                 node.parentNode.insertBefore(cn, node);
21552             }
21553             node.parentNode.removeChild(node);
21554             this.iterateChildren(node, this.cleanWord);
21555             return;
21556         }
21557         // clean styles
21558         if (node.className.length) {
21559             
21560             var cn = node.className.split(/\W+/);
21561             var cna = [];
21562             Roo.each(cn, function(cls) {
21563                 if (cls.match(/Mso[a-zA-Z]+/)) {
21564                     return;
21565                 }
21566                 cna.push(cls);
21567             });
21568             node.className = cna.length ? cna.join(' ') : '';
21569             if (!cna.length) {
21570                 node.removeAttribute("class");
21571             }
21572         }
21573         
21574         if (node.hasAttribute("lang")) {
21575             node.removeAttribute("lang");
21576         }
21577         
21578         if (node.hasAttribute("style")) {
21579             
21580             var styles = node.getAttribute("style").split(";");
21581             var nstyle = [];
21582             Roo.each(styles, function(s) {
21583                 if (!s.match(/:/)) {
21584                     return;
21585                 }
21586                 var kv = s.split(":");
21587                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21588                     return;
21589                 }
21590                 // what ever is left... we allow.
21591                 nstyle.push(s);
21592             });
21593             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21594             if (!nstyle.length) {
21595                 node.removeAttribute('style');
21596             }
21597         }
21598         this.iterateChildren(node, this.cleanWord);
21599         
21600         
21601         
21602     },
21603     /**
21604      * iterateChildren of a Node, calling fn each time, using this as the scole..
21605      * @param {DomNode} node node to iterate children of.
21606      * @param {Function} fn method of this class to call on each item.
21607      */
21608     iterateChildren : function(node, fn)
21609     {
21610         if (!node.childNodes.length) {
21611                 return;
21612         }
21613         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21614            fn.call(this, node.childNodes[i])
21615         }
21616     },
21617     
21618     
21619     /**
21620      * cleanTableWidths.
21621      *
21622      * Quite often pasting from word etc.. results in tables with column and widths.
21623      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21624      *
21625      */
21626     cleanTableWidths : function(node)
21627     {
21628          
21629          
21630         if (!node) {
21631             this.cleanTableWidths(this.doc.body);
21632             return;
21633         }
21634         
21635         // ignore list...
21636         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21637             return; 
21638         }
21639         Roo.log(node.tagName);
21640         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21641             this.iterateChildren(node, this.cleanTableWidths);
21642             return;
21643         }
21644         if (node.hasAttribute('width')) {
21645             node.removeAttribute('width');
21646         }
21647         
21648          
21649         if (node.hasAttribute("style")) {
21650             // pretty basic...
21651             
21652             var styles = node.getAttribute("style").split(";");
21653             var nstyle = [];
21654             Roo.each(styles, function(s) {
21655                 if (!s.match(/:/)) {
21656                     return;
21657                 }
21658                 var kv = s.split(":");
21659                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21660                     return;
21661                 }
21662                 // what ever is left... we allow.
21663                 nstyle.push(s);
21664             });
21665             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21666             if (!nstyle.length) {
21667                 node.removeAttribute('style');
21668             }
21669         }
21670         
21671         this.iterateChildren(node, this.cleanTableWidths);
21672         
21673         
21674     },
21675     
21676     
21677     
21678     
21679     domToHTML : function(currentElement, depth, nopadtext) {
21680         
21681         depth = depth || 0;
21682         nopadtext = nopadtext || false;
21683     
21684         if (!currentElement) {
21685             return this.domToHTML(this.doc.body);
21686         }
21687         
21688         //Roo.log(currentElement);
21689         var j;
21690         var allText = false;
21691         var nodeName = currentElement.nodeName;
21692         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21693         
21694         if  (nodeName == '#text') {
21695             
21696             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21697         }
21698         
21699         
21700         var ret = '';
21701         if (nodeName != 'BODY') {
21702              
21703             var i = 0;
21704             // Prints the node tagName, such as <A>, <IMG>, etc
21705             if (tagName) {
21706                 var attr = [];
21707                 for(i = 0; i < currentElement.attributes.length;i++) {
21708                     // quoting?
21709                     var aname = currentElement.attributes.item(i).name;
21710                     if (!currentElement.attributes.item(i).value.length) {
21711                         continue;
21712                     }
21713                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21714                 }
21715                 
21716                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21717             } 
21718             else {
21719                 
21720                 // eack
21721             }
21722         } else {
21723             tagName = false;
21724         }
21725         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21726             return ret;
21727         }
21728         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21729             nopadtext = true;
21730         }
21731         
21732         
21733         // Traverse the tree
21734         i = 0;
21735         var currentElementChild = currentElement.childNodes.item(i);
21736         var allText = true;
21737         var innerHTML  = '';
21738         lastnode = '';
21739         while (currentElementChild) {
21740             // Formatting code (indent the tree so it looks nice on the screen)
21741             var nopad = nopadtext;
21742             if (lastnode == 'SPAN') {
21743                 nopad  = true;
21744             }
21745             // text
21746             if  (currentElementChild.nodeName == '#text') {
21747                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21748                 toadd = nopadtext ? toadd : toadd.trim();
21749                 if (!nopad && toadd.length > 80) {
21750                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21751                 }
21752                 innerHTML  += toadd;
21753                 
21754                 i++;
21755                 currentElementChild = currentElement.childNodes.item(i);
21756                 lastNode = '';
21757                 continue;
21758             }
21759             allText = false;
21760             
21761             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21762                 
21763             // Recursively traverse the tree structure of the child node
21764             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21765             lastnode = currentElementChild.nodeName;
21766             i++;
21767             currentElementChild=currentElement.childNodes.item(i);
21768         }
21769         
21770         ret += innerHTML;
21771         
21772         if (!allText) {
21773                 // The remaining code is mostly for formatting the tree
21774             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21775         }
21776         
21777         
21778         if (tagName) {
21779             ret+= "</"+tagName+">";
21780         }
21781         return ret;
21782         
21783     },
21784         
21785     applyBlacklists : function()
21786     {
21787         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21788         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21789         
21790         this.white = [];
21791         this.black = [];
21792         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21793             if (b.indexOf(tag) > -1) {
21794                 return;
21795             }
21796             this.white.push(tag);
21797             
21798         }, this);
21799         
21800         Roo.each(w, function(tag) {
21801             if (b.indexOf(tag) > -1) {
21802                 return;
21803             }
21804             if (this.white.indexOf(tag) > -1) {
21805                 return;
21806             }
21807             this.white.push(tag);
21808             
21809         }, this);
21810         
21811         
21812         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21813             if (w.indexOf(tag) > -1) {
21814                 return;
21815             }
21816             this.black.push(tag);
21817             
21818         }, this);
21819         
21820         Roo.each(b, function(tag) {
21821             if (w.indexOf(tag) > -1) {
21822                 return;
21823             }
21824             if (this.black.indexOf(tag) > -1) {
21825                 return;
21826             }
21827             this.black.push(tag);
21828             
21829         }, this);
21830         
21831         
21832         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21833         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21834         
21835         this.cwhite = [];
21836         this.cblack = [];
21837         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21838             if (b.indexOf(tag) > -1) {
21839                 return;
21840             }
21841             this.cwhite.push(tag);
21842             
21843         }, this);
21844         
21845         Roo.each(w, function(tag) {
21846             if (b.indexOf(tag) > -1) {
21847                 return;
21848             }
21849             if (this.cwhite.indexOf(tag) > -1) {
21850                 return;
21851             }
21852             this.cwhite.push(tag);
21853             
21854         }, this);
21855         
21856         
21857         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21858             if (w.indexOf(tag) > -1) {
21859                 return;
21860             }
21861             this.cblack.push(tag);
21862             
21863         }, this);
21864         
21865         Roo.each(b, function(tag) {
21866             if (w.indexOf(tag) > -1) {
21867                 return;
21868             }
21869             if (this.cblack.indexOf(tag) > -1) {
21870                 return;
21871             }
21872             this.cblack.push(tag);
21873             
21874         }, this);
21875     },
21876     
21877     setStylesheets : function(stylesheets)
21878     {
21879         if(typeof(stylesheets) == 'string'){
21880             Roo.get(this.iframe.contentDocument.head).createChild({
21881                 tag : 'link',
21882                 rel : 'stylesheet',
21883                 type : 'text/css',
21884                 href : stylesheets
21885             });
21886             
21887             return;
21888         }
21889         var _this = this;
21890      
21891         Roo.each(stylesheets, function(s) {
21892             if(!s.length){
21893                 return;
21894             }
21895             
21896             Roo.get(_this.iframe.contentDocument.head).createChild({
21897                 tag : 'link',
21898                 rel : 'stylesheet',
21899                 type : 'text/css',
21900                 href : s
21901             });
21902         });
21903
21904         
21905     },
21906     
21907     removeStylesheets : function()
21908     {
21909         var _this = this;
21910         
21911         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21912             s.remove();
21913         });
21914     },
21915     
21916     setStyle : function(style)
21917     {
21918         Roo.get(this.iframe.contentDocument.head).createChild({
21919             tag : 'style',
21920             type : 'text/css',
21921             html : style
21922         });
21923
21924         return;
21925     }
21926     
21927     // hide stuff that is not compatible
21928     /**
21929      * @event blur
21930      * @hide
21931      */
21932     /**
21933      * @event change
21934      * @hide
21935      */
21936     /**
21937      * @event focus
21938      * @hide
21939      */
21940     /**
21941      * @event specialkey
21942      * @hide
21943      */
21944     /**
21945      * @cfg {String} fieldClass @hide
21946      */
21947     /**
21948      * @cfg {String} focusClass @hide
21949      */
21950     /**
21951      * @cfg {String} autoCreate @hide
21952      */
21953     /**
21954      * @cfg {String} inputType @hide
21955      */
21956     /**
21957      * @cfg {String} invalidClass @hide
21958      */
21959     /**
21960      * @cfg {String} invalidText @hide
21961      */
21962     /**
21963      * @cfg {String} msgFx @hide
21964      */
21965     /**
21966      * @cfg {String} validateOnBlur @hide
21967      */
21968 });
21969
21970 Roo.HtmlEditorCore.white = [
21971         'area', 'br', 'img', 'input', 'hr', 'wbr',
21972         
21973        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21974        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21975        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21976        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21977        'table',   'ul',         'xmp', 
21978        
21979        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21980       'thead',   'tr', 
21981      
21982       'dir', 'menu', 'ol', 'ul', 'dl',
21983        
21984       'embed',  'object'
21985 ];
21986
21987
21988 Roo.HtmlEditorCore.black = [
21989     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21990         'applet', // 
21991         'base',   'basefont', 'bgsound', 'blink',  'body', 
21992         'frame',  'frameset', 'head',    'html',   'ilayer', 
21993         'iframe', 'layer',  'link',     'meta',    'object',   
21994         'script', 'style' ,'title',  'xml' // clean later..
21995 ];
21996 Roo.HtmlEditorCore.clean = [
21997     'script', 'style', 'title', 'xml'
21998 ];
21999 Roo.HtmlEditorCore.remove = [
22000     'font'
22001 ];
22002 // attributes..
22003
22004 Roo.HtmlEditorCore.ablack = [
22005     'on'
22006 ];
22007     
22008 Roo.HtmlEditorCore.aclean = [ 
22009     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22010 ];
22011
22012 // protocols..
22013 Roo.HtmlEditorCore.pwhite= [
22014         'http',  'https',  'mailto'
22015 ];
22016
22017 // white listed style attributes.
22018 Roo.HtmlEditorCore.cwhite= [
22019       //  'text-align', /// default is to allow most things..
22020       
22021          
22022 //        'font-size'//??
22023 ];
22024
22025 // black listed style attributes.
22026 Roo.HtmlEditorCore.cblack= [
22027       //  'font-size' -- this can be set by the project 
22028 ];
22029
22030
22031 Roo.HtmlEditorCore.swapCodes   =[ 
22032     [    8211, "--" ], 
22033     [    8212, "--" ], 
22034     [    8216,  "'" ],  
22035     [    8217, "'" ],  
22036     [    8220, '"' ],  
22037     [    8221, '"' ],  
22038     [    8226, "*" ],  
22039     [    8230, "..." ]
22040 ]; 
22041
22042     //<script type="text/javascript">
22043
22044 /*
22045  * Ext JS Library 1.1.1
22046  * Copyright(c) 2006-2007, Ext JS, LLC.
22047  * Licence LGPL
22048  * 
22049  */
22050  
22051  
22052 Roo.form.HtmlEditor = function(config){
22053     
22054     
22055     
22056     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22057     
22058     if (!this.toolbars) {
22059         this.toolbars = [];
22060     }
22061     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22062     
22063     
22064 };
22065
22066 /**
22067  * @class Roo.form.HtmlEditor
22068  * @extends Roo.form.Field
22069  * Provides a lightweight HTML Editor component.
22070  *
22071  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22072  * 
22073  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22074  * supported by this editor.</b><br/><br/>
22075  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22076  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22077  */
22078 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22079     /**
22080      * @cfg {Boolean} clearUp
22081      */
22082     clearUp : true,
22083       /**
22084      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22085      */
22086     toolbars : false,
22087    
22088      /**
22089      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22090      *                        Roo.resizable.
22091      */
22092     resizable : false,
22093      /**
22094      * @cfg {Number} height (in pixels)
22095      */   
22096     height: 300,
22097    /**
22098      * @cfg {Number} width (in pixels)
22099      */   
22100     width: 500,
22101     
22102     /**
22103      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22104      * 
22105      */
22106     stylesheets: false,
22107     
22108     
22109      /**
22110      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22111      * 
22112      */
22113     cblack: false,
22114     /**
22115      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22116      * 
22117      */
22118     cwhite: false,
22119     
22120      /**
22121      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22122      * 
22123      */
22124     black: false,
22125     /**
22126      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22127      * 
22128      */
22129     white: false,
22130     
22131     // id of frame..
22132     frameId: false,
22133     
22134     // private properties
22135     validationEvent : false,
22136     deferHeight: true,
22137     initialized : false,
22138     activated : false,
22139     
22140     onFocus : Roo.emptyFn,
22141     iframePad:3,
22142     hideMode:'offsets',
22143     
22144     actionMode : 'container', // defaults to hiding it...
22145     
22146     defaultAutoCreate : { // modified by initCompnoent..
22147         tag: "textarea",
22148         style:"width:500px;height:300px;",
22149         autocomplete: "new-password"
22150     },
22151
22152     // private
22153     initComponent : function(){
22154         this.addEvents({
22155             /**
22156              * @event initialize
22157              * Fires when the editor is fully initialized (including the iframe)
22158              * @param {HtmlEditor} this
22159              */
22160             initialize: true,
22161             /**
22162              * @event activate
22163              * Fires when the editor is first receives the focus. Any insertion must wait
22164              * until after this event.
22165              * @param {HtmlEditor} this
22166              */
22167             activate: true,
22168              /**
22169              * @event beforesync
22170              * Fires before the textarea is updated with content from the editor iframe. Return false
22171              * to cancel the sync.
22172              * @param {HtmlEditor} this
22173              * @param {String} html
22174              */
22175             beforesync: true,
22176              /**
22177              * @event beforepush
22178              * Fires before the iframe editor is updated with content from the textarea. Return false
22179              * to cancel the push.
22180              * @param {HtmlEditor} this
22181              * @param {String} html
22182              */
22183             beforepush: true,
22184              /**
22185              * @event sync
22186              * Fires when the textarea is updated with content from the editor iframe.
22187              * @param {HtmlEditor} this
22188              * @param {String} html
22189              */
22190             sync: true,
22191              /**
22192              * @event push
22193              * Fires when the iframe editor is updated with content from the textarea.
22194              * @param {HtmlEditor} this
22195              * @param {String} html
22196              */
22197             push: true,
22198              /**
22199              * @event editmodechange
22200              * Fires when the editor switches edit modes
22201              * @param {HtmlEditor} this
22202              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22203              */
22204             editmodechange: true,
22205             /**
22206              * @event editorevent
22207              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22208              * @param {HtmlEditor} this
22209              */
22210             editorevent: true,
22211             /**
22212              * @event firstfocus
22213              * Fires when on first focus - needed by toolbars..
22214              * @param {HtmlEditor} this
22215              */
22216             firstfocus: true,
22217             /**
22218              * @event autosave
22219              * Auto save the htmlEditor value as a file into Events
22220              * @param {HtmlEditor} this
22221              */
22222             autosave: true,
22223             /**
22224              * @event savedpreview
22225              * preview the saved version of htmlEditor
22226              * @param {HtmlEditor} this
22227              */
22228             savedpreview: true,
22229             
22230             /**
22231             * @event stylesheetsclick
22232             * Fires when press the Sytlesheets button
22233             * @param {Roo.HtmlEditorCore} this
22234             */
22235             stylesheetsclick: true
22236         });
22237         this.defaultAutoCreate =  {
22238             tag: "textarea",
22239             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22240             autocomplete: "new-password"
22241         };
22242     },
22243
22244     /**
22245      * Protected method that will not generally be called directly. It
22246      * is called when the editor creates its toolbar. Override this method if you need to
22247      * add custom toolbar buttons.
22248      * @param {HtmlEditor} editor
22249      */
22250     createToolbar : function(editor){
22251         Roo.log("create toolbars");
22252         if (!editor.toolbars || !editor.toolbars.length) {
22253             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22254         }
22255         
22256         for (var i =0 ; i < editor.toolbars.length;i++) {
22257             editor.toolbars[i] = Roo.factory(
22258                     typeof(editor.toolbars[i]) == 'string' ?
22259                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22260                 Roo.form.HtmlEditor);
22261             editor.toolbars[i].init(editor);
22262         }
22263          
22264         
22265     },
22266
22267      
22268     // private
22269     onRender : function(ct, position)
22270     {
22271         var _t = this;
22272         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22273         
22274         this.wrap = this.el.wrap({
22275             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22276         });
22277         
22278         this.editorcore.onRender(ct, position);
22279          
22280         if (this.resizable) {
22281             this.resizeEl = new Roo.Resizable(this.wrap, {
22282                 pinned : true,
22283                 wrap: true,
22284                 dynamic : true,
22285                 minHeight : this.height,
22286                 height: this.height,
22287                 handles : this.resizable,
22288                 width: this.width,
22289                 listeners : {
22290                     resize : function(r, w, h) {
22291                         _t.onResize(w,h); // -something
22292                     }
22293                 }
22294             });
22295             
22296         }
22297         this.createToolbar(this);
22298        
22299         
22300         if(!this.width){
22301             this.setSize(this.wrap.getSize());
22302         }
22303         if (this.resizeEl) {
22304             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22305             // should trigger onReize..
22306         }
22307         
22308         this.keyNav = new Roo.KeyNav(this.el, {
22309             
22310             "tab" : function(e){
22311                 e.preventDefault();
22312                 
22313                 var value = this.getValue();
22314                 
22315                 var start = this.el.dom.selectionStart;
22316                 var end = this.el.dom.selectionEnd;
22317                 
22318                 if(!e.shiftKey){
22319                     
22320                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22321                     this.el.dom.setSelectionRange(end + 1, end + 1);
22322                     return;
22323                 }
22324                 
22325                 var f = value.substring(0, start).split("\t");
22326                 
22327                 if(f.pop().length != 0){
22328                     return;
22329                 }
22330                 
22331                 this.setValue(f.join("\t") + value.substring(end));
22332                 this.el.dom.setSelectionRange(start - 1, start - 1);
22333                 
22334             },
22335             
22336             "home" : function(e){
22337                 e.preventDefault();
22338                 
22339                 var curr = this.el.dom.selectionStart;
22340                 var lines = this.getValue().split("\n");
22341                 
22342                 if(!lines.length){
22343                     return;
22344                 }
22345                 
22346                 if(e.ctrlKey){
22347                     this.el.dom.setSelectionRange(0, 0);
22348                     return;
22349                 }
22350                 
22351                 var pos = 0;
22352                 
22353                 for (var i = 0; i < lines.length;i++) {
22354                     pos += lines[i].length;
22355                     
22356                     if(i != 0){
22357                         pos += 1;
22358                     }
22359                     
22360                     if(pos < curr){
22361                         continue;
22362                     }
22363                     
22364                     pos -= lines[i].length;
22365                     
22366                     break;
22367                 }
22368                 
22369                 if(!e.shiftKey){
22370                     this.el.dom.setSelectionRange(pos, pos);
22371                     return;
22372                 }
22373                 
22374                 this.el.dom.selectionStart = pos;
22375                 this.el.dom.selectionEnd = curr;
22376             },
22377             
22378             "end" : function(e){
22379                 e.preventDefault();
22380                 
22381                 var curr = this.el.dom.selectionStart;
22382                 var lines = this.getValue().split("\n");
22383                 
22384                 if(!lines.length){
22385                     return;
22386                 }
22387                 
22388                 if(e.ctrlKey){
22389                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22390                     return;
22391                 }
22392                 
22393                 var pos = 0;
22394                 
22395                 for (var i = 0; i < lines.length;i++) {
22396                     
22397                     pos += lines[i].length;
22398                     
22399                     if(i != 0){
22400                         pos += 1;
22401                     }
22402                     
22403                     if(pos < curr){
22404                         continue;
22405                     }
22406                     
22407                     break;
22408                 }
22409                 
22410                 if(!e.shiftKey){
22411                     this.el.dom.setSelectionRange(pos, pos);
22412                     return;
22413                 }
22414                 
22415                 this.el.dom.selectionStart = curr;
22416                 this.el.dom.selectionEnd = pos;
22417             },
22418
22419             scope : this,
22420
22421             doRelay : function(foo, bar, hname){
22422                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22423             },
22424
22425             forceKeyDown: true
22426         });
22427         
22428 //        if(this.autosave && this.w){
22429 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22430 //        }
22431     },
22432
22433     // private
22434     onResize : function(w, h)
22435     {
22436         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22437         var ew = false;
22438         var eh = false;
22439         
22440         if(this.el ){
22441             if(typeof w == 'number'){
22442                 var aw = w - this.wrap.getFrameWidth('lr');
22443                 this.el.setWidth(this.adjustWidth('textarea', aw));
22444                 ew = aw;
22445             }
22446             if(typeof h == 'number'){
22447                 var tbh = 0;
22448                 for (var i =0; i < this.toolbars.length;i++) {
22449                     // fixme - ask toolbars for heights?
22450                     tbh += this.toolbars[i].tb.el.getHeight();
22451                     if (this.toolbars[i].footer) {
22452                         tbh += this.toolbars[i].footer.el.getHeight();
22453                     }
22454                 }
22455                 
22456                 
22457                 
22458                 
22459                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22460                 ah -= 5; // knock a few pixes off for look..
22461 //                Roo.log(ah);
22462                 this.el.setHeight(this.adjustWidth('textarea', ah));
22463                 var eh = ah;
22464             }
22465         }
22466         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22467         this.editorcore.onResize(ew,eh);
22468         
22469     },
22470
22471     /**
22472      * Toggles the editor between standard and source edit mode.
22473      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22474      */
22475     toggleSourceEdit : function(sourceEditMode)
22476     {
22477         this.editorcore.toggleSourceEdit(sourceEditMode);
22478         
22479         if(this.editorcore.sourceEditMode){
22480             Roo.log('editor - showing textarea');
22481             
22482 //            Roo.log('in');
22483 //            Roo.log(this.syncValue());
22484             this.editorcore.syncValue();
22485             this.el.removeClass('x-hidden');
22486             this.el.dom.removeAttribute('tabIndex');
22487             this.el.focus();
22488             
22489             for (var i = 0; i < this.toolbars.length; i++) {
22490                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22491                     this.toolbars[i].tb.hide();
22492                     this.toolbars[i].footer.hide();
22493                 }
22494             }
22495             
22496         }else{
22497             Roo.log('editor - hiding textarea');
22498 //            Roo.log('out')
22499 //            Roo.log(this.pushValue()); 
22500             this.editorcore.pushValue();
22501             
22502             this.el.addClass('x-hidden');
22503             this.el.dom.setAttribute('tabIndex', -1);
22504             
22505             for (var i = 0; i < this.toolbars.length; i++) {
22506                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22507                     this.toolbars[i].tb.show();
22508                     this.toolbars[i].footer.show();
22509                 }
22510             }
22511             
22512             //this.deferFocus();
22513         }
22514         
22515         this.setSize(this.wrap.getSize());
22516         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22517         
22518         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22519     },
22520  
22521     // private (for BoxComponent)
22522     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22523
22524     // private (for BoxComponent)
22525     getResizeEl : function(){
22526         return this.wrap;
22527     },
22528
22529     // private (for BoxComponent)
22530     getPositionEl : function(){
22531         return this.wrap;
22532     },
22533
22534     // private
22535     initEvents : function(){
22536         this.originalValue = this.getValue();
22537     },
22538
22539     /**
22540      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22541      * @method
22542      */
22543     markInvalid : Roo.emptyFn,
22544     /**
22545      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22546      * @method
22547      */
22548     clearInvalid : Roo.emptyFn,
22549
22550     setValue : function(v){
22551         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22552         this.editorcore.pushValue();
22553     },
22554
22555      
22556     // private
22557     deferFocus : function(){
22558         this.focus.defer(10, this);
22559     },
22560
22561     // doc'ed in Field
22562     focus : function(){
22563         this.editorcore.focus();
22564         
22565     },
22566       
22567
22568     // private
22569     onDestroy : function(){
22570         
22571         
22572         
22573         if(this.rendered){
22574             
22575             for (var i =0; i < this.toolbars.length;i++) {
22576                 // fixme - ask toolbars for heights?
22577                 this.toolbars[i].onDestroy();
22578             }
22579             
22580             this.wrap.dom.innerHTML = '';
22581             this.wrap.remove();
22582         }
22583     },
22584
22585     // private
22586     onFirstFocus : function(){
22587         //Roo.log("onFirstFocus");
22588         this.editorcore.onFirstFocus();
22589          for (var i =0; i < this.toolbars.length;i++) {
22590             this.toolbars[i].onFirstFocus();
22591         }
22592         
22593     },
22594     
22595     // private
22596     syncValue : function()
22597     {
22598         this.editorcore.syncValue();
22599     },
22600     
22601     pushValue : function()
22602     {
22603         this.editorcore.pushValue();
22604     },
22605     
22606     setStylesheets : function(stylesheets)
22607     {
22608         this.editorcore.setStylesheets(stylesheets);
22609     },
22610     
22611     removeStylesheets : function()
22612     {
22613         this.editorcore.removeStylesheets();
22614     }
22615      
22616     
22617     // hide stuff that is not compatible
22618     /**
22619      * @event blur
22620      * @hide
22621      */
22622     /**
22623      * @event change
22624      * @hide
22625      */
22626     /**
22627      * @event focus
22628      * @hide
22629      */
22630     /**
22631      * @event specialkey
22632      * @hide
22633      */
22634     /**
22635      * @cfg {String} fieldClass @hide
22636      */
22637     /**
22638      * @cfg {String} focusClass @hide
22639      */
22640     /**
22641      * @cfg {String} autoCreate @hide
22642      */
22643     /**
22644      * @cfg {String} inputType @hide
22645      */
22646     /**
22647      * @cfg {String} invalidClass @hide
22648      */
22649     /**
22650      * @cfg {String} invalidText @hide
22651      */
22652     /**
22653      * @cfg {String} msgFx @hide
22654      */
22655     /**
22656      * @cfg {String} validateOnBlur @hide
22657      */
22658 });
22659  
22660     // <script type="text/javascript">
22661 /*
22662  * Based on
22663  * Ext JS Library 1.1.1
22664  * Copyright(c) 2006-2007, Ext JS, LLC.
22665  *  
22666  
22667  */
22668
22669 /**
22670  * @class Roo.form.HtmlEditorToolbar1
22671  * Basic Toolbar
22672  * 
22673  * Usage:
22674  *
22675  new Roo.form.HtmlEditor({
22676     ....
22677     toolbars : [
22678         new Roo.form.HtmlEditorToolbar1({
22679             disable : { fonts: 1 , format: 1, ..., ... , ...],
22680             btns : [ .... ]
22681         })
22682     }
22683      
22684  * 
22685  * @cfg {Object} disable List of elements to disable..
22686  * @cfg {Array} btns List of additional buttons.
22687  * 
22688  * 
22689  * NEEDS Extra CSS? 
22690  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22691  */
22692  
22693 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22694 {
22695     
22696     Roo.apply(this, config);
22697     
22698     // default disabled, based on 'good practice'..
22699     this.disable = this.disable || {};
22700     Roo.applyIf(this.disable, {
22701         fontSize : true,
22702         colors : true,
22703         specialElements : true
22704     });
22705     
22706     
22707     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22708     // dont call parent... till later.
22709 }
22710
22711 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22712     
22713     tb: false,
22714     
22715     rendered: false,
22716     
22717     editor : false,
22718     editorcore : false,
22719     /**
22720      * @cfg {Object} disable  List of toolbar elements to disable
22721          
22722      */
22723     disable : false,
22724     
22725     
22726      /**
22727      * @cfg {String} createLinkText The default text for the create link prompt
22728      */
22729     createLinkText : 'Please enter the URL for the link:',
22730     /**
22731      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22732      */
22733     defaultLinkValue : 'http:/'+'/',
22734    
22735     
22736       /**
22737      * @cfg {Array} fontFamilies An array of available font families
22738      */
22739     fontFamilies : [
22740         'Arial',
22741         'Courier New',
22742         'Tahoma',
22743         'Times New Roman',
22744         'Verdana'
22745     ],
22746     
22747     specialChars : [
22748            "&#169;",
22749           "&#174;",     
22750           "&#8482;",    
22751           "&#163;" ,    
22752          // "&#8212;",    
22753           "&#8230;",    
22754           "&#247;" ,    
22755         //  "&#225;" ,     ?? a acute?
22756            "&#8364;"    , //Euro
22757        //   "&#8220;"    ,
22758         //  "&#8221;"    ,
22759         //  "&#8226;"    ,
22760           "&#176;"  //   , // degrees
22761
22762          // "&#233;"     , // e ecute
22763          // "&#250;"     , // u ecute?
22764     ],
22765     
22766     specialElements : [
22767         {
22768             text: "Insert Table",
22769             xtype: 'MenuItem',
22770             xns : Roo.Menu,
22771             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22772                 
22773         },
22774         {    
22775             text: "Insert Image",
22776             xtype: 'MenuItem',
22777             xns : Roo.Menu,
22778             ihtml : '<img src="about:blank"/>'
22779             
22780         }
22781         
22782          
22783     ],
22784     
22785     
22786     inputElements : [ 
22787             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22788             "input:submit", "input:button", "select", "textarea", "label" ],
22789     formats : [
22790         ["p"] ,  
22791         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22792         ["pre"],[ "code"], 
22793         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22794         ['div'],['span']
22795     ],
22796     
22797     cleanStyles : [
22798         "font-size"
22799     ],
22800      /**
22801      * @cfg {String} defaultFont default font to use.
22802      */
22803     defaultFont: 'tahoma',
22804    
22805     fontSelect : false,
22806     
22807     
22808     formatCombo : false,
22809     
22810     init : function(editor)
22811     {
22812         this.editor = editor;
22813         this.editorcore = editor.editorcore ? editor.editorcore : editor;
22814         var editorcore = this.editorcore;
22815         
22816         var _t = this;
22817         
22818         var fid = editorcore.frameId;
22819         var etb = this;
22820         function btn(id, toggle, handler){
22821             var xid = fid + '-'+ id ;
22822             return {
22823                 id : xid,
22824                 cmd : id,
22825                 cls : 'x-btn-icon x-edit-'+id,
22826                 enableToggle:toggle !== false,
22827                 scope: _t, // was editor...
22828                 handler:handler||_t.relayBtnCmd,
22829                 clickEvent:'mousedown',
22830                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22831                 tabIndex:-1
22832             };
22833         }
22834         
22835         
22836         
22837         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
22838         this.tb = tb;
22839          // stop form submits
22840         tb.el.on('click', function(e){
22841             e.preventDefault(); // what does this do?
22842         });
22843
22844         if(!this.disable.font) { // && !Roo.isSafari){
22845             /* why no safari for fonts 
22846             editor.fontSelect = tb.el.createChild({
22847                 tag:'select',
22848                 tabIndex: -1,
22849                 cls:'x-font-select',
22850                 html: this.createFontOptions()
22851             });
22852             
22853             editor.fontSelect.on('change', function(){
22854                 var font = editor.fontSelect.dom.value;
22855                 editor.relayCmd('fontname', font);
22856                 editor.deferFocus();
22857             }, editor);
22858             
22859             tb.add(
22860                 editor.fontSelect.dom,
22861                 '-'
22862             );
22863             */
22864             
22865         };
22866         if(!this.disable.formats){
22867             this.formatCombo = new Roo.form.ComboBox({
22868                 store: new Roo.data.SimpleStore({
22869                     id : 'tag',
22870                     fields: ['tag'],
22871                     data : this.formats // from states.js
22872                 }),
22873                 blockFocus : true,
22874                 name : '',
22875                 //autoCreate : {tag: "div",  size: "20"},
22876                 displayField:'tag',
22877                 typeAhead: false,
22878                 mode: 'local',
22879                 editable : false,
22880                 triggerAction: 'all',
22881                 emptyText:'Add tag',
22882                 selectOnFocus:true,
22883                 width:135,
22884                 listeners : {
22885                     'select': function(c, r, i) {
22886                         editorcore.insertTag(r.get('tag'));
22887                         editor.focus();
22888                     }
22889                 }
22890
22891             });
22892             tb.addField(this.formatCombo);
22893             
22894         }
22895         
22896         if(!this.disable.format){
22897             tb.add(
22898                 btn('bold'),
22899                 btn('italic'),
22900                 btn('underline'),
22901                 btn('strikethrough')
22902             );
22903         };
22904         if(!this.disable.fontSize){
22905             tb.add(
22906                 '-',
22907                 
22908                 
22909                 btn('increasefontsize', false, editorcore.adjustFont),
22910                 btn('decreasefontsize', false, editorcore.adjustFont)
22911             );
22912         };
22913         
22914         
22915         if(!this.disable.colors){
22916             tb.add(
22917                 '-', {
22918                     id:editorcore.frameId +'-forecolor',
22919                     cls:'x-btn-icon x-edit-forecolor',
22920                     clickEvent:'mousedown',
22921                     tooltip: this.buttonTips['forecolor'] || undefined,
22922                     tabIndex:-1,
22923                     menu : new Roo.menu.ColorMenu({
22924                         allowReselect: true,
22925                         focus: Roo.emptyFn,
22926                         value:'000000',
22927                         plain:true,
22928                         selectHandler: function(cp, color){
22929                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
22930                             editor.deferFocus();
22931                         },
22932                         scope: editorcore,
22933                         clickEvent:'mousedown'
22934                     })
22935                 }, {
22936                     id:editorcore.frameId +'backcolor',
22937                     cls:'x-btn-icon x-edit-backcolor',
22938                     clickEvent:'mousedown',
22939                     tooltip: this.buttonTips['backcolor'] || undefined,
22940                     tabIndex:-1,
22941                     menu : new Roo.menu.ColorMenu({
22942                         focus: Roo.emptyFn,
22943                         value:'FFFFFF',
22944                         plain:true,
22945                         allowReselect: true,
22946                         selectHandler: function(cp, color){
22947                             if(Roo.isGecko){
22948                                 editorcore.execCmd('useCSS', false);
22949                                 editorcore.execCmd('hilitecolor', color);
22950                                 editorcore.execCmd('useCSS', true);
22951                                 editor.deferFocus();
22952                             }else{
22953                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
22954                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
22955                                 editor.deferFocus();
22956                             }
22957                         },
22958                         scope:editorcore,
22959                         clickEvent:'mousedown'
22960                     })
22961                 }
22962             );
22963         };
22964         // now add all the items...
22965         
22966
22967         if(!this.disable.alignments){
22968             tb.add(
22969                 '-',
22970                 btn('justifyleft'),
22971                 btn('justifycenter'),
22972                 btn('justifyright')
22973             );
22974         };
22975
22976         //if(!Roo.isSafari){
22977             if(!this.disable.links){
22978                 tb.add(
22979                     '-',
22980                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
22981                 );
22982             };
22983
22984             if(!this.disable.lists){
22985                 tb.add(
22986                     '-',
22987                     btn('insertorderedlist'),
22988                     btn('insertunorderedlist')
22989                 );
22990             }
22991             if(!this.disable.sourceEdit){
22992                 tb.add(
22993                     '-',
22994                     btn('sourceedit', true, function(btn){
22995                         this.toggleSourceEdit(btn.pressed);
22996                     })
22997                 );
22998             }
22999         //}
23000         
23001         var smenu = { };
23002         // special menu.. - needs to be tidied up..
23003         if (!this.disable.special) {
23004             smenu = {
23005                 text: "&#169;",
23006                 cls: 'x-edit-none',
23007                 
23008                 menu : {
23009                     items : []
23010                 }
23011             };
23012             for (var i =0; i < this.specialChars.length; i++) {
23013                 smenu.menu.items.push({
23014                     
23015                     html: this.specialChars[i],
23016                     handler: function(a,b) {
23017                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23018                         //editor.insertAtCursor(a.html);
23019                         
23020                     },
23021                     tabIndex:-1
23022                 });
23023             }
23024             
23025             
23026             tb.add(smenu);
23027             
23028             
23029         }
23030         
23031         var cmenu = { };
23032         if (!this.disable.cleanStyles) {
23033             cmenu = {
23034                 cls: 'x-btn-icon x-btn-clear',
23035                 
23036                 menu : {
23037                     items : []
23038                 }
23039             };
23040             for (var i =0; i < this.cleanStyles.length; i++) {
23041                 cmenu.menu.items.push({
23042                     actiontype : this.cleanStyles[i],
23043                     html: 'Remove ' + this.cleanStyles[i],
23044                     handler: function(a,b) {
23045 //                        Roo.log(a);
23046 //                        Roo.log(b);
23047                         var c = Roo.get(editorcore.doc.body);
23048                         c.select('[style]').each(function(s) {
23049                             s.dom.style.removeProperty(a.actiontype);
23050                         });
23051                         editorcore.syncValue();
23052                     },
23053                     tabIndex:-1
23054                 });
23055             }
23056              cmenu.menu.items.push({
23057                 actiontype : 'tablewidths',
23058                 html: 'Remove Table Widths',
23059                 handler: function(a,b) {
23060                     editorcore.cleanTableWidths();
23061                     editorcore.syncValue();
23062                 },
23063                 tabIndex:-1
23064             });
23065             cmenu.menu.items.push({
23066                 actiontype : 'word',
23067                 html: 'Remove MS Word Formating',
23068                 handler: function(a,b) {
23069                     editorcore.cleanWord();
23070                     editorcore.syncValue();
23071                 },
23072                 tabIndex:-1
23073             });
23074             
23075             cmenu.menu.items.push({
23076                 actiontype : 'all',
23077                 html: 'Remove All Styles',
23078                 handler: function(a,b) {
23079                     
23080                     var c = Roo.get(editorcore.doc.body);
23081                     c.select('[style]').each(function(s) {
23082                         s.dom.removeAttribute('style');
23083                     });
23084                     editorcore.syncValue();
23085                 },
23086                 tabIndex:-1
23087             });
23088             
23089             cmenu.menu.items.push({
23090                 actiontype : 'all',
23091                 html: 'Remove All CSS Classes',
23092                 handler: function(a,b) {
23093                     
23094                     var c = Roo.get(editorcore.doc.body);
23095                     c.select('[class]').each(function(s) {
23096                         s.dom.className = '';
23097                     });
23098                     editorcore.syncValue();
23099                 },
23100                 tabIndex:-1
23101             });
23102             
23103              cmenu.menu.items.push({
23104                 actiontype : 'tidy',
23105                 html: 'Tidy HTML Source',
23106                 handler: function(a,b) {
23107                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23108                     editorcore.syncValue();
23109                 },
23110                 tabIndex:-1
23111             });
23112             
23113             
23114             tb.add(cmenu);
23115         }
23116          
23117         if (!this.disable.specialElements) {
23118             var semenu = {
23119                 text: "Other;",
23120                 cls: 'x-edit-none',
23121                 menu : {
23122                     items : []
23123                 }
23124             };
23125             for (var i =0; i < this.specialElements.length; i++) {
23126                 semenu.menu.items.push(
23127                     Roo.apply({ 
23128                         handler: function(a,b) {
23129                             editor.insertAtCursor(this.ihtml);
23130                         }
23131                     }, this.specialElements[i])
23132                 );
23133                     
23134             }
23135             
23136             tb.add(semenu);
23137             
23138             
23139         }
23140          
23141         
23142         if (this.btns) {
23143             for(var i =0; i< this.btns.length;i++) {
23144                 var b = Roo.factory(this.btns[i],Roo.form);
23145                 b.cls =  'x-edit-none';
23146                 
23147                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23148                     b.cls += ' x-init-enable';
23149                 }
23150                 
23151                 b.scope = editorcore;
23152                 tb.add(b);
23153             }
23154         
23155         }
23156         
23157         
23158         
23159         // disable everything...
23160         
23161         this.tb.items.each(function(item){
23162             
23163            if(
23164                 item.id != editorcore.frameId+ '-sourceedit' && 
23165                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23166             ){
23167                 
23168                 item.disable();
23169             }
23170         });
23171         this.rendered = true;
23172         
23173         // the all the btns;
23174         editor.on('editorevent', this.updateToolbar, this);
23175         // other toolbars need to implement this..
23176         //editor.on('editmodechange', this.updateToolbar, this);
23177     },
23178     
23179     
23180     relayBtnCmd : function(btn) {
23181         this.editorcore.relayCmd(btn.cmd);
23182     },
23183     // private used internally
23184     createLink : function(){
23185         Roo.log("create link?");
23186         var url = prompt(this.createLinkText, this.defaultLinkValue);
23187         if(url && url != 'http:/'+'/'){
23188             this.editorcore.relayCmd('createlink', url);
23189         }
23190     },
23191
23192     
23193     /**
23194      * Protected method that will not generally be called directly. It triggers
23195      * a toolbar update by reading the markup state of the current selection in the editor.
23196      */
23197     updateToolbar: function(){
23198
23199         if(!this.editorcore.activated){
23200             this.editor.onFirstFocus();
23201             return;
23202         }
23203
23204         var btns = this.tb.items.map, 
23205             doc = this.editorcore.doc,
23206             frameId = this.editorcore.frameId;
23207
23208         if(!this.disable.font && !Roo.isSafari){
23209             /*
23210             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23211             if(name != this.fontSelect.dom.value){
23212                 this.fontSelect.dom.value = name;
23213             }
23214             */
23215         }
23216         if(!this.disable.format){
23217             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23218             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23219             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23220             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23221         }
23222         if(!this.disable.alignments){
23223             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23224             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23225             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23226         }
23227         if(!Roo.isSafari && !this.disable.lists){
23228             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23229             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23230         }
23231         
23232         var ans = this.editorcore.getAllAncestors();
23233         if (this.formatCombo) {
23234             
23235             
23236             var store = this.formatCombo.store;
23237             this.formatCombo.setValue("");
23238             for (var i =0; i < ans.length;i++) {
23239                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23240                     // select it..
23241                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23242                     break;
23243                 }
23244             }
23245         }
23246         
23247         
23248         
23249         // hides menus... - so this cant be on a menu...
23250         Roo.menu.MenuMgr.hideAll();
23251
23252         //this.editorsyncValue();
23253     },
23254    
23255     
23256     createFontOptions : function(){
23257         var buf = [], fs = this.fontFamilies, ff, lc;
23258         
23259         
23260         
23261         for(var i = 0, len = fs.length; i< len; i++){
23262             ff = fs[i];
23263             lc = ff.toLowerCase();
23264             buf.push(
23265                 '<option value="',lc,'" style="font-family:',ff,';"',
23266                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23267                     ff,
23268                 '</option>'
23269             );
23270         }
23271         return buf.join('');
23272     },
23273     
23274     toggleSourceEdit : function(sourceEditMode){
23275         
23276         Roo.log("toolbar toogle");
23277         if(sourceEditMode === undefined){
23278             sourceEditMode = !this.sourceEditMode;
23279         }
23280         this.sourceEditMode = sourceEditMode === true;
23281         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23282         // just toggle the button?
23283         if(btn.pressed !== this.sourceEditMode){
23284             btn.toggle(this.sourceEditMode);
23285             return;
23286         }
23287         
23288         if(sourceEditMode){
23289             Roo.log("disabling buttons");
23290             this.tb.items.each(function(item){
23291                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23292                     item.disable();
23293                 }
23294             });
23295           
23296         }else{
23297             Roo.log("enabling buttons");
23298             if(this.editorcore.initialized){
23299                 this.tb.items.each(function(item){
23300                     item.enable();
23301                 });
23302             }
23303             
23304         }
23305         Roo.log("calling toggole on editor");
23306         // tell the editor that it's been pressed..
23307         this.editor.toggleSourceEdit(sourceEditMode);
23308        
23309     },
23310      /**
23311      * Object collection of toolbar tooltips for the buttons in the editor. The key
23312      * is the command id associated with that button and the value is a valid QuickTips object.
23313      * For example:
23314 <pre><code>
23315 {
23316     bold : {
23317         title: 'Bold (Ctrl+B)',
23318         text: 'Make the selected text bold.',
23319         cls: 'x-html-editor-tip'
23320     },
23321     italic : {
23322         title: 'Italic (Ctrl+I)',
23323         text: 'Make the selected text italic.',
23324         cls: 'x-html-editor-tip'
23325     },
23326     ...
23327 </code></pre>
23328     * @type Object
23329      */
23330     buttonTips : {
23331         bold : {
23332             title: 'Bold (Ctrl+B)',
23333             text: 'Make the selected text bold.',
23334             cls: 'x-html-editor-tip'
23335         },
23336         italic : {
23337             title: 'Italic (Ctrl+I)',
23338             text: 'Make the selected text italic.',
23339             cls: 'x-html-editor-tip'
23340         },
23341         underline : {
23342             title: 'Underline (Ctrl+U)',
23343             text: 'Underline the selected text.',
23344             cls: 'x-html-editor-tip'
23345         },
23346         strikethrough : {
23347             title: 'Strikethrough',
23348             text: 'Strikethrough the selected text.',
23349             cls: 'x-html-editor-tip'
23350         },
23351         increasefontsize : {
23352             title: 'Grow Text',
23353             text: 'Increase the font size.',
23354             cls: 'x-html-editor-tip'
23355         },
23356         decreasefontsize : {
23357             title: 'Shrink Text',
23358             text: 'Decrease the font size.',
23359             cls: 'x-html-editor-tip'
23360         },
23361         backcolor : {
23362             title: 'Text Highlight Color',
23363             text: 'Change the background color of the selected text.',
23364             cls: 'x-html-editor-tip'
23365         },
23366         forecolor : {
23367             title: 'Font Color',
23368             text: 'Change the color of the selected text.',
23369             cls: 'x-html-editor-tip'
23370         },
23371         justifyleft : {
23372             title: 'Align Text Left',
23373             text: 'Align text to the left.',
23374             cls: 'x-html-editor-tip'
23375         },
23376         justifycenter : {
23377             title: 'Center Text',
23378             text: 'Center text in the editor.',
23379             cls: 'x-html-editor-tip'
23380         },
23381         justifyright : {
23382             title: 'Align Text Right',
23383             text: 'Align text to the right.',
23384             cls: 'x-html-editor-tip'
23385         },
23386         insertunorderedlist : {
23387             title: 'Bullet List',
23388             text: 'Start a bulleted list.',
23389             cls: 'x-html-editor-tip'
23390         },
23391         insertorderedlist : {
23392             title: 'Numbered List',
23393             text: 'Start a numbered list.',
23394             cls: 'x-html-editor-tip'
23395         },
23396         createlink : {
23397             title: 'Hyperlink',
23398             text: 'Make the selected text a hyperlink.',
23399             cls: 'x-html-editor-tip'
23400         },
23401         sourceedit : {
23402             title: 'Source Edit',
23403             text: 'Switch to source editing mode.',
23404             cls: 'x-html-editor-tip'
23405         }
23406     },
23407     // private
23408     onDestroy : function(){
23409         if(this.rendered){
23410             
23411             this.tb.items.each(function(item){
23412                 if(item.menu){
23413                     item.menu.removeAll();
23414                     if(item.menu.el){
23415                         item.menu.el.destroy();
23416                     }
23417                 }
23418                 item.destroy();
23419             });
23420              
23421         }
23422     },
23423     onFirstFocus: function() {
23424         this.tb.items.each(function(item){
23425            item.enable();
23426         });
23427     }
23428 });
23429
23430
23431
23432
23433 // <script type="text/javascript">
23434 /*
23435  * Based on
23436  * Ext JS Library 1.1.1
23437  * Copyright(c) 2006-2007, Ext JS, LLC.
23438  *  
23439  
23440  */
23441
23442  
23443 /**
23444  * @class Roo.form.HtmlEditor.ToolbarContext
23445  * Context Toolbar
23446  * 
23447  * Usage:
23448  *
23449  new Roo.form.HtmlEditor({
23450     ....
23451     toolbars : [
23452         { xtype: 'ToolbarStandard', styles : {} }
23453         { xtype: 'ToolbarContext', disable : {} }
23454     ]
23455 })
23456
23457      
23458  * 
23459  * @config : {Object} disable List of elements to disable.. (not done yet.)
23460  * @config : {Object} styles  Map of styles available.
23461  * 
23462  */
23463
23464 Roo.form.HtmlEditor.ToolbarContext = function(config)
23465 {
23466     
23467     Roo.apply(this, config);
23468     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23469     // dont call parent... till later.
23470     this.styles = this.styles || {};
23471 }
23472
23473  
23474
23475 Roo.form.HtmlEditor.ToolbarContext.types = {
23476     'IMG' : {
23477         width : {
23478             title: "Width",
23479             width: 40
23480         },
23481         height:  {
23482             title: "Height",
23483             width: 40
23484         },
23485         align: {
23486             title: "Align",
23487             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23488             width : 80
23489             
23490         },
23491         border: {
23492             title: "Border",
23493             width: 40
23494         },
23495         alt: {
23496             title: "Alt",
23497             width: 120
23498         },
23499         src : {
23500             title: "Src",
23501             width: 220
23502         }
23503         
23504     },
23505     'A' : {
23506         name : {
23507             title: "Name",
23508             width: 50
23509         },
23510         target:  {
23511             title: "Target",
23512             width: 120
23513         },
23514         href:  {
23515             title: "Href",
23516             width: 220
23517         } // border?
23518         
23519     },
23520     'TABLE' : {
23521         rows : {
23522             title: "Rows",
23523             width: 20
23524         },
23525         cols : {
23526             title: "Cols",
23527             width: 20
23528         },
23529         width : {
23530             title: "Width",
23531             width: 40
23532         },
23533         height : {
23534             title: "Height",
23535             width: 40
23536         },
23537         border : {
23538             title: "Border",
23539             width: 20
23540         }
23541     },
23542     'TD' : {
23543         width : {
23544             title: "Width",
23545             width: 40
23546         },
23547         height : {
23548             title: "Height",
23549             width: 40
23550         },   
23551         align: {
23552             title: "Align",
23553             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23554             width: 80
23555         },
23556         valign: {
23557             title: "Valign",
23558             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23559             width: 80
23560         },
23561         colspan: {
23562             title: "Colspan",
23563             width: 20
23564             
23565         },
23566          'font-family'  : {
23567             title : "Font",
23568             style : 'fontFamily',
23569             displayField: 'display',
23570             optname : 'font-family',
23571             width: 140
23572         }
23573     },
23574     'INPUT' : {
23575         name : {
23576             title: "name",
23577             width: 120
23578         },
23579         value : {
23580             title: "Value",
23581             width: 120
23582         },
23583         width : {
23584             title: "Width",
23585             width: 40
23586         }
23587     },
23588     'LABEL' : {
23589         'for' : {
23590             title: "For",
23591             width: 120
23592         }
23593     },
23594     'TEXTAREA' : {
23595           name : {
23596             title: "name",
23597             width: 120
23598         },
23599         rows : {
23600             title: "Rows",
23601             width: 20
23602         },
23603         cols : {
23604             title: "Cols",
23605             width: 20
23606         }
23607     },
23608     'SELECT' : {
23609         name : {
23610             title: "name",
23611             width: 120
23612         },
23613         selectoptions : {
23614             title: "Options",
23615             width: 200
23616         }
23617     },
23618     
23619     // should we really allow this??
23620     // should this just be 
23621     'BODY' : {
23622         title : {
23623             title: "Title",
23624             width: 200,
23625             disabled : true
23626         }
23627     },
23628     'SPAN' : {
23629         'font-family'  : {
23630             title : "Font",
23631             style : 'fontFamily',
23632             displayField: 'display',
23633             optname : 'font-family',
23634             width: 140
23635         }
23636     },
23637     'DIV' : {
23638         'font-family'  : {
23639             title : "Font",
23640             style : 'fontFamily',
23641             displayField: 'display',
23642             optname : 'font-family',
23643             width: 140
23644         }
23645     },
23646      'P' : {
23647         'font-family'  : {
23648             title : "Font",
23649             style : 'fontFamily',
23650             displayField: 'display',
23651             optname : 'font-family',
23652             width: 140
23653         }
23654     },
23655     
23656     '*' : {
23657         // empty..
23658     }
23659
23660 };
23661
23662 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23663 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23664
23665 Roo.form.HtmlEditor.ToolbarContext.options = {
23666         'font-family'  : [ 
23667                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23668                 [ 'Courier New', 'Courier New'],
23669                 [ 'Tahoma', 'Tahoma'],
23670                 [ 'Times New Roman,serif', 'Times'],
23671                 [ 'Verdana','Verdana' ]
23672         ]
23673 };
23674
23675 // fixme - these need to be configurable..
23676  
23677
23678 //Roo.form.HtmlEditor.ToolbarContext.types
23679
23680
23681 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23682     
23683     tb: false,
23684     
23685     rendered: false,
23686     
23687     editor : false,
23688     editorcore : false,
23689     /**
23690      * @cfg {Object} disable  List of toolbar elements to disable
23691          
23692      */
23693     disable : false,
23694     /**
23695      * @cfg {Object} styles List of styles 
23696      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23697      *
23698      * These must be defined in the page, so they get rendered correctly..
23699      * .headline { }
23700      * TD.underline { }
23701      * 
23702      */
23703     styles : false,
23704     
23705     options: false,
23706     
23707     toolbars : false,
23708     
23709     init : function(editor)
23710     {
23711         this.editor = editor;
23712         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23713         var editorcore = this.editorcore;
23714         
23715         var fid = editorcore.frameId;
23716         var etb = this;
23717         function btn(id, toggle, handler){
23718             var xid = fid + '-'+ id ;
23719             return {
23720                 id : xid,
23721                 cmd : id,
23722                 cls : 'x-btn-icon x-edit-'+id,
23723                 enableToggle:toggle !== false,
23724                 scope: editorcore, // was editor...
23725                 handler:handler||editorcore.relayBtnCmd,
23726                 clickEvent:'mousedown',
23727                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23728                 tabIndex:-1
23729             };
23730         }
23731         // create a new element.
23732         var wdiv = editor.wrap.createChild({
23733                 tag: 'div'
23734             }, editor.wrap.dom.firstChild.nextSibling, true);
23735         
23736         // can we do this more than once??
23737         
23738          // stop form submits
23739       
23740  
23741         // disable everything...
23742         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23743         this.toolbars = {};
23744            
23745         for (var i in  ty) {
23746           
23747             this.toolbars[i] = this.buildToolbar(ty[i],i);
23748         }
23749         this.tb = this.toolbars.BODY;
23750         this.tb.el.show();
23751         this.buildFooter();
23752         this.footer.show();
23753         editor.on('hide', function( ) { this.footer.hide() }, this);
23754         editor.on('show', function( ) { this.footer.show() }, this);
23755         
23756          
23757         this.rendered = true;
23758         
23759         // the all the btns;
23760         editor.on('editorevent', this.updateToolbar, this);
23761         // other toolbars need to implement this..
23762         //editor.on('editmodechange', this.updateToolbar, this);
23763     },
23764     
23765     
23766     
23767     /**
23768      * Protected method that will not generally be called directly. It triggers
23769      * a toolbar update by reading the markup state of the current selection in the editor.
23770      *
23771      * Note you can force an update by calling on('editorevent', scope, false)
23772      */
23773     updateToolbar: function(editor,ev,sel){
23774
23775         //Roo.log(ev);
23776         // capture mouse up - this is handy for selecting images..
23777         // perhaps should go somewhere else...
23778         if(!this.editorcore.activated){
23779              this.editor.onFirstFocus();
23780             return;
23781         }
23782         
23783         
23784         
23785         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23786         // selectNode - might want to handle IE?
23787         if (ev &&
23788             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23789             ev.target && ev.target.tagName == 'IMG') {
23790             // they have click on an image...
23791             // let's see if we can change the selection...
23792             sel = ev.target;
23793          
23794               var nodeRange = sel.ownerDocument.createRange();
23795             try {
23796                 nodeRange.selectNode(sel);
23797             } catch (e) {
23798                 nodeRange.selectNodeContents(sel);
23799             }
23800             //nodeRange.collapse(true);
23801             var s = this.editorcore.win.getSelection();
23802             s.removeAllRanges();
23803             s.addRange(nodeRange);
23804         }  
23805         
23806       
23807         var updateFooter = sel ? false : true;
23808         
23809         
23810         var ans = this.editorcore.getAllAncestors();
23811         
23812         // pick
23813         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23814         
23815         if (!sel) { 
23816             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
23817             sel = sel ? sel : this.editorcore.doc.body;
23818             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23819             
23820         }
23821         // pick a menu that exists..
23822         var tn = sel.tagName.toUpperCase();
23823         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23824         
23825         tn = sel.tagName.toUpperCase();
23826         
23827         var lastSel = this.tb.selectedNode;
23828         
23829         this.tb.selectedNode = sel;
23830         
23831         // if current menu does not match..
23832         
23833         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23834                 
23835             this.tb.el.hide();
23836             ///console.log("show: " + tn);
23837             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
23838             this.tb.el.show();
23839             // update name
23840             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
23841             
23842             
23843             // update attributes
23844             if (this.tb.fields) {
23845                 this.tb.fields.each(function(e) {
23846                     if (e.stylename) {
23847                         e.setValue(sel.style[e.stylename]);
23848                         return;
23849                     } 
23850                    e.setValue(sel.getAttribute(e.attrname));
23851                 });
23852             }
23853             
23854             var hasStyles = false;
23855             for(var i in this.styles) {
23856                 hasStyles = true;
23857                 break;
23858             }
23859             
23860             // update styles
23861             if (hasStyles) { 
23862                 var st = this.tb.fields.item(0);
23863                 
23864                 st.store.removeAll();
23865                
23866                 
23867                 var cn = sel.className.split(/\s+/);
23868                 
23869                 var avs = [];
23870                 if (this.styles['*']) {
23871                     
23872                     Roo.each(this.styles['*'], function(v) {
23873                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23874                     });
23875                 }
23876                 if (this.styles[tn]) { 
23877                     Roo.each(this.styles[tn], function(v) {
23878                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23879                     });
23880                 }
23881                 
23882                 st.store.loadData(avs);
23883                 st.collapse();
23884                 st.setValue(cn);
23885             }
23886             // flag our selected Node.
23887             this.tb.selectedNode = sel;
23888            
23889            
23890             Roo.menu.MenuMgr.hideAll();
23891
23892         }
23893         
23894         if (!updateFooter) {
23895             //this.footDisp.dom.innerHTML = ''; 
23896             return;
23897         }
23898         // update the footer
23899         //
23900         var html = '';
23901         
23902         this.footerEls = ans.reverse();
23903         Roo.each(this.footerEls, function(a,i) {
23904             if (!a) { return; }
23905             html += html.length ? ' &gt; '  :  '';
23906             
23907             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
23908             
23909         });
23910        
23911         // 
23912         var sz = this.footDisp.up('td').getSize();
23913         this.footDisp.dom.style.width = (sz.width -10) + 'px';
23914         this.footDisp.dom.style.marginLeft = '5px';
23915         
23916         this.footDisp.dom.style.overflow = 'hidden';
23917         
23918         this.footDisp.dom.innerHTML = html;
23919             
23920         //this.editorsyncValue();
23921     },
23922      
23923     
23924    
23925        
23926     // private
23927     onDestroy : function(){
23928         if(this.rendered){
23929             
23930             this.tb.items.each(function(item){
23931                 if(item.menu){
23932                     item.menu.removeAll();
23933                     if(item.menu.el){
23934                         item.menu.el.destroy();
23935                     }
23936                 }
23937                 item.destroy();
23938             });
23939              
23940         }
23941     },
23942     onFirstFocus: function() {
23943         // need to do this for all the toolbars..
23944         this.tb.items.each(function(item){
23945            item.enable();
23946         });
23947     },
23948     buildToolbar: function(tlist, nm)
23949     {
23950         var editor = this.editor;
23951         var editorcore = this.editorcore;
23952          // create a new element.
23953         var wdiv = editor.wrap.createChild({
23954                 tag: 'div'
23955             }, editor.wrap.dom.firstChild.nextSibling, true);
23956         
23957        
23958         var tb = new Roo.Toolbar(wdiv);
23959         // add the name..
23960         
23961         tb.add(nm+ ":&nbsp;");
23962         
23963         var styles = [];
23964         for(var i in this.styles) {
23965             styles.push(i);
23966         }
23967         
23968         // styles...
23969         if (styles && styles.length) {
23970             
23971             // this needs a multi-select checkbox...
23972             tb.addField( new Roo.form.ComboBox({
23973                 store: new Roo.data.SimpleStore({
23974                     id : 'val',
23975                     fields: ['val', 'selected'],
23976                     data : [] 
23977                 }),
23978                 name : '-roo-edit-className',
23979                 attrname : 'className',
23980                 displayField: 'val',
23981                 typeAhead: false,
23982                 mode: 'local',
23983                 editable : false,
23984                 triggerAction: 'all',
23985                 emptyText:'Select Style',
23986                 selectOnFocus:true,
23987                 width: 130,
23988                 listeners : {
23989                     'select': function(c, r, i) {
23990                         // initial support only for on class per el..
23991                         tb.selectedNode.className =  r ? r.get('val') : '';
23992                         editorcore.syncValue();
23993                     }
23994                 }
23995     
23996             }));
23997         }
23998         
23999         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24000         var tbops = tbc.options;
24001         
24002         for (var i in tlist) {
24003             
24004             var item = tlist[i];
24005             tb.add(item.title + ":&nbsp;");
24006             
24007             
24008             //optname == used so you can configure the options available..
24009             var opts = item.opts ? item.opts : false;
24010             if (item.optname) {
24011                 opts = tbops[item.optname];
24012            
24013             }
24014             
24015             if (opts) {
24016                 // opts == pulldown..
24017                 tb.addField( new Roo.form.ComboBox({
24018                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24019                         id : 'val',
24020                         fields: ['val', 'display'],
24021                         data : opts  
24022                     }),
24023                     name : '-roo-edit-' + i,
24024                     attrname : i,
24025                     stylename : item.style ? item.style : false,
24026                     displayField: item.displayField ? item.displayField : 'val',
24027                     valueField :  'val',
24028                     typeAhead: false,
24029                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24030                     editable : false,
24031                     triggerAction: 'all',
24032                     emptyText:'Select',
24033                     selectOnFocus:true,
24034                     width: item.width ? item.width  : 130,
24035                     listeners : {
24036                         'select': function(c, r, i) {
24037                             if (c.stylename) {
24038                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24039                                 return;
24040                             }
24041                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24042                         }
24043                     }
24044
24045                 }));
24046                 continue;
24047                     
24048                  
24049                 
24050                 tb.addField( new Roo.form.TextField({
24051                     name: i,
24052                     width: 100,
24053                     //allowBlank:false,
24054                     value: ''
24055                 }));
24056                 continue;
24057             }
24058             tb.addField( new Roo.form.TextField({
24059                 name: '-roo-edit-' + i,
24060                 attrname : i,
24061                 
24062                 width: item.width,
24063                 //allowBlank:true,
24064                 value: '',
24065                 listeners: {
24066                     'change' : function(f, nv, ov) {
24067                         tb.selectedNode.setAttribute(f.attrname, nv);
24068                         editorcore.syncValue();
24069                     }
24070                 }
24071             }));
24072              
24073         }
24074         
24075         var _this = this;
24076         
24077         if(nm == 'BODY'){
24078             tb.addSeparator();
24079         
24080             tb.addButton( {
24081                 text: 'Stylesheets',
24082
24083                 listeners : {
24084                     click : function ()
24085                     {
24086                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24087                     }
24088                 }
24089             });
24090         }
24091         
24092         tb.addFill();
24093         tb.addButton( {
24094             text: 'Remove Tag',
24095     
24096             listeners : {
24097                 click : function ()
24098                 {
24099                     // remove
24100                     // undo does not work.
24101                      
24102                     var sn = tb.selectedNode;
24103                     
24104                     var pn = sn.parentNode;
24105                     
24106                     var stn =  sn.childNodes[0];
24107                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24108                     while (sn.childNodes.length) {
24109                         var node = sn.childNodes[0];
24110                         sn.removeChild(node);
24111                         //Roo.log(node);
24112                         pn.insertBefore(node, sn);
24113                         
24114                     }
24115                     pn.removeChild(sn);
24116                     var range = editorcore.createRange();
24117         
24118                     range.setStart(stn,0);
24119                     range.setEnd(en,0); //????
24120                     //range.selectNode(sel);
24121                     
24122                     
24123                     var selection = editorcore.getSelection();
24124                     selection.removeAllRanges();
24125                     selection.addRange(range);
24126                     
24127                     
24128                     
24129                     //_this.updateToolbar(null, null, pn);
24130                     _this.updateToolbar(null, null, null);
24131                     _this.footDisp.dom.innerHTML = ''; 
24132                 }
24133             }
24134             
24135                     
24136                 
24137             
24138         });
24139         
24140         
24141         tb.el.on('click', function(e){
24142             e.preventDefault(); // what does this do?
24143         });
24144         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24145         tb.el.hide();
24146         tb.name = nm;
24147         // dont need to disable them... as they will get hidden
24148         return tb;
24149          
24150         
24151     },
24152     buildFooter : function()
24153     {
24154         
24155         var fel = this.editor.wrap.createChild();
24156         this.footer = new Roo.Toolbar(fel);
24157         // toolbar has scrolly on left / right?
24158         var footDisp= new Roo.Toolbar.Fill();
24159         var _t = this;
24160         this.footer.add(
24161             {
24162                 text : '&lt;',
24163                 xtype: 'Button',
24164                 handler : function() {
24165                     _t.footDisp.scrollTo('left',0,true)
24166                 }
24167             }
24168         );
24169         this.footer.add( footDisp );
24170         this.footer.add( 
24171             {
24172                 text : '&gt;',
24173                 xtype: 'Button',
24174                 handler : function() {
24175                     // no animation..
24176                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24177                 }
24178             }
24179         );
24180         var fel = Roo.get(footDisp.el);
24181         fel.addClass('x-editor-context');
24182         this.footDispWrap = fel; 
24183         this.footDispWrap.overflow  = 'hidden';
24184         
24185         this.footDisp = fel.createChild();
24186         this.footDispWrap.on('click', this.onContextClick, this)
24187         
24188         
24189     },
24190     onContextClick : function (ev,dom)
24191     {
24192         ev.preventDefault();
24193         var  cn = dom.className;
24194         //Roo.log(cn);
24195         if (!cn.match(/x-ed-loc-/)) {
24196             return;
24197         }
24198         var n = cn.split('-').pop();
24199         var ans = this.footerEls;
24200         var sel = ans[n];
24201         
24202          // pick
24203         var range = this.editorcore.createRange();
24204         
24205         range.selectNodeContents(sel);
24206         //range.selectNode(sel);
24207         
24208         
24209         var selection = this.editorcore.getSelection();
24210         selection.removeAllRanges();
24211         selection.addRange(range);
24212         
24213         
24214         
24215         this.updateToolbar(null, null, sel);
24216         
24217         
24218     }
24219     
24220     
24221     
24222     
24223     
24224 });
24225
24226
24227
24228
24229
24230 /*
24231  * Based on:
24232  * Ext JS Library 1.1.1
24233  * Copyright(c) 2006-2007, Ext JS, LLC.
24234  *
24235  * Originally Released Under LGPL - original licence link has changed is not relivant.
24236  *
24237  * Fork - LGPL
24238  * <script type="text/javascript">
24239  */
24240  
24241 /**
24242  * @class Roo.form.BasicForm
24243  * @extends Roo.util.Observable
24244  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24245  * @constructor
24246  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24247  * @param {Object} config Configuration options
24248  */
24249 Roo.form.BasicForm = function(el, config){
24250     this.allItems = [];
24251     this.childForms = [];
24252     Roo.apply(this, config);
24253     /*
24254      * The Roo.form.Field items in this form.
24255      * @type MixedCollection
24256      */
24257      
24258      
24259     this.items = new Roo.util.MixedCollection(false, function(o){
24260         return o.id || (o.id = Roo.id());
24261     });
24262     this.addEvents({
24263         /**
24264          * @event beforeaction
24265          * Fires before any action is performed. Return false to cancel the action.
24266          * @param {Form} this
24267          * @param {Action} action The action to be performed
24268          */
24269         beforeaction: true,
24270         /**
24271          * @event actionfailed
24272          * Fires when an action fails.
24273          * @param {Form} this
24274          * @param {Action} action The action that failed
24275          */
24276         actionfailed : true,
24277         /**
24278          * @event actioncomplete
24279          * Fires when an action is completed.
24280          * @param {Form} this
24281          * @param {Action} action The action that completed
24282          */
24283         actioncomplete : true
24284     });
24285     if(el){
24286         this.initEl(el);
24287     }
24288     Roo.form.BasicForm.superclass.constructor.call(this);
24289 };
24290
24291 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24292     /**
24293      * @cfg {String} method
24294      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24295      */
24296     /**
24297      * @cfg {DataReader} reader
24298      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24299      * This is optional as there is built-in support for processing JSON.
24300      */
24301     /**
24302      * @cfg {DataReader} errorReader
24303      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24304      * This is completely optional as there is built-in support for processing JSON.
24305      */
24306     /**
24307      * @cfg {String} url
24308      * The URL to use for form actions if one isn't supplied in the action options.
24309      */
24310     /**
24311      * @cfg {Boolean} fileUpload
24312      * Set to true if this form is a file upload.
24313      */
24314      
24315     /**
24316      * @cfg {Object} baseParams
24317      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24318      */
24319      /**
24320      
24321     /**
24322      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24323      */
24324     timeout: 30,
24325
24326     // private
24327     activeAction : null,
24328
24329     /**
24330      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24331      * or setValues() data instead of when the form was first created.
24332      */
24333     trackResetOnLoad : false,
24334     
24335     
24336     /**
24337      * childForms - used for multi-tab forms
24338      * @type {Array}
24339      */
24340     childForms : false,
24341     
24342     /**
24343      * allItems - full list of fields.
24344      * @type {Array}
24345      */
24346     allItems : false,
24347     
24348     /**
24349      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24350      * element by passing it or its id or mask the form itself by passing in true.
24351      * @type Mixed
24352      */
24353     waitMsgTarget : false,
24354
24355     // private
24356     initEl : function(el){
24357         this.el = Roo.get(el);
24358         this.id = this.el.id || Roo.id();
24359         this.el.on('submit', this.onSubmit, this);
24360         this.el.addClass('x-form');
24361     },
24362
24363     // private
24364     onSubmit : function(e){
24365         e.stopEvent();
24366     },
24367
24368     /**
24369      * Returns true if client-side validation on the form is successful.
24370      * @return Boolean
24371      */
24372     isValid : function(){
24373         var valid = true;
24374         this.items.each(function(f){
24375            if(!f.validate()){
24376                valid = false;
24377            }
24378         });
24379         return valid;
24380     },
24381
24382     /**
24383      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24384      * @return Boolean
24385      */
24386     isDirty : function(){
24387         var dirty = false;
24388         this.items.each(function(f){
24389            if(f.isDirty()){
24390                dirty = true;
24391                return false;
24392            }
24393         });
24394         return dirty;
24395     },
24396     
24397     /**
24398      * Returns true if any fields in this form have changed since their original load. (New version)
24399      * @return Boolean
24400      */
24401     
24402     hasChanged : function()
24403     {
24404         var dirty = false;
24405         this.items.each(function(f){
24406            if(f.hasChanged()){
24407                dirty = true;
24408                return false;
24409            }
24410         });
24411         return dirty;
24412         
24413     },
24414     /**
24415      * Resets all hasChanged to 'false' -
24416      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24417      * So hasChanged storage is only to be used for this purpose
24418      * @return Boolean
24419      */
24420     resetHasChanged : function()
24421     {
24422         this.items.each(function(f){
24423            f.resetHasChanged();
24424         });
24425         
24426     },
24427     
24428     
24429     /**
24430      * Performs a predefined action (submit or load) or custom actions you define on this form.
24431      * @param {String} actionName The name of the action type
24432      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24433      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24434      * accept other config options):
24435      * <pre>
24436 Property          Type             Description
24437 ----------------  ---------------  ----------------------------------------------------------------------------------
24438 url               String           The url for the action (defaults to the form's url)
24439 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24440 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24441 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24442                                    validate the form on the client (defaults to false)
24443      * </pre>
24444      * @return {BasicForm} this
24445      */
24446     doAction : function(action, options){
24447         if(typeof action == 'string'){
24448             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24449         }
24450         if(this.fireEvent('beforeaction', this, action) !== false){
24451             this.beforeAction(action);
24452             action.run.defer(100, action);
24453         }
24454         return this;
24455     },
24456
24457     /**
24458      * Shortcut to do a submit action.
24459      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24460      * @return {BasicForm} this
24461      */
24462     submit : function(options){
24463         this.doAction('submit', options);
24464         return this;
24465     },
24466
24467     /**
24468      * Shortcut to do a load action.
24469      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24470      * @return {BasicForm} this
24471      */
24472     load : function(options){
24473         this.doAction('load', options);
24474         return this;
24475     },
24476
24477     /**
24478      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24479      * @param {Record} record The record to edit
24480      * @return {BasicForm} this
24481      */
24482     updateRecord : function(record){
24483         record.beginEdit();
24484         var fs = record.fields;
24485         fs.each(function(f){
24486             var field = this.findField(f.name);
24487             if(field){
24488                 record.set(f.name, field.getValue());
24489             }
24490         }, this);
24491         record.endEdit();
24492         return this;
24493     },
24494
24495     /**
24496      * Loads an Roo.data.Record into this form.
24497      * @param {Record} record The record to load
24498      * @return {BasicForm} this
24499      */
24500     loadRecord : function(record){
24501         this.setValues(record.data);
24502         return this;
24503     },
24504
24505     // private
24506     beforeAction : function(action){
24507         var o = action.options;
24508         
24509        
24510         if(this.waitMsgTarget === true){
24511             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24512         }else if(this.waitMsgTarget){
24513             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24514             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24515         }else {
24516             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24517         }
24518          
24519     },
24520
24521     // private
24522     afterAction : function(action, success){
24523         this.activeAction = null;
24524         var o = action.options;
24525         
24526         if(this.waitMsgTarget === true){
24527             this.el.unmask();
24528         }else if(this.waitMsgTarget){
24529             this.waitMsgTarget.unmask();
24530         }else{
24531             Roo.MessageBox.updateProgress(1);
24532             Roo.MessageBox.hide();
24533         }
24534          
24535         if(success){
24536             if(o.reset){
24537                 this.reset();
24538             }
24539             Roo.callback(o.success, o.scope, [this, action]);
24540             this.fireEvent('actioncomplete', this, action);
24541             
24542         }else{
24543             
24544             // failure condition..
24545             // we have a scenario where updates need confirming.
24546             // eg. if a locking scenario exists..
24547             // we look for { errors : { needs_confirm : true }} in the response.
24548             if (
24549                 (typeof(action.result) != 'undefined')  &&
24550                 (typeof(action.result.errors) != 'undefined')  &&
24551                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24552            ){
24553                 var _t = this;
24554                 Roo.MessageBox.confirm(
24555                     "Change requires confirmation",
24556                     action.result.errorMsg,
24557                     function(r) {
24558                         if (r != 'yes') {
24559                             return;
24560                         }
24561                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24562                     }
24563                     
24564                 );
24565                 
24566                 
24567                 
24568                 return;
24569             }
24570             
24571             Roo.callback(o.failure, o.scope, [this, action]);
24572             // show an error message if no failed handler is set..
24573             if (!this.hasListener('actionfailed')) {
24574                 Roo.MessageBox.alert("Error",
24575                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24576                         action.result.errorMsg :
24577                         "Saving Failed, please check your entries or try again"
24578                 );
24579             }
24580             
24581             this.fireEvent('actionfailed', this, action);
24582         }
24583         
24584     },
24585
24586     /**
24587      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24588      * @param {String} id The value to search for
24589      * @return Field
24590      */
24591     findField : function(id){
24592         var field = this.items.get(id);
24593         if(!field){
24594             this.items.each(function(f){
24595                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24596                     field = f;
24597                     return false;
24598                 }
24599             });
24600         }
24601         return field || null;
24602     },
24603
24604     /**
24605      * Add a secondary form to this one, 
24606      * Used to provide tabbed forms. One form is primary, with hidden values 
24607      * which mirror the elements from the other forms.
24608      * 
24609      * @param {Roo.form.Form} form to add.
24610      * 
24611      */
24612     addForm : function(form)
24613     {
24614        
24615         if (this.childForms.indexOf(form) > -1) {
24616             // already added..
24617             return;
24618         }
24619         this.childForms.push(form);
24620         var n = '';
24621         Roo.each(form.allItems, function (fe) {
24622             
24623             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24624             if (this.findField(n)) { // already added..
24625                 return;
24626             }
24627             var add = new Roo.form.Hidden({
24628                 name : n
24629             });
24630             add.render(this.el);
24631             
24632             this.add( add );
24633         }, this);
24634         
24635     },
24636     /**
24637      * Mark fields in this form invalid in bulk.
24638      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24639      * @return {BasicForm} this
24640      */
24641     markInvalid : function(errors){
24642         if(errors instanceof Array){
24643             for(var i = 0, len = errors.length; i < len; i++){
24644                 var fieldError = errors[i];
24645                 var f = this.findField(fieldError.id);
24646                 if(f){
24647                     f.markInvalid(fieldError.msg);
24648                 }
24649             }
24650         }else{
24651             var field, id;
24652             for(id in errors){
24653                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24654                     field.markInvalid(errors[id]);
24655                 }
24656             }
24657         }
24658         Roo.each(this.childForms || [], function (f) {
24659             f.markInvalid(errors);
24660         });
24661         
24662         return this;
24663     },
24664
24665     /**
24666      * Set values for fields in this form in bulk.
24667      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24668      * @return {BasicForm} this
24669      */
24670     setValues : function(values){
24671         if(values instanceof Array){ // array of objects
24672             for(var i = 0, len = values.length; i < len; i++){
24673                 var v = values[i];
24674                 var f = this.findField(v.id);
24675                 if(f){
24676                     f.setValue(v.value);
24677                     if(this.trackResetOnLoad){
24678                         f.originalValue = f.getValue();
24679                     }
24680                 }
24681             }
24682         }else{ // object hash
24683             var field, id;
24684             for(id in values){
24685                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24686                     
24687                     if (field.setFromData && 
24688                         field.valueField && 
24689                         field.displayField &&
24690                         // combos' with local stores can 
24691                         // be queried via setValue()
24692                         // to set their value..
24693                         (field.store && !field.store.isLocal)
24694                         ) {
24695                         // it's a combo
24696                         var sd = { };
24697                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24698                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24699                         field.setFromData(sd);
24700                         
24701                     } else {
24702                         field.setValue(values[id]);
24703                     }
24704                     
24705                     
24706                     if(this.trackResetOnLoad){
24707                         field.originalValue = field.getValue();
24708                     }
24709                 }
24710             }
24711         }
24712         this.resetHasChanged();
24713         
24714         
24715         Roo.each(this.childForms || [], function (f) {
24716             f.setValues(values);
24717             f.resetHasChanged();
24718         });
24719                 
24720         return this;
24721     },
24722
24723     /**
24724      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24725      * they are returned as an array.
24726      * @param {Boolean} asString
24727      * @return {Object}
24728      */
24729     getValues : function(asString){
24730         if (this.childForms) {
24731             // copy values from the child forms
24732             Roo.each(this.childForms, function (f) {
24733                 this.setValues(f.getValues());
24734             }, this);
24735         }
24736         
24737         
24738         
24739         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24740         if(asString === true){
24741             return fs;
24742         }
24743         return Roo.urlDecode(fs);
24744     },
24745     
24746     /**
24747      * Returns the fields in this form as an object with key/value pairs. 
24748      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24749      * @return {Object}
24750      */
24751     getFieldValues : function(with_hidden)
24752     {
24753         if (this.childForms) {
24754             // copy values from the child forms
24755             // should this call getFieldValues - probably not as we do not currently copy
24756             // hidden fields when we generate..
24757             Roo.each(this.childForms, function (f) {
24758                 this.setValues(f.getValues());
24759             }, this);
24760         }
24761         
24762         var ret = {};
24763         this.items.each(function(f){
24764             if (!f.getName()) {
24765                 return;
24766             }
24767             var v = f.getValue();
24768             if (f.inputType =='radio') {
24769                 if (typeof(ret[f.getName()]) == 'undefined') {
24770                     ret[f.getName()] = ''; // empty..
24771                 }
24772                 
24773                 if (!f.el.dom.checked) {
24774                     return;
24775                     
24776                 }
24777                 v = f.el.dom.value;
24778                 
24779             }
24780             
24781             // not sure if this supported any more..
24782             if ((typeof(v) == 'object') && f.getRawValue) {
24783                 v = f.getRawValue() ; // dates..
24784             }
24785             // combo boxes where name != hiddenName...
24786             if (f.name != f.getName()) {
24787                 ret[f.name] = f.getRawValue();
24788             }
24789             ret[f.getName()] = v;
24790         });
24791         
24792         return ret;
24793     },
24794
24795     /**
24796      * Clears all invalid messages in this form.
24797      * @return {BasicForm} this
24798      */
24799     clearInvalid : function(){
24800         this.items.each(function(f){
24801            f.clearInvalid();
24802         });
24803         
24804         Roo.each(this.childForms || [], function (f) {
24805             f.clearInvalid();
24806         });
24807         
24808         
24809         return this;
24810     },
24811
24812     /**
24813      * Resets this form.
24814      * @return {BasicForm} this
24815      */
24816     reset : function(){
24817         this.items.each(function(f){
24818             f.reset();
24819         });
24820         
24821         Roo.each(this.childForms || [], function (f) {
24822             f.reset();
24823         });
24824         this.resetHasChanged();
24825         
24826         return this;
24827     },
24828
24829     /**
24830      * Add Roo.form components to this form.
24831      * @param {Field} field1
24832      * @param {Field} field2 (optional)
24833      * @param {Field} etc (optional)
24834      * @return {BasicForm} this
24835      */
24836     add : function(){
24837         this.items.addAll(Array.prototype.slice.call(arguments, 0));
24838         return this;
24839     },
24840
24841
24842     /**
24843      * Removes a field from the items collection (does NOT remove its markup).
24844      * @param {Field} field
24845      * @return {BasicForm} this
24846      */
24847     remove : function(field){
24848         this.items.remove(field);
24849         return this;
24850     },
24851
24852     /**
24853      * Looks at the fields in this form, checks them for an id attribute,
24854      * and calls applyTo on the existing dom element with that id.
24855      * @return {BasicForm} this
24856      */
24857     render : function(){
24858         this.items.each(function(f){
24859             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
24860                 f.applyTo(f.id);
24861             }
24862         });
24863         return this;
24864     },
24865
24866     /**
24867      * Calls {@link Ext#apply} for all fields in this form with the passed object.
24868      * @param {Object} values
24869      * @return {BasicForm} this
24870      */
24871     applyToFields : function(o){
24872         this.items.each(function(f){
24873            Roo.apply(f, o);
24874         });
24875         return this;
24876     },
24877
24878     /**
24879      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
24880      * @param {Object} values
24881      * @return {BasicForm} this
24882      */
24883     applyIfToFields : function(o){
24884         this.items.each(function(f){
24885            Roo.applyIf(f, o);
24886         });
24887         return this;
24888     }
24889 });
24890
24891 // back compat
24892 Roo.BasicForm = Roo.form.BasicForm;/*
24893  * Based on:
24894  * Ext JS Library 1.1.1
24895  * Copyright(c) 2006-2007, Ext JS, LLC.
24896  *
24897  * Originally Released Under LGPL - original licence link has changed is not relivant.
24898  *
24899  * Fork - LGPL
24900  * <script type="text/javascript">
24901  */
24902
24903 /**
24904  * @class Roo.form.Form
24905  * @extends Roo.form.BasicForm
24906  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
24907  * @constructor
24908  * @param {Object} config Configuration options
24909  */
24910 Roo.form.Form = function(config){
24911     var xitems =  [];
24912     if (config.items) {
24913         xitems = config.items;
24914         delete config.items;
24915     }
24916    
24917     
24918     Roo.form.Form.superclass.constructor.call(this, null, config);
24919     this.url = this.url || this.action;
24920     if(!this.root){
24921         this.root = new Roo.form.Layout(Roo.applyIf({
24922             id: Roo.id()
24923         }, config));
24924     }
24925     this.active = this.root;
24926     /**
24927      * Array of all the buttons that have been added to this form via {@link addButton}
24928      * @type Array
24929      */
24930     this.buttons = [];
24931     this.allItems = [];
24932     this.addEvents({
24933         /**
24934          * @event clientvalidation
24935          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
24936          * @param {Form} this
24937          * @param {Boolean} valid true if the form has passed client-side validation
24938          */
24939         clientvalidation: true,
24940         /**
24941          * @event rendered
24942          * Fires when the form is rendered
24943          * @param {Roo.form.Form} form
24944          */
24945         rendered : true
24946     });
24947     
24948     if (this.progressUrl) {
24949             // push a hidden field onto the list of fields..
24950             this.addxtype( {
24951                     xns: Roo.form, 
24952                     xtype : 'Hidden', 
24953                     name : 'UPLOAD_IDENTIFIER' 
24954             });
24955         }
24956         
24957     
24958     Roo.each(xitems, this.addxtype, this);
24959     
24960     
24961     
24962 };
24963
24964 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
24965     /**
24966      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
24967      */
24968     /**
24969      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
24970      */
24971     /**
24972      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
24973      */
24974     buttonAlign:'center',
24975
24976     /**
24977      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
24978      */
24979     minButtonWidth:75,
24980
24981     /**
24982      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
24983      * This property cascades to child containers if not set.
24984      */
24985     labelAlign:'left',
24986
24987     /**
24988      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
24989      * fires a looping event with that state. This is required to bind buttons to the valid
24990      * state using the config value formBind:true on the button.
24991      */
24992     monitorValid : false,
24993
24994     /**
24995      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
24996      */
24997     monitorPoll : 200,
24998     
24999     /**
25000      * @cfg {String} progressUrl - Url to return progress data 
25001      */
25002     
25003     progressUrl : false,
25004   
25005     /**
25006      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25007      * fields are added and the column is closed. If no fields are passed the column remains open
25008      * until end() is called.
25009      * @param {Object} config The config to pass to the column
25010      * @param {Field} field1 (optional)
25011      * @param {Field} field2 (optional)
25012      * @param {Field} etc (optional)
25013      * @return Column The column container object
25014      */
25015     column : function(c){
25016         var col = new Roo.form.Column(c);
25017         this.start(col);
25018         if(arguments.length > 1){ // duplicate code required because of Opera
25019             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25020             this.end();
25021         }
25022         return col;
25023     },
25024
25025     /**
25026      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25027      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25028      * until end() is called.
25029      * @param {Object} config The config to pass to the fieldset
25030      * @param {Field} field1 (optional)
25031      * @param {Field} field2 (optional)
25032      * @param {Field} etc (optional)
25033      * @return FieldSet The fieldset container object
25034      */
25035     fieldset : function(c){
25036         var fs = new Roo.form.FieldSet(c);
25037         this.start(fs);
25038         if(arguments.length > 1){ // duplicate code required because of Opera
25039             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25040             this.end();
25041         }
25042         return fs;
25043     },
25044
25045     /**
25046      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25047      * fields are added and the container is closed. If no fields are passed the container remains open
25048      * until end() is called.
25049      * @param {Object} config The config to pass to the Layout
25050      * @param {Field} field1 (optional)
25051      * @param {Field} field2 (optional)
25052      * @param {Field} etc (optional)
25053      * @return Layout The container object
25054      */
25055     container : function(c){
25056         var l = new Roo.form.Layout(c);
25057         this.start(l);
25058         if(arguments.length > 1){ // duplicate code required because of Opera
25059             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25060             this.end();
25061         }
25062         return l;
25063     },
25064
25065     /**
25066      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25067      * @param {Object} container A Roo.form.Layout or subclass of Layout
25068      * @return {Form} this
25069      */
25070     start : function(c){
25071         // cascade label info
25072         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25073         this.active.stack.push(c);
25074         c.ownerCt = this.active;
25075         this.active = c;
25076         return this;
25077     },
25078
25079     /**
25080      * Closes the current open container
25081      * @return {Form} this
25082      */
25083     end : function(){
25084         if(this.active == this.root){
25085             return this;
25086         }
25087         this.active = this.active.ownerCt;
25088         return this;
25089     },
25090
25091     /**
25092      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25093      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25094      * as the label of the field.
25095      * @param {Field} field1
25096      * @param {Field} field2 (optional)
25097      * @param {Field} etc. (optional)
25098      * @return {Form} this
25099      */
25100     add : function(){
25101         this.active.stack.push.apply(this.active.stack, arguments);
25102         this.allItems.push.apply(this.allItems,arguments);
25103         var r = [];
25104         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25105             if(a[i].isFormField){
25106                 r.push(a[i]);
25107             }
25108         }
25109         if(r.length > 0){
25110             Roo.form.Form.superclass.add.apply(this, r);
25111         }
25112         return this;
25113     },
25114     
25115
25116     
25117     
25118     
25119      /**
25120      * Find any element that has been added to a form, using it's ID or name
25121      * This can include framesets, columns etc. along with regular fields..
25122      * @param {String} id - id or name to find.
25123      
25124      * @return {Element} e - or false if nothing found.
25125      */
25126     findbyId : function(id)
25127     {
25128         var ret = false;
25129         if (!id) {
25130             return ret;
25131         }
25132         Roo.each(this.allItems, function(f){
25133             if (f.id == id || f.name == id ){
25134                 ret = f;
25135                 return false;
25136             }
25137         });
25138         return ret;
25139     },
25140
25141     
25142     
25143     /**
25144      * Render this form into the passed container. This should only be called once!
25145      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25146      * @return {Form} this
25147      */
25148     render : function(ct)
25149     {
25150         
25151         
25152         
25153         ct = Roo.get(ct);
25154         var o = this.autoCreate || {
25155             tag: 'form',
25156             method : this.method || 'POST',
25157             id : this.id || Roo.id()
25158         };
25159         this.initEl(ct.createChild(o));
25160
25161         this.root.render(this.el);
25162         
25163        
25164              
25165         this.items.each(function(f){
25166             f.render('x-form-el-'+f.id);
25167         });
25168
25169         if(this.buttons.length > 0){
25170             // tables are required to maintain order and for correct IE layout
25171             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25172                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25173                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25174             }}, null, true);
25175             var tr = tb.getElementsByTagName('tr')[0];
25176             for(var i = 0, len = this.buttons.length; i < len; i++) {
25177                 var b = this.buttons[i];
25178                 var td = document.createElement('td');
25179                 td.className = 'x-form-btn-td';
25180                 b.render(tr.appendChild(td));
25181             }
25182         }
25183         if(this.monitorValid){ // initialize after render
25184             this.startMonitoring();
25185         }
25186         this.fireEvent('rendered', this);
25187         return this;
25188     },
25189
25190     /**
25191      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25192      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25193      * object or a valid Roo.DomHelper element config
25194      * @param {Function} handler The function called when the button is clicked
25195      * @param {Object} scope (optional) The scope of the handler function
25196      * @return {Roo.Button}
25197      */
25198     addButton : function(config, handler, scope){
25199         var bc = {
25200             handler: handler,
25201             scope: scope,
25202             minWidth: this.minButtonWidth,
25203             hideParent:true
25204         };
25205         if(typeof config == "string"){
25206             bc.text = config;
25207         }else{
25208             Roo.apply(bc, config);
25209         }
25210         var btn = new Roo.Button(null, bc);
25211         this.buttons.push(btn);
25212         return btn;
25213     },
25214
25215      /**
25216      * Adds a series of form elements (using the xtype property as the factory method.
25217      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25218      * @param {Object} config 
25219      */
25220     
25221     addxtype : function()
25222     {
25223         var ar = Array.prototype.slice.call(arguments, 0);
25224         var ret = false;
25225         for(var i = 0; i < ar.length; i++) {
25226             if (!ar[i]) {
25227                 continue; // skip -- if this happends something invalid got sent, we 
25228                 // should ignore it, as basically that interface element will not show up
25229                 // and that should be pretty obvious!!
25230             }
25231             
25232             if (Roo.form[ar[i].xtype]) {
25233                 ar[i].form = this;
25234                 var fe = Roo.factory(ar[i], Roo.form);
25235                 if (!ret) {
25236                     ret = fe;
25237                 }
25238                 fe.form = this;
25239                 if (fe.store) {
25240                     fe.store.form = this;
25241                 }
25242                 if (fe.isLayout) {  
25243                          
25244                     this.start(fe);
25245                     this.allItems.push(fe);
25246                     if (fe.items && fe.addxtype) {
25247                         fe.addxtype.apply(fe, fe.items);
25248                         delete fe.items;
25249                     }
25250                      this.end();
25251                     continue;
25252                 }
25253                 
25254                 
25255                  
25256                 this.add(fe);
25257               //  console.log('adding ' + ar[i].xtype);
25258             }
25259             if (ar[i].xtype == 'Button') {  
25260                 //console.log('adding button');
25261                 //console.log(ar[i]);
25262                 this.addButton(ar[i]);
25263                 this.allItems.push(fe);
25264                 continue;
25265             }
25266             
25267             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25268                 alert('end is not supported on xtype any more, use items');
25269             //    this.end();
25270             //    //console.log('adding end');
25271             }
25272             
25273         }
25274         return ret;
25275     },
25276     
25277     /**
25278      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25279      * option "monitorValid"
25280      */
25281     startMonitoring : function(){
25282         if(!this.bound){
25283             this.bound = true;
25284             Roo.TaskMgr.start({
25285                 run : this.bindHandler,
25286                 interval : this.monitorPoll || 200,
25287                 scope: this
25288             });
25289         }
25290     },
25291
25292     /**
25293      * Stops monitoring of the valid state of this form
25294      */
25295     stopMonitoring : function(){
25296         this.bound = false;
25297     },
25298
25299     // private
25300     bindHandler : function(){
25301         if(!this.bound){
25302             return false; // stops binding
25303         }
25304         var valid = true;
25305         this.items.each(function(f){
25306             if(!f.isValid(true)){
25307                 valid = false;
25308                 return false;
25309             }
25310         });
25311         for(var i = 0, len = this.buttons.length; i < len; i++){
25312             var btn = this.buttons[i];
25313             if(btn.formBind === true && btn.disabled === valid){
25314                 btn.setDisabled(!valid);
25315             }
25316         }
25317         this.fireEvent('clientvalidation', this, valid);
25318     }
25319     
25320     
25321     
25322     
25323     
25324     
25325     
25326     
25327 });
25328
25329
25330 // back compat
25331 Roo.Form = Roo.form.Form;
25332 /*
25333  * Based on:
25334  * Ext JS Library 1.1.1
25335  * Copyright(c) 2006-2007, Ext JS, LLC.
25336  *
25337  * Originally Released Under LGPL - original licence link has changed is not relivant.
25338  *
25339  * Fork - LGPL
25340  * <script type="text/javascript">
25341  */
25342
25343 // as we use this in bootstrap.
25344 Roo.namespace('Roo.form');
25345  /**
25346  * @class Roo.form.Action
25347  * Internal Class used to handle form actions
25348  * @constructor
25349  * @param {Roo.form.BasicForm} el The form element or its id
25350  * @param {Object} config Configuration options
25351  */
25352
25353  
25354  
25355 // define the action interface
25356 Roo.form.Action = function(form, options){
25357     this.form = form;
25358     this.options = options || {};
25359 };
25360 /**
25361  * Client Validation Failed
25362  * @const 
25363  */
25364 Roo.form.Action.CLIENT_INVALID = 'client';
25365 /**
25366  * Server Validation Failed
25367  * @const 
25368  */
25369 Roo.form.Action.SERVER_INVALID = 'server';
25370  /**
25371  * Connect to Server Failed
25372  * @const 
25373  */
25374 Roo.form.Action.CONNECT_FAILURE = 'connect';
25375 /**
25376  * Reading Data from Server Failed
25377  * @const 
25378  */
25379 Roo.form.Action.LOAD_FAILURE = 'load';
25380
25381 Roo.form.Action.prototype = {
25382     type : 'default',
25383     failureType : undefined,
25384     response : undefined,
25385     result : undefined,
25386
25387     // interface method
25388     run : function(options){
25389
25390     },
25391
25392     // interface method
25393     success : function(response){
25394
25395     },
25396
25397     // interface method
25398     handleResponse : function(response){
25399
25400     },
25401
25402     // default connection failure
25403     failure : function(response){
25404         
25405         this.response = response;
25406         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25407         this.form.afterAction(this, false);
25408     },
25409
25410     processResponse : function(response){
25411         this.response = response;
25412         if(!response.responseText){
25413             return true;
25414         }
25415         this.result = this.handleResponse(response);
25416         return this.result;
25417     },
25418
25419     // utility functions used internally
25420     getUrl : function(appendParams){
25421         var url = this.options.url || this.form.url || this.form.el.dom.action;
25422         if(appendParams){
25423             var p = this.getParams();
25424             if(p){
25425                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25426             }
25427         }
25428         return url;
25429     },
25430
25431     getMethod : function(){
25432         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25433     },
25434
25435     getParams : function(){
25436         var bp = this.form.baseParams;
25437         var p = this.options.params;
25438         if(p){
25439             if(typeof p == "object"){
25440                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25441             }else if(typeof p == 'string' && bp){
25442                 p += '&' + Roo.urlEncode(bp);
25443             }
25444         }else if(bp){
25445             p = Roo.urlEncode(bp);
25446         }
25447         return p;
25448     },
25449
25450     createCallback : function(){
25451         return {
25452             success: this.success,
25453             failure: this.failure,
25454             scope: this,
25455             timeout: (this.form.timeout*1000),
25456             upload: this.form.fileUpload ? this.success : undefined
25457         };
25458     }
25459 };
25460
25461 Roo.form.Action.Submit = function(form, options){
25462     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25463 };
25464
25465 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25466     type : 'submit',
25467
25468     haveProgress : false,
25469     uploadComplete : false,
25470     
25471     // uploadProgress indicator.
25472     uploadProgress : function()
25473     {
25474         if (!this.form.progressUrl) {
25475             return;
25476         }
25477         
25478         if (!this.haveProgress) {
25479             Roo.MessageBox.progress("Uploading", "Uploading");
25480         }
25481         if (this.uploadComplete) {
25482            Roo.MessageBox.hide();
25483            return;
25484         }
25485         
25486         this.haveProgress = true;
25487    
25488         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25489         
25490         var c = new Roo.data.Connection();
25491         c.request({
25492             url : this.form.progressUrl,
25493             params: {
25494                 id : uid
25495             },
25496             method: 'GET',
25497             success : function(req){
25498                //console.log(data);
25499                 var rdata = false;
25500                 var edata;
25501                 try  {
25502                    rdata = Roo.decode(req.responseText)
25503                 } catch (e) {
25504                     Roo.log("Invalid data from server..");
25505                     Roo.log(edata);
25506                     return;
25507                 }
25508                 if (!rdata || !rdata.success) {
25509                     Roo.log(rdata);
25510                     Roo.MessageBox.alert(Roo.encode(rdata));
25511                     return;
25512                 }
25513                 var data = rdata.data;
25514                 
25515                 if (this.uploadComplete) {
25516                    Roo.MessageBox.hide();
25517                    return;
25518                 }
25519                    
25520                 if (data){
25521                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25522                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25523                     );
25524                 }
25525                 this.uploadProgress.defer(2000,this);
25526             },
25527        
25528             failure: function(data) {
25529                 Roo.log('progress url failed ');
25530                 Roo.log(data);
25531             },
25532             scope : this
25533         });
25534            
25535     },
25536     
25537     
25538     run : function()
25539     {
25540         // run get Values on the form, so it syncs any secondary forms.
25541         this.form.getValues();
25542         
25543         var o = this.options;
25544         var method = this.getMethod();
25545         var isPost = method == 'POST';
25546         if(o.clientValidation === false || this.form.isValid()){
25547             
25548             if (this.form.progressUrl) {
25549                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25550                     (new Date() * 1) + '' + Math.random());
25551                     
25552             } 
25553             
25554             
25555             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25556                 form:this.form.el.dom,
25557                 url:this.getUrl(!isPost),
25558                 method: method,
25559                 params:isPost ? this.getParams() : null,
25560                 isUpload: this.form.fileUpload
25561             }));
25562             
25563             this.uploadProgress();
25564
25565         }else if (o.clientValidation !== false){ // client validation failed
25566             this.failureType = Roo.form.Action.CLIENT_INVALID;
25567             this.form.afterAction(this, false);
25568         }
25569     },
25570
25571     success : function(response)
25572     {
25573         this.uploadComplete= true;
25574         if (this.haveProgress) {
25575             Roo.MessageBox.hide();
25576         }
25577         
25578         
25579         var result = this.processResponse(response);
25580         if(result === true || result.success){
25581             this.form.afterAction(this, true);
25582             return;
25583         }
25584         if(result.errors){
25585             this.form.markInvalid(result.errors);
25586             this.failureType = Roo.form.Action.SERVER_INVALID;
25587         }
25588         this.form.afterAction(this, false);
25589     },
25590     failure : function(response)
25591     {
25592         this.uploadComplete= true;
25593         if (this.haveProgress) {
25594             Roo.MessageBox.hide();
25595         }
25596         
25597         this.response = response;
25598         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25599         this.form.afterAction(this, false);
25600     },
25601     
25602     handleResponse : function(response){
25603         if(this.form.errorReader){
25604             var rs = this.form.errorReader.read(response);
25605             var errors = [];
25606             if(rs.records){
25607                 for(var i = 0, len = rs.records.length; i < len; i++) {
25608                     var r = rs.records[i];
25609                     errors[i] = r.data;
25610                 }
25611             }
25612             if(errors.length < 1){
25613                 errors = null;
25614             }
25615             return {
25616                 success : rs.success,
25617                 errors : errors
25618             };
25619         }
25620         var ret = false;
25621         try {
25622             ret = Roo.decode(response.responseText);
25623         } catch (e) {
25624             ret = {
25625                 success: false,
25626                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25627                 errors : []
25628             };
25629         }
25630         return ret;
25631         
25632     }
25633 });
25634
25635
25636 Roo.form.Action.Load = function(form, options){
25637     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25638     this.reader = this.form.reader;
25639 };
25640
25641 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
25642     type : 'load',
25643
25644     run : function(){
25645         
25646         Roo.Ajax.request(Roo.apply(
25647                 this.createCallback(), {
25648                     method:this.getMethod(),
25649                     url:this.getUrl(false),
25650                     params:this.getParams()
25651         }));
25652     },
25653
25654     success : function(response){
25655         
25656         var result = this.processResponse(response);
25657         if(result === true || !result.success || !result.data){
25658             this.failureType = Roo.form.Action.LOAD_FAILURE;
25659             this.form.afterAction(this, false);
25660             return;
25661         }
25662         this.form.clearInvalid();
25663         this.form.setValues(result.data);
25664         this.form.afterAction(this, true);
25665     },
25666
25667     handleResponse : function(response){
25668         if(this.form.reader){
25669             var rs = this.form.reader.read(response);
25670             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
25671             return {
25672                 success : rs.success,
25673                 data : data
25674             };
25675         }
25676         return Roo.decode(response.responseText);
25677     }
25678 });
25679
25680 Roo.form.Action.ACTION_TYPES = {
25681     'load' : Roo.form.Action.Load,
25682     'submit' : Roo.form.Action.Submit
25683 };/*
25684  * Based on:
25685  * Ext JS Library 1.1.1
25686  * Copyright(c) 2006-2007, Ext JS, LLC.
25687  *
25688  * Originally Released Under LGPL - original licence link has changed is not relivant.
25689  *
25690  * Fork - LGPL
25691  * <script type="text/javascript">
25692  */
25693  
25694 /**
25695  * @class Roo.form.Layout
25696  * @extends Roo.Component
25697  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
25698  * @constructor
25699  * @param {Object} config Configuration options
25700  */
25701 Roo.form.Layout = function(config){
25702     var xitems = [];
25703     if (config.items) {
25704         xitems = config.items;
25705         delete config.items;
25706     }
25707     Roo.form.Layout.superclass.constructor.call(this, config);
25708     this.stack = [];
25709     Roo.each(xitems, this.addxtype, this);
25710      
25711 };
25712
25713 Roo.extend(Roo.form.Layout, Roo.Component, {
25714     /**
25715      * @cfg {String/Object} autoCreate
25716      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
25717      */
25718     /**
25719      * @cfg {String/Object/Function} style
25720      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
25721      * a function which returns such a specification.
25722      */
25723     /**
25724      * @cfg {String} labelAlign
25725      * Valid values are "left," "top" and "right" (defaults to "left")
25726      */
25727     /**
25728      * @cfg {Number} labelWidth
25729      * Fixed width in pixels of all field labels (defaults to undefined)
25730      */
25731     /**
25732      * @cfg {Boolean} clear
25733      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
25734      */
25735     clear : true,
25736     /**
25737      * @cfg {String} labelSeparator
25738      * The separator to use after field labels (defaults to ':')
25739      */
25740     labelSeparator : ':',
25741     /**
25742      * @cfg {Boolean} hideLabels
25743      * True to suppress the display of field labels in this layout (defaults to false)
25744      */
25745     hideLabels : false,
25746
25747     // private
25748     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
25749     
25750     isLayout : true,
25751     
25752     // private
25753     onRender : function(ct, position){
25754         if(this.el){ // from markup
25755             this.el = Roo.get(this.el);
25756         }else {  // generate
25757             var cfg = this.getAutoCreate();
25758             this.el = ct.createChild(cfg, position);
25759         }
25760         if(this.style){
25761             this.el.applyStyles(this.style);
25762         }
25763         if(this.labelAlign){
25764             this.el.addClass('x-form-label-'+this.labelAlign);
25765         }
25766         if(this.hideLabels){
25767             this.labelStyle = "display:none";
25768             this.elementStyle = "padding-left:0;";
25769         }else{
25770             if(typeof this.labelWidth == 'number'){
25771                 this.labelStyle = "width:"+this.labelWidth+"px;";
25772                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
25773             }
25774             if(this.labelAlign == 'top'){
25775                 this.labelStyle = "width:auto;";
25776                 this.elementStyle = "padding-left:0;";
25777             }
25778         }
25779         var stack = this.stack;
25780         var slen = stack.length;
25781         if(slen > 0){
25782             if(!this.fieldTpl){
25783                 var t = new Roo.Template(
25784                     '<div class="x-form-item {5}">',
25785                         '<label for="{0}" style="{2}">{1}{4}</label>',
25786                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25787                         '</div>',
25788                     '</div><div class="x-form-clear-left"></div>'
25789                 );
25790                 t.disableFormats = true;
25791                 t.compile();
25792                 Roo.form.Layout.prototype.fieldTpl = t;
25793             }
25794             for(var i = 0; i < slen; i++) {
25795                 if(stack[i].isFormField){
25796                     this.renderField(stack[i]);
25797                 }else{
25798                     this.renderComponent(stack[i]);
25799                 }
25800             }
25801         }
25802         if(this.clear){
25803             this.el.createChild({cls:'x-form-clear'});
25804         }
25805     },
25806
25807     // private
25808     renderField : function(f){
25809         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
25810                f.id, //0
25811                f.fieldLabel, //1
25812                f.labelStyle||this.labelStyle||'', //2
25813                this.elementStyle||'', //3
25814                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
25815                f.itemCls||this.itemCls||''  //5
25816        ], true).getPrevSibling());
25817     },
25818
25819     // private
25820     renderComponent : function(c){
25821         c.render(c.isLayout ? this.el : this.el.createChild());    
25822     },
25823     /**
25824      * Adds a object form elements (using the xtype property as the factory method.)
25825      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
25826      * @param {Object} config 
25827      */
25828     addxtype : function(o)
25829     {
25830         // create the lement.
25831         o.form = this.form;
25832         var fe = Roo.factory(o, Roo.form);
25833         this.form.allItems.push(fe);
25834         this.stack.push(fe);
25835         
25836         if (fe.isFormField) {
25837             this.form.items.add(fe);
25838         }
25839          
25840         return fe;
25841     }
25842 });
25843
25844 /**
25845  * @class Roo.form.Column
25846  * @extends Roo.form.Layout
25847  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
25848  * @constructor
25849  * @param {Object} config Configuration options
25850  */
25851 Roo.form.Column = function(config){
25852     Roo.form.Column.superclass.constructor.call(this, config);
25853 };
25854
25855 Roo.extend(Roo.form.Column, Roo.form.Layout, {
25856     /**
25857      * @cfg {Number/String} width
25858      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25859      */
25860     /**
25861      * @cfg {String/Object} autoCreate
25862      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
25863      */
25864
25865     // private
25866     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
25867
25868     // private
25869     onRender : function(ct, position){
25870         Roo.form.Column.superclass.onRender.call(this, ct, position);
25871         if(this.width){
25872             this.el.setWidth(this.width);
25873         }
25874     }
25875 });
25876
25877
25878 /**
25879  * @class Roo.form.Row
25880  * @extends Roo.form.Layout
25881  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
25882  * @constructor
25883  * @param {Object} config Configuration options
25884  */
25885
25886  
25887 Roo.form.Row = function(config){
25888     Roo.form.Row.superclass.constructor.call(this, config);
25889 };
25890  
25891 Roo.extend(Roo.form.Row, Roo.form.Layout, {
25892       /**
25893      * @cfg {Number/String} width
25894      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25895      */
25896     /**
25897      * @cfg {Number/String} height
25898      * The fixed height of the column in pixels or CSS value (defaults to "auto")
25899      */
25900     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
25901     
25902     padWidth : 20,
25903     // private
25904     onRender : function(ct, position){
25905         //console.log('row render');
25906         if(!this.rowTpl){
25907             var t = new Roo.Template(
25908                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
25909                     '<label for="{0}" style="{2}">{1}{4}</label>',
25910                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25911                     '</div>',
25912                 '</div>'
25913             );
25914             t.disableFormats = true;
25915             t.compile();
25916             Roo.form.Layout.prototype.rowTpl = t;
25917         }
25918         this.fieldTpl = this.rowTpl;
25919         
25920         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
25921         var labelWidth = 100;
25922         
25923         if ((this.labelAlign != 'top')) {
25924             if (typeof this.labelWidth == 'number') {
25925                 labelWidth = this.labelWidth
25926             }
25927             this.padWidth =  20 + labelWidth;
25928             
25929         }
25930         
25931         Roo.form.Column.superclass.onRender.call(this, ct, position);
25932         if(this.width){
25933             this.el.setWidth(this.width);
25934         }
25935         if(this.height){
25936             this.el.setHeight(this.height);
25937         }
25938     },
25939     
25940     // private
25941     renderField : function(f){
25942         f.fieldEl = this.fieldTpl.append(this.el, [
25943                f.id, f.fieldLabel,
25944                f.labelStyle||this.labelStyle||'',
25945                this.elementStyle||'',
25946                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
25947                f.itemCls||this.itemCls||'',
25948                f.width ? f.width + this.padWidth : 160 + this.padWidth
25949        ],true);
25950     }
25951 });
25952  
25953
25954 /**
25955  * @class Roo.form.FieldSet
25956  * @extends Roo.form.Layout
25957  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
25958  * @constructor
25959  * @param {Object} config Configuration options
25960  */
25961 Roo.form.FieldSet = function(config){
25962     Roo.form.FieldSet.superclass.constructor.call(this, config);
25963 };
25964
25965 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
25966     /**
25967      * @cfg {String} legend
25968      * The text to display as the legend for the FieldSet (defaults to '')
25969      */
25970     /**
25971      * @cfg {String/Object} autoCreate
25972      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
25973      */
25974
25975     // private
25976     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
25977
25978     // private
25979     onRender : function(ct, position){
25980         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
25981         if(this.legend){
25982             this.setLegend(this.legend);
25983         }
25984     },
25985
25986     // private
25987     setLegend : function(text){
25988         if(this.rendered){
25989             this.el.child('legend').update(text);
25990         }
25991     }
25992 });/*
25993  * Based on:
25994  * Ext JS Library 1.1.1
25995  * Copyright(c) 2006-2007, Ext JS, LLC.
25996  *
25997  * Originally Released Under LGPL - original licence link has changed is not relivant.
25998  *
25999  * Fork - LGPL
26000  * <script type="text/javascript">
26001  */
26002 /**
26003  * @class Roo.form.VTypes
26004  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26005  * @singleton
26006  */
26007 Roo.form.VTypes = function(){
26008     // closure these in so they are only created once.
26009     var alpha = /^[a-zA-Z_]+$/;
26010     var alphanum = /^[a-zA-Z0-9_]+$/;
26011     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26012     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26013
26014     // All these messages and functions are configurable
26015     return {
26016         /**
26017          * The function used to validate email addresses
26018          * @param {String} value The email address
26019          */
26020         'email' : function(v){
26021             return email.test(v);
26022         },
26023         /**
26024          * The error text to display when the email validation function returns false
26025          * @type String
26026          */
26027         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26028         /**
26029          * The keystroke filter mask to be applied on email input
26030          * @type RegExp
26031          */
26032         'emailMask' : /[a-z0-9_\.\-@]/i,
26033
26034         /**
26035          * The function used to validate URLs
26036          * @param {String} value The URL
26037          */
26038         'url' : function(v){
26039             return url.test(v);
26040         },
26041         /**
26042          * The error text to display when the url validation function returns false
26043          * @type String
26044          */
26045         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26046         
26047         /**
26048          * The function used to validate alpha values
26049          * @param {String} value The value
26050          */
26051         'alpha' : function(v){
26052             return alpha.test(v);
26053         },
26054         /**
26055          * The error text to display when the alpha validation function returns false
26056          * @type String
26057          */
26058         'alphaText' : 'This field should only contain letters and _',
26059         /**
26060          * The keystroke filter mask to be applied on alpha input
26061          * @type RegExp
26062          */
26063         'alphaMask' : /[a-z_]/i,
26064
26065         /**
26066          * The function used to validate alphanumeric values
26067          * @param {String} value The value
26068          */
26069         'alphanum' : function(v){
26070             return alphanum.test(v);
26071         },
26072         /**
26073          * The error text to display when the alphanumeric validation function returns false
26074          * @type String
26075          */
26076         'alphanumText' : 'This field should only contain letters, numbers and _',
26077         /**
26078          * The keystroke filter mask to be applied on alphanumeric input
26079          * @type RegExp
26080          */
26081         'alphanumMask' : /[a-z0-9_]/i
26082     };
26083 }();//<script type="text/javascript">
26084
26085 /**
26086  * @class Roo.form.FCKeditor
26087  * @extends Roo.form.TextArea
26088  * Wrapper around the FCKEditor http://www.fckeditor.net
26089  * @constructor
26090  * Creates a new FCKeditor
26091  * @param {Object} config Configuration options
26092  */
26093 Roo.form.FCKeditor = function(config){
26094     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26095     this.addEvents({
26096          /**
26097          * @event editorinit
26098          * Fired when the editor is initialized - you can add extra handlers here..
26099          * @param {FCKeditor} this
26100          * @param {Object} the FCK object.
26101          */
26102         editorinit : true
26103     });
26104     
26105     
26106 };
26107 Roo.form.FCKeditor.editors = { };
26108 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26109 {
26110     //defaultAutoCreate : {
26111     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26112     //},
26113     // private
26114     /**
26115      * @cfg {Object} fck options - see fck manual for details.
26116      */
26117     fckconfig : false,
26118     
26119     /**
26120      * @cfg {Object} fck toolbar set (Basic or Default)
26121      */
26122     toolbarSet : 'Basic',
26123     /**
26124      * @cfg {Object} fck BasePath
26125      */ 
26126     basePath : '/fckeditor/',
26127     
26128     
26129     frame : false,
26130     
26131     value : '',
26132     
26133    
26134     onRender : function(ct, position)
26135     {
26136         if(!this.el){
26137             this.defaultAutoCreate = {
26138                 tag: "textarea",
26139                 style:"width:300px;height:60px;",
26140                 autocomplete: "new-password"
26141             };
26142         }
26143         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26144         /*
26145         if(this.grow){
26146             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26147             if(this.preventScrollbars){
26148                 this.el.setStyle("overflow", "hidden");
26149             }
26150             this.el.setHeight(this.growMin);
26151         }
26152         */
26153         //console.log('onrender' + this.getId() );
26154         Roo.form.FCKeditor.editors[this.getId()] = this;
26155          
26156
26157         this.replaceTextarea() ;
26158         
26159     },
26160     
26161     getEditor : function() {
26162         return this.fckEditor;
26163     },
26164     /**
26165      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26166      * @param {Mixed} value The value to set
26167      */
26168     
26169     
26170     setValue : function(value)
26171     {
26172         //console.log('setValue: ' + value);
26173         
26174         if(typeof(value) == 'undefined') { // not sure why this is happending...
26175             return;
26176         }
26177         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26178         
26179         //if(!this.el || !this.getEditor()) {
26180         //    this.value = value;
26181             //this.setValue.defer(100,this,[value]);    
26182         //    return;
26183         //} 
26184         
26185         if(!this.getEditor()) {
26186             return;
26187         }
26188         
26189         this.getEditor().SetData(value);
26190         
26191         //
26192
26193     },
26194
26195     /**
26196      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26197      * @return {Mixed} value The field value
26198      */
26199     getValue : function()
26200     {
26201         
26202         if (this.frame && this.frame.dom.style.display == 'none') {
26203             return Roo.form.FCKeditor.superclass.getValue.call(this);
26204         }
26205         
26206         if(!this.el || !this.getEditor()) {
26207            
26208            // this.getValue.defer(100,this); 
26209             return this.value;
26210         }
26211        
26212         
26213         var value=this.getEditor().GetData();
26214         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26215         return Roo.form.FCKeditor.superclass.getValue.call(this);
26216         
26217
26218     },
26219
26220     /**
26221      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26222      * @return {Mixed} value The field value
26223      */
26224     getRawValue : function()
26225     {
26226         if (this.frame && this.frame.dom.style.display == 'none') {
26227             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26228         }
26229         
26230         if(!this.el || !this.getEditor()) {
26231             //this.getRawValue.defer(100,this); 
26232             return this.value;
26233             return;
26234         }
26235         
26236         
26237         
26238         var value=this.getEditor().GetData();
26239         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26240         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26241          
26242     },
26243     
26244     setSize : function(w,h) {
26245         
26246         
26247         
26248         //if (this.frame && this.frame.dom.style.display == 'none') {
26249         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26250         //    return;
26251         //}
26252         //if(!this.el || !this.getEditor()) {
26253         //    this.setSize.defer(100,this, [w,h]); 
26254         //    return;
26255         //}
26256         
26257         
26258         
26259         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26260         
26261         this.frame.dom.setAttribute('width', w);
26262         this.frame.dom.setAttribute('height', h);
26263         this.frame.setSize(w,h);
26264         
26265     },
26266     
26267     toggleSourceEdit : function(value) {
26268         
26269       
26270          
26271         this.el.dom.style.display = value ? '' : 'none';
26272         this.frame.dom.style.display = value ?  'none' : '';
26273         
26274     },
26275     
26276     
26277     focus: function(tag)
26278     {
26279         if (this.frame.dom.style.display == 'none') {
26280             return Roo.form.FCKeditor.superclass.focus.call(this);
26281         }
26282         if(!this.el || !this.getEditor()) {
26283             this.focus.defer(100,this, [tag]); 
26284             return;
26285         }
26286         
26287         
26288         
26289         
26290         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26291         this.getEditor().Focus();
26292         if (tgs.length) {
26293             if (!this.getEditor().Selection.GetSelection()) {
26294                 this.focus.defer(100,this, [tag]); 
26295                 return;
26296             }
26297             
26298             
26299             var r = this.getEditor().EditorDocument.createRange();
26300             r.setStart(tgs[0],0);
26301             r.setEnd(tgs[0],0);
26302             this.getEditor().Selection.GetSelection().removeAllRanges();
26303             this.getEditor().Selection.GetSelection().addRange(r);
26304             this.getEditor().Focus();
26305         }
26306         
26307     },
26308     
26309     
26310     
26311     replaceTextarea : function()
26312     {
26313         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26314             return ;
26315         }
26316         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26317         //{
26318             // We must check the elements firstly using the Id and then the name.
26319         var oTextarea = document.getElementById( this.getId() );
26320         
26321         var colElementsByName = document.getElementsByName( this.getId() ) ;
26322          
26323         oTextarea.style.display = 'none' ;
26324
26325         if ( oTextarea.tabIndex ) {            
26326             this.TabIndex = oTextarea.tabIndex ;
26327         }
26328         
26329         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26330         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26331         this.frame = Roo.get(this.getId() + '___Frame')
26332     },
26333     
26334     _getConfigHtml : function()
26335     {
26336         var sConfig = '' ;
26337
26338         for ( var o in this.fckconfig ) {
26339             sConfig += sConfig.length > 0  ? '&amp;' : '';
26340             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26341         }
26342
26343         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26344     },
26345     
26346     
26347     _getIFrameHtml : function()
26348     {
26349         var sFile = 'fckeditor.html' ;
26350         /* no idea what this is about..
26351         try
26352         {
26353             if ( (/fcksource=true/i).test( window.top.location.search ) )
26354                 sFile = 'fckeditor.original.html' ;
26355         }
26356         catch (e) { 
26357         */
26358
26359         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26360         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26361         
26362         
26363         var html = '<iframe id="' + this.getId() +
26364             '___Frame" src="' + sLink +
26365             '" width="' + this.width +
26366             '" height="' + this.height + '"' +
26367             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26368             ' frameborder="0" scrolling="no"></iframe>' ;
26369
26370         return html ;
26371     },
26372     
26373     _insertHtmlBefore : function( html, element )
26374     {
26375         if ( element.insertAdjacentHTML )       {
26376             // IE
26377             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26378         } else { // Gecko
26379             var oRange = document.createRange() ;
26380             oRange.setStartBefore( element ) ;
26381             var oFragment = oRange.createContextualFragment( html );
26382             element.parentNode.insertBefore( oFragment, element ) ;
26383         }
26384     }
26385     
26386     
26387   
26388     
26389     
26390     
26391     
26392
26393 });
26394
26395 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26396
26397 function FCKeditor_OnComplete(editorInstance){
26398     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26399     f.fckEditor = editorInstance;
26400     //console.log("loaded");
26401     f.fireEvent('editorinit', f, editorInstance);
26402
26403   
26404
26405  
26406
26407
26408
26409
26410
26411
26412
26413
26414
26415
26416
26417
26418
26419
26420
26421 //<script type="text/javascript">
26422 /**
26423  * @class Roo.form.GridField
26424  * @extends Roo.form.Field
26425  * Embed a grid (or editable grid into a form)
26426  * STATUS ALPHA
26427  * 
26428  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26429  * it needs 
26430  * xgrid.store = Roo.data.Store
26431  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26432  * xgrid.store.reader = Roo.data.JsonReader 
26433  * 
26434  * 
26435  * @constructor
26436  * Creates a new GridField
26437  * @param {Object} config Configuration options
26438  */
26439 Roo.form.GridField = function(config){
26440     Roo.form.GridField.superclass.constructor.call(this, config);
26441      
26442 };
26443
26444 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26445     /**
26446      * @cfg {Number} width  - used to restrict width of grid..
26447      */
26448     width : 100,
26449     /**
26450      * @cfg {Number} height - used to restrict height of grid..
26451      */
26452     height : 50,
26453      /**
26454      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26455          * 
26456          *}
26457      */
26458     xgrid : false, 
26459     /**
26460      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26461      * {tag: "input", type: "checkbox", autocomplete: "off"})
26462      */
26463    // defaultAutoCreate : { tag: 'div' },
26464     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26465     /**
26466      * @cfg {String} addTitle Text to include for adding a title.
26467      */
26468     addTitle : false,
26469     //
26470     onResize : function(){
26471         Roo.form.Field.superclass.onResize.apply(this, arguments);
26472     },
26473
26474     initEvents : function(){
26475         // Roo.form.Checkbox.superclass.initEvents.call(this);
26476         // has no events...
26477        
26478     },
26479
26480
26481     getResizeEl : function(){
26482         return this.wrap;
26483     },
26484
26485     getPositionEl : function(){
26486         return this.wrap;
26487     },
26488
26489     // private
26490     onRender : function(ct, position){
26491         
26492         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26493         var style = this.style;
26494         delete this.style;
26495         
26496         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26497         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26498         this.viewEl = this.wrap.createChild({ tag: 'div' });
26499         if (style) {
26500             this.viewEl.applyStyles(style);
26501         }
26502         if (this.width) {
26503             this.viewEl.setWidth(this.width);
26504         }
26505         if (this.height) {
26506             this.viewEl.setHeight(this.height);
26507         }
26508         //if(this.inputValue !== undefined){
26509         //this.setValue(this.value);
26510         
26511         
26512         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26513         
26514         
26515         this.grid.render();
26516         this.grid.getDataSource().on('remove', this.refreshValue, this);
26517         this.grid.getDataSource().on('update', this.refreshValue, this);
26518         this.grid.on('afteredit', this.refreshValue, this);
26519  
26520     },
26521      
26522     
26523     /**
26524      * Sets the value of the item. 
26525      * @param {String} either an object  or a string..
26526      */
26527     setValue : function(v){
26528         //this.value = v;
26529         v = v || []; // empty set..
26530         // this does not seem smart - it really only affects memoryproxy grids..
26531         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26532             var ds = this.grid.getDataSource();
26533             // assumes a json reader..
26534             var data = {}
26535             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26536             ds.loadData( data);
26537         }
26538         // clear selection so it does not get stale.
26539         if (this.grid.sm) { 
26540             this.grid.sm.clearSelections();
26541         }
26542         
26543         Roo.form.GridField.superclass.setValue.call(this, v);
26544         this.refreshValue();
26545         // should load data in the grid really....
26546     },
26547     
26548     // private
26549     refreshValue: function() {
26550          var val = [];
26551         this.grid.getDataSource().each(function(r) {
26552             val.push(r.data);
26553         });
26554         this.el.dom.value = Roo.encode(val);
26555     }
26556     
26557      
26558     
26559     
26560 });/*
26561  * Based on:
26562  * Ext JS Library 1.1.1
26563  * Copyright(c) 2006-2007, Ext JS, LLC.
26564  *
26565  * Originally Released Under LGPL - original licence link has changed is not relivant.
26566  *
26567  * Fork - LGPL
26568  * <script type="text/javascript">
26569  */
26570 /**
26571  * @class Roo.form.DisplayField
26572  * @extends Roo.form.Field
26573  * A generic Field to display non-editable data.
26574  * @cfg {Boolean} closable (true|false) default false
26575  * @constructor
26576  * Creates a new Display Field item.
26577  * @param {Object} config Configuration options
26578  */
26579 Roo.form.DisplayField = function(config){
26580     Roo.form.DisplayField.superclass.constructor.call(this, config);
26581     
26582     this.addEvents({
26583         /**
26584          * @event close
26585          * Fires after the click the close btn
26586              * @param {Roo.form.DisplayField} this
26587              */
26588         close : true
26589     });
26590 };
26591
26592 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26593     inputType:      'hidden',
26594     allowBlank:     true,
26595     readOnly:         true,
26596     
26597  
26598     /**
26599      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26600      */
26601     focusClass : undefined,
26602     /**
26603      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26604      */
26605     fieldClass: 'x-form-field',
26606     
26607      /**
26608      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26609      */
26610     valueRenderer: undefined,
26611     
26612     width: 100,
26613     /**
26614      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26615      * {tag: "input", type: "checkbox", autocomplete: "off"})
26616      */
26617      
26618  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26619  
26620     closable : false,
26621     
26622     onResize : function(){
26623         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26624         
26625     },
26626
26627     initEvents : function(){
26628         // Roo.form.Checkbox.superclass.initEvents.call(this);
26629         // has no events...
26630         
26631         if(this.closable){
26632             this.closeEl.on('click', this.onClose, this);
26633         }
26634        
26635     },
26636
26637
26638     getResizeEl : function(){
26639         return this.wrap;
26640     },
26641
26642     getPositionEl : function(){
26643         return this.wrap;
26644     },
26645
26646     // private
26647     onRender : function(ct, position){
26648         
26649         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
26650         //if(this.inputValue !== undefined){
26651         this.wrap = this.el.wrap();
26652         
26653         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
26654         
26655         if(this.closable){
26656             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
26657         }
26658         
26659         if (this.bodyStyle) {
26660             this.viewEl.applyStyles(this.bodyStyle);
26661         }
26662         //this.viewEl.setStyle('padding', '2px');
26663         
26664         this.setValue(this.value);
26665         
26666     },
26667 /*
26668     // private
26669     initValue : Roo.emptyFn,
26670
26671   */
26672
26673         // private
26674     onClick : function(){
26675         
26676     },
26677
26678     /**
26679      * Sets the checked state of the checkbox.
26680      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
26681      */
26682     setValue : function(v){
26683         this.value = v;
26684         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
26685         // this might be called before we have a dom element..
26686         if (!this.viewEl) {
26687             return;
26688         }
26689         this.viewEl.dom.innerHTML = html;
26690         Roo.form.DisplayField.superclass.setValue.call(this, v);
26691
26692     },
26693     
26694     onClose : function(e)
26695     {
26696         e.preventDefault();
26697         
26698         this.fireEvent('close', this);
26699     }
26700 });/*
26701  * 
26702  * Licence- LGPL
26703  * 
26704  */
26705
26706 /**
26707  * @class Roo.form.DayPicker
26708  * @extends Roo.form.Field
26709  * A Day picker show [M] [T] [W] ....
26710  * @constructor
26711  * Creates a new Day Picker
26712  * @param {Object} config Configuration options
26713  */
26714 Roo.form.DayPicker= function(config){
26715     Roo.form.DayPicker.superclass.constructor.call(this, config);
26716      
26717 };
26718
26719 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
26720     /**
26721      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26722      */
26723     focusClass : undefined,
26724     /**
26725      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26726      */
26727     fieldClass: "x-form-field",
26728    
26729     /**
26730      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26731      * {tag: "input", type: "checkbox", autocomplete: "off"})
26732      */
26733     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
26734     
26735    
26736     actionMode : 'viewEl', 
26737     //
26738     // private
26739  
26740     inputType : 'hidden',
26741     
26742      
26743     inputElement: false, // real input element?
26744     basedOn: false, // ????
26745     
26746     isFormField: true, // not sure where this is needed!!!!
26747
26748     onResize : function(){
26749         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
26750         if(!this.boxLabel){
26751             this.el.alignTo(this.wrap, 'c-c');
26752         }
26753     },
26754
26755     initEvents : function(){
26756         Roo.form.Checkbox.superclass.initEvents.call(this);
26757         this.el.on("click", this.onClick,  this);
26758         this.el.on("change", this.onClick,  this);
26759     },
26760
26761
26762     getResizeEl : function(){
26763         return this.wrap;
26764     },
26765
26766     getPositionEl : function(){
26767         return this.wrap;
26768     },
26769
26770     
26771     // private
26772     onRender : function(ct, position){
26773         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
26774        
26775         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
26776         
26777         var r1 = '<table><tr>';
26778         var r2 = '<tr class="x-form-daypick-icons">';
26779         for (var i=0; i < 7; i++) {
26780             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
26781             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
26782         }
26783         
26784         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
26785         viewEl.select('img').on('click', this.onClick, this);
26786         this.viewEl = viewEl;   
26787         
26788         
26789         // this will not work on Chrome!!!
26790         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
26791         this.el.on('propertychange', this.setFromHidden,  this);  //ie
26792         
26793         
26794           
26795
26796     },
26797
26798     // private
26799     initValue : Roo.emptyFn,
26800
26801     /**
26802      * Returns the checked state of the checkbox.
26803      * @return {Boolean} True if checked, else false
26804      */
26805     getValue : function(){
26806         return this.el.dom.value;
26807         
26808     },
26809
26810         // private
26811     onClick : function(e){ 
26812         //this.setChecked(!this.checked);
26813         Roo.get(e.target).toggleClass('x-menu-item-checked');
26814         this.refreshValue();
26815         //if(this.el.dom.checked != this.checked){
26816         //    this.setValue(this.el.dom.checked);
26817        // }
26818     },
26819     
26820     // private
26821     refreshValue : function()
26822     {
26823         var val = '';
26824         this.viewEl.select('img',true).each(function(e,i,n)  {
26825             val += e.is(".x-menu-item-checked") ? String(n) : '';
26826         });
26827         this.setValue(val, true);
26828     },
26829
26830     /**
26831      * Sets the checked state of the checkbox.
26832      * On is always based on a string comparison between inputValue and the param.
26833      * @param {Boolean/String} value - the value to set 
26834      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
26835      */
26836     setValue : function(v,suppressEvent){
26837         if (!this.el.dom) {
26838             return;
26839         }
26840         var old = this.el.dom.value ;
26841         this.el.dom.value = v;
26842         if (suppressEvent) {
26843             return ;
26844         }
26845          
26846         // update display..
26847         this.viewEl.select('img',true).each(function(e,i,n)  {
26848             
26849             var on = e.is(".x-menu-item-checked");
26850             var newv = v.indexOf(String(n)) > -1;
26851             if (on != newv) {
26852                 e.toggleClass('x-menu-item-checked');
26853             }
26854             
26855         });
26856         
26857         
26858         this.fireEvent('change', this, v, old);
26859         
26860         
26861     },
26862    
26863     // handle setting of hidden value by some other method!!?!?
26864     setFromHidden: function()
26865     {
26866         if(!this.el){
26867             return;
26868         }
26869         //console.log("SET FROM HIDDEN");
26870         //alert('setFrom hidden');
26871         this.setValue(this.el.dom.value);
26872     },
26873     
26874     onDestroy : function()
26875     {
26876         if(this.viewEl){
26877             Roo.get(this.viewEl).remove();
26878         }
26879          
26880         Roo.form.DayPicker.superclass.onDestroy.call(this);
26881     }
26882
26883 });/*
26884  * RooJS Library 1.1.1
26885  * Copyright(c) 2008-2011  Alan Knowles
26886  *
26887  * License - LGPL
26888  */
26889  
26890
26891 /**
26892  * @class Roo.form.ComboCheck
26893  * @extends Roo.form.ComboBox
26894  * A combobox for multiple select items.
26895  *
26896  * FIXME - could do with a reset button..
26897  * 
26898  * @constructor
26899  * Create a new ComboCheck
26900  * @param {Object} config Configuration options
26901  */
26902 Roo.form.ComboCheck = function(config){
26903     Roo.form.ComboCheck.superclass.constructor.call(this, config);
26904     // should verify some data...
26905     // like
26906     // hiddenName = required..
26907     // displayField = required
26908     // valudField == required
26909     var req= [ 'hiddenName', 'displayField', 'valueField' ];
26910     var _t = this;
26911     Roo.each(req, function(e) {
26912         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
26913             throw "Roo.form.ComboCheck : missing value for: " + e;
26914         }
26915     });
26916     
26917     
26918 };
26919
26920 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
26921      
26922      
26923     editable : false,
26924      
26925     selectedClass: 'x-menu-item-checked', 
26926     
26927     // private
26928     onRender : function(ct, position){
26929         var _t = this;
26930         
26931         
26932         
26933         if(!this.tpl){
26934             var cls = 'x-combo-list';
26935
26936             
26937             this.tpl =  new Roo.Template({
26938                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
26939                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
26940                    '<span>{' + this.displayField + '}</span>' +
26941                     '</div>' 
26942                 
26943             });
26944         }
26945  
26946         
26947         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
26948         this.view.singleSelect = false;
26949         this.view.multiSelect = true;
26950         this.view.toggleSelect = true;
26951         this.pageTb.add(new Roo.Toolbar.Fill(), {
26952             
26953             text: 'Done',
26954             handler: function()
26955             {
26956                 _t.collapse();
26957             }
26958         });
26959     },
26960     
26961     onViewOver : function(e, t){
26962         // do nothing...
26963         return;
26964         
26965     },
26966     
26967     onViewClick : function(doFocus,index){
26968         return;
26969         
26970     },
26971     select: function () {
26972         //Roo.log("SELECT CALLED");
26973     },
26974      
26975     selectByValue : function(xv, scrollIntoView){
26976         var ar = this.getValueArray();
26977         var sels = [];
26978         
26979         Roo.each(ar, function(v) {
26980             if(v === undefined || v === null){
26981                 return;
26982             }
26983             var r = this.findRecord(this.valueField, v);
26984             if(r){
26985                 sels.push(this.store.indexOf(r))
26986                 
26987             }
26988         },this);
26989         this.view.select(sels);
26990         return false;
26991     },
26992     
26993     
26994     
26995     onSelect : function(record, index){
26996        // Roo.log("onselect Called");
26997        // this is only called by the clear button now..
26998         this.view.clearSelections();
26999         this.setValue('[]');
27000         if (this.value != this.valueBefore) {
27001             this.fireEvent('change', this, this.value, this.valueBefore);
27002             this.valueBefore = this.value;
27003         }
27004     },
27005     getValueArray : function()
27006     {
27007         var ar = [] ;
27008         
27009         try {
27010             //Roo.log(this.value);
27011             if (typeof(this.value) == 'undefined') {
27012                 return [];
27013             }
27014             var ar = Roo.decode(this.value);
27015             return  ar instanceof Array ? ar : []; //?? valid?
27016             
27017         } catch(e) {
27018             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27019             return [];
27020         }
27021          
27022     },
27023     expand : function ()
27024     {
27025         
27026         Roo.form.ComboCheck.superclass.expand.call(this);
27027         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27028         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27029         
27030
27031     },
27032     
27033     collapse : function(){
27034         Roo.form.ComboCheck.superclass.collapse.call(this);
27035         var sl = this.view.getSelectedIndexes();
27036         var st = this.store;
27037         var nv = [];
27038         var tv = [];
27039         var r;
27040         Roo.each(sl, function(i) {
27041             r = st.getAt(i);
27042             nv.push(r.get(this.valueField));
27043         },this);
27044         this.setValue(Roo.encode(nv));
27045         if (this.value != this.valueBefore) {
27046
27047             this.fireEvent('change', this, this.value, this.valueBefore);
27048             this.valueBefore = this.value;
27049         }
27050         
27051     },
27052     
27053     setValue : function(v){
27054         // Roo.log(v);
27055         this.value = v;
27056         
27057         var vals = this.getValueArray();
27058         var tv = [];
27059         Roo.each(vals, function(k) {
27060             var r = this.findRecord(this.valueField, k);
27061             if(r){
27062                 tv.push(r.data[this.displayField]);
27063             }else if(this.valueNotFoundText !== undefined){
27064                 tv.push( this.valueNotFoundText );
27065             }
27066         },this);
27067        // Roo.log(tv);
27068         
27069         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27070         this.hiddenField.value = v;
27071         this.value = v;
27072     }
27073     
27074 });/*
27075  * Based on:
27076  * Ext JS Library 1.1.1
27077  * Copyright(c) 2006-2007, Ext JS, LLC.
27078  *
27079  * Originally Released Under LGPL - original licence link has changed is not relivant.
27080  *
27081  * Fork - LGPL
27082  * <script type="text/javascript">
27083  */
27084  
27085 /**
27086  * @class Roo.form.Signature
27087  * @extends Roo.form.Field
27088  * Signature field.  
27089  * @constructor
27090  * 
27091  * @param {Object} config Configuration options
27092  */
27093
27094 Roo.form.Signature = function(config){
27095     Roo.form.Signature.superclass.constructor.call(this, config);
27096     
27097     this.addEvents({// not in used??
27098          /**
27099          * @event confirm
27100          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27101              * @param {Roo.form.Signature} combo This combo box
27102              */
27103         'confirm' : true,
27104         /**
27105          * @event reset
27106          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27107              * @param {Roo.form.ComboBox} combo This combo box
27108              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27109              */
27110         'reset' : true
27111     });
27112 };
27113
27114 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27115     /**
27116      * @cfg {Object} labels Label to use when rendering a form.
27117      * defaults to 
27118      * labels : { 
27119      *      clear : "Clear",
27120      *      confirm : "Confirm"
27121      *  }
27122      */
27123     labels : { 
27124         clear : "Clear",
27125         confirm : "Confirm"
27126     },
27127     /**
27128      * @cfg {Number} width The signature panel width (defaults to 300)
27129      */
27130     width: 300,
27131     /**
27132      * @cfg {Number} height The signature panel height (defaults to 100)
27133      */
27134     height : 100,
27135     /**
27136      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27137      */
27138     allowBlank : false,
27139     
27140     //private
27141     // {Object} signPanel The signature SVG panel element (defaults to {})
27142     signPanel : {},
27143     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27144     isMouseDown : false,
27145     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27146     isConfirmed : false,
27147     // {String} signatureTmp SVG mapping string (defaults to empty string)
27148     signatureTmp : '',
27149     
27150     
27151     defaultAutoCreate : { // modified by initCompnoent..
27152         tag: "input",
27153         type:"hidden"
27154     },
27155
27156     // private
27157     onRender : function(ct, position){
27158         
27159         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27160         
27161         this.wrap = this.el.wrap({
27162             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27163         });
27164         
27165         this.createToolbar(this);
27166         this.signPanel = this.wrap.createChild({
27167                 tag: 'div',
27168                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27169             }, this.el
27170         );
27171             
27172         this.svgID = Roo.id();
27173         this.svgEl = this.signPanel.createChild({
27174               xmlns : 'http://www.w3.org/2000/svg',
27175               tag : 'svg',
27176               id : this.svgID + "-svg",
27177               width: this.width,
27178               height: this.height,
27179               viewBox: '0 0 '+this.width+' '+this.height,
27180               cn : [
27181                 {
27182                     tag: "rect",
27183                     id: this.svgID + "-svg-r",
27184                     width: this.width,
27185                     height: this.height,
27186                     fill: "#ffa"
27187                 },
27188                 {
27189                     tag: "line",
27190                     id: this.svgID + "-svg-l",
27191                     x1: "0", // start
27192                     y1: (this.height*0.8), // start set the line in 80% of height
27193                     x2: this.width, // end
27194                     y2: (this.height*0.8), // end set the line in 80% of height
27195                     'stroke': "#666",
27196                     'stroke-width': "1",
27197                     'stroke-dasharray': "3",
27198                     'shape-rendering': "crispEdges",
27199                     'pointer-events': "none"
27200                 },
27201                 {
27202                     tag: "path",
27203                     id: this.svgID + "-svg-p",
27204                     'stroke': "navy",
27205                     'stroke-width': "3",
27206                     'fill': "none",
27207                     'pointer-events': 'none'
27208                 }
27209               ]
27210         });
27211         this.createSVG();
27212         this.svgBox = this.svgEl.dom.getScreenCTM();
27213     },
27214     createSVG : function(){ 
27215         var svg = this.signPanel;
27216         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27217         var t = this;
27218
27219         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27220         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27221         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27222         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27223         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27224         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27225         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27226         
27227     },
27228     isTouchEvent : function(e){
27229         return e.type.match(/^touch/);
27230     },
27231     getCoords : function (e) {
27232         var pt    = this.svgEl.dom.createSVGPoint();
27233         pt.x = e.clientX; 
27234         pt.y = e.clientY;
27235         if (this.isTouchEvent(e)) {
27236             pt.x =  e.targetTouches[0].clientX;
27237             pt.y = e.targetTouches[0].clientY;
27238         }
27239         var a = this.svgEl.dom.getScreenCTM();
27240         var b = a.inverse();
27241         var mx = pt.matrixTransform(b);
27242         return mx.x + ',' + mx.y;
27243     },
27244     //mouse event headler 
27245     down : function (e) {
27246         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27247         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27248         
27249         this.isMouseDown = true;
27250         
27251         e.preventDefault();
27252     },
27253     move : function (e) {
27254         if (this.isMouseDown) {
27255             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27256             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27257         }
27258         
27259         e.preventDefault();
27260     },
27261     up : function (e) {
27262         this.isMouseDown = false;
27263         var sp = this.signatureTmp.split(' ');
27264         
27265         if(sp.length > 1){
27266             if(!sp[sp.length-2].match(/^L/)){
27267                 sp.pop();
27268                 sp.pop();
27269                 sp.push("");
27270                 this.signatureTmp = sp.join(" ");
27271             }
27272         }
27273         if(this.getValue() != this.signatureTmp){
27274             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27275             this.isConfirmed = false;
27276         }
27277         e.preventDefault();
27278     },
27279     
27280     /**
27281      * Protected method that will not generally be called directly. It
27282      * is called when the editor creates its toolbar. Override this method if you need to
27283      * add custom toolbar buttons.
27284      * @param {HtmlEditor} editor
27285      */
27286     createToolbar : function(editor){
27287          function btn(id, toggle, handler){
27288             var xid = fid + '-'+ id ;
27289             return {
27290                 id : xid,
27291                 cmd : id,
27292                 cls : 'x-btn-icon x-edit-'+id,
27293                 enableToggle:toggle !== false,
27294                 scope: editor, // was editor...
27295                 handler:handler||editor.relayBtnCmd,
27296                 clickEvent:'mousedown',
27297                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27298                 tabIndex:-1
27299             };
27300         }
27301         
27302         
27303         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27304         this.tb = tb;
27305         this.tb.add(
27306            {
27307                 cls : ' x-signature-btn x-signature-'+id,
27308                 scope: editor, // was editor...
27309                 handler: this.reset,
27310                 clickEvent:'mousedown',
27311                 text: this.labels.clear
27312             },
27313             {
27314                  xtype : 'Fill',
27315                  xns: Roo.Toolbar
27316             }, 
27317             {
27318                 cls : '  x-signature-btn x-signature-'+id,
27319                 scope: editor, // was editor...
27320                 handler: this.confirmHandler,
27321                 clickEvent:'mousedown',
27322                 text: this.labels.confirm
27323             }
27324         );
27325     
27326     },
27327     //public
27328     /**
27329      * when user is clicked confirm then show this image.....
27330      * 
27331      * @return {String} Image Data URI
27332      */
27333     getImageDataURI : function(){
27334         var svg = this.svgEl.dom.parentNode.innerHTML;
27335         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27336         return src; 
27337     },
27338     /**
27339      * 
27340      * @return {Boolean} this.isConfirmed
27341      */
27342     getConfirmed : function(){
27343         return this.isConfirmed;
27344     },
27345     /**
27346      * 
27347      * @return {Number} this.width
27348      */
27349     getWidth : function(){
27350         return this.width;
27351     },
27352     /**
27353      * 
27354      * @return {Number} this.height
27355      */
27356     getHeight : function(){
27357         return this.height;
27358     },
27359     // private
27360     getSignature : function(){
27361         return this.signatureTmp;
27362     },
27363     // private
27364     reset : function(){
27365         this.signatureTmp = '';
27366         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27367         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27368         this.isConfirmed = false;
27369         Roo.form.Signature.superclass.reset.call(this);
27370     },
27371     setSignature : function(s){
27372         this.signatureTmp = s;
27373         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27374         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27375         this.setValue(s);
27376         this.isConfirmed = false;
27377         Roo.form.Signature.superclass.reset.call(this);
27378     }, 
27379     test : function(){
27380 //        Roo.log(this.signPanel.dom.contentWindow.up())
27381     },
27382     //private
27383     setConfirmed : function(){
27384         
27385         
27386         
27387 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27388     },
27389     // private
27390     confirmHandler : function(){
27391         if(!this.getSignature()){
27392             return;
27393         }
27394         
27395         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27396         this.setValue(this.getSignature());
27397         this.isConfirmed = true;
27398         
27399         this.fireEvent('confirm', this);
27400     },
27401     // private
27402     // Subclasses should provide the validation implementation by overriding this
27403     validateValue : function(value){
27404         if(this.allowBlank){
27405             return true;
27406         }
27407         
27408         if(this.isConfirmed){
27409             return true;
27410         }
27411         return false;
27412     }
27413 });/*
27414  * Based on:
27415  * Ext JS Library 1.1.1
27416  * Copyright(c) 2006-2007, Ext JS, LLC.
27417  *
27418  * Originally Released Under LGPL - original licence link has changed is not relivant.
27419  *
27420  * Fork - LGPL
27421  * <script type="text/javascript">
27422  */
27423  
27424
27425 /**
27426  * @class Roo.form.ComboBox
27427  * @extends Roo.form.TriggerField
27428  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27429  * @constructor
27430  * Create a new ComboBox.
27431  * @param {Object} config Configuration options
27432  */
27433 Roo.form.Select = function(config){
27434     Roo.form.Select.superclass.constructor.call(this, config);
27435      
27436 };
27437
27438 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27439     /**
27440      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27441      */
27442     /**
27443      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27444      * rendering into an Roo.Editor, defaults to false)
27445      */
27446     /**
27447      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27448      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27449      */
27450     /**
27451      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27452      */
27453     /**
27454      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27455      * the dropdown list (defaults to undefined, with no header element)
27456      */
27457
27458      /**
27459      * @cfg {String/Roo.Template} tpl The template to use to render the output
27460      */
27461      
27462     // private
27463     defaultAutoCreate : {tag: "select"  },
27464     /**
27465      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27466      */
27467     listWidth: undefined,
27468     /**
27469      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27470      * mode = 'remote' or 'text' if mode = 'local')
27471      */
27472     displayField: undefined,
27473     /**
27474      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27475      * mode = 'remote' or 'value' if mode = 'local'). 
27476      * Note: use of a valueField requires the user make a selection
27477      * in order for a value to be mapped.
27478      */
27479     valueField: undefined,
27480     
27481     
27482     /**
27483      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27484      * field's data value (defaults to the underlying DOM element's name)
27485      */
27486     hiddenName: undefined,
27487     /**
27488      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27489      */
27490     listClass: '',
27491     /**
27492      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27493      */
27494     selectedClass: 'x-combo-selected',
27495     /**
27496      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27497      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27498      * which displays a downward arrow icon).
27499      */
27500     triggerClass : 'x-form-arrow-trigger',
27501     /**
27502      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27503      */
27504     shadow:'sides',
27505     /**
27506      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27507      * anchor positions (defaults to 'tl-bl')
27508      */
27509     listAlign: 'tl-bl?',
27510     /**
27511      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27512      */
27513     maxHeight: 300,
27514     /**
27515      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27516      * query specified by the allQuery config option (defaults to 'query')
27517      */
27518     triggerAction: 'query',
27519     /**
27520      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27521      * (defaults to 4, does not apply if editable = false)
27522      */
27523     minChars : 4,
27524     /**
27525      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27526      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27527      */
27528     typeAhead: false,
27529     /**
27530      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27531      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27532      */
27533     queryDelay: 500,
27534     /**
27535      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27536      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27537      */
27538     pageSize: 0,
27539     /**
27540      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27541      * when editable = true (defaults to false)
27542      */
27543     selectOnFocus:false,
27544     /**
27545      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27546      */
27547     queryParam: 'query',
27548     /**
27549      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27550      * when mode = 'remote' (defaults to 'Loading...')
27551      */
27552     loadingText: 'Loading...',
27553     /**
27554      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27555      */
27556     resizable: false,
27557     /**
27558      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27559      */
27560     handleHeight : 8,
27561     /**
27562      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27563      * traditional select (defaults to true)
27564      */
27565     editable: true,
27566     /**
27567      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27568      */
27569     allQuery: '',
27570     /**
27571      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27572      */
27573     mode: 'remote',
27574     /**
27575      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27576      * listWidth has a higher value)
27577      */
27578     minListWidth : 70,
27579     /**
27580      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27581      * allow the user to set arbitrary text into the field (defaults to false)
27582      */
27583     forceSelection:false,
27584     /**
27585      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27586      * if typeAhead = true (defaults to 250)
27587      */
27588     typeAheadDelay : 250,
27589     /**
27590      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27591      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27592      */
27593     valueNotFoundText : undefined,
27594     
27595     /**
27596      * @cfg {String} defaultValue The value displayed after loading the store.
27597      */
27598     defaultValue: '',
27599     
27600     /**
27601      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27602      */
27603     blockFocus : false,
27604     
27605     /**
27606      * @cfg {Boolean} disableClear Disable showing of clear button.
27607      */
27608     disableClear : false,
27609     /**
27610      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
27611      */
27612     alwaysQuery : false,
27613     
27614     //private
27615     addicon : false,
27616     editicon: false,
27617     
27618     // element that contains real text value.. (when hidden is used..)
27619      
27620     // private
27621     onRender : function(ct, position){
27622         Roo.form.Field.prototype.onRender.call(this, ct, position);
27623         
27624         if(this.store){
27625             this.store.on('beforeload', this.onBeforeLoad, this);
27626             this.store.on('load', this.onLoad, this);
27627             this.store.on('loadexception', this.onLoadException, this);
27628             this.store.load({});
27629         }
27630         
27631         
27632         
27633     },
27634
27635     // private
27636     initEvents : function(){
27637         //Roo.form.ComboBox.superclass.initEvents.call(this);
27638  
27639     },
27640
27641     onDestroy : function(){
27642        
27643         if(this.store){
27644             this.store.un('beforeload', this.onBeforeLoad, this);
27645             this.store.un('load', this.onLoad, this);
27646             this.store.un('loadexception', this.onLoadException, this);
27647         }
27648         //Roo.form.ComboBox.superclass.onDestroy.call(this);
27649     },
27650
27651     // private
27652     fireKey : function(e){
27653         if(e.isNavKeyPress() && !this.list.isVisible()){
27654             this.fireEvent("specialkey", this, e);
27655         }
27656     },
27657
27658     // private
27659     onResize: function(w, h){
27660         
27661         return; 
27662     
27663         
27664     },
27665
27666     /**
27667      * Allow or prevent the user from directly editing the field text.  If false is passed,
27668      * the user will only be able to select from the items defined in the dropdown list.  This method
27669      * is the runtime equivalent of setting the 'editable' config option at config time.
27670      * @param {Boolean} value True to allow the user to directly edit the field text
27671      */
27672     setEditable : function(value){
27673          
27674     },
27675
27676     // private
27677     onBeforeLoad : function(){
27678         
27679         Roo.log("Select before load");
27680         return;
27681     
27682         this.innerList.update(this.loadingText ?
27683                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
27684         //this.restrictHeight();
27685         this.selectedIndex = -1;
27686     },
27687
27688     // private
27689     onLoad : function(){
27690
27691     
27692         var dom = this.el.dom;
27693         dom.innerHTML = '';
27694          var od = dom.ownerDocument;
27695          
27696         if (this.emptyText) {
27697             var op = od.createElement('option');
27698             op.setAttribute('value', '');
27699             op.innerHTML = String.format('{0}', this.emptyText);
27700             dom.appendChild(op);
27701         }
27702         if(this.store.getCount() > 0){
27703            
27704             var vf = this.valueField;
27705             var df = this.displayField;
27706             this.store.data.each(function(r) {
27707                 // which colmsn to use... testing - cdoe / title..
27708                 var op = od.createElement('option');
27709                 op.setAttribute('value', r.data[vf]);
27710                 op.innerHTML = String.format('{0}', r.data[df]);
27711                 dom.appendChild(op);
27712             });
27713             if (typeof(this.defaultValue != 'undefined')) {
27714                 this.setValue(this.defaultValue);
27715             }
27716             
27717              
27718         }else{
27719             //this.onEmptyResults();
27720         }
27721         //this.el.focus();
27722     },
27723     // private
27724     onLoadException : function()
27725     {
27726         dom.innerHTML = '';
27727             
27728         Roo.log("Select on load exception");
27729         return;
27730     
27731         this.collapse();
27732         Roo.log(this.store.reader.jsonData);
27733         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
27734             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
27735         }
27736         
27737         
27738     },
27739     // private
27740     onTypeAhead : function(){
27741          
27742     },
27743
27744     // private
27745     onSelect : function(record, index){
27746         Roo.log('on select?');
27747         return;
27748         if(this.fireEvent('beforeselect', this, record, index) !== false){
27749             this.setFromData(index > -1 ? record.data : false);
27750             this.collapse();
27751             this.fireEvent('select', this, record, index);
27752         }
27753     },
27754
27755     /**
27756      * Returns the currently selected field value or empty string if no value is set.
27757      * @return {String} value The selected value
27758      */
27759     getValue : function(){
27760         var dom = this.el.dom;
27761         this.value = dom.options[dom.selectedIndex].value;
27762         return this.value;
27763         
27764     },
27765
27766     /**
27767      * Clears any text/value currently set in the field
27768      */
27769     clearValue : function(){
27770         this.value = '';
27771         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
27772         
27773     },
27774
27775     /**
27776      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
27777      * will be displayed in the field.  If the value does not match the data value of an existing item,
27778      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
27779      * Otherwise the field will be blank (although the value will still be set).
27780      * @param {String} value The value to match
27781      */
27782     setValue : function(v){
27783         var d = this.el.dom;
27784         for (var i =0; i < d.options.length;i++) {
27785             if (v == d.options[i].value) {
27786                 d.selectedIndex = i;
27787                 this.value = v;
27788                 return;
27789             }
27790         }
27791         this.clearValue();
27792     },
27793     /**
27794      * @property {Object} the last set data for the element
27795      */
27796     
27797     lastData : false,
27798     /**
27799      * Sets the value of the field based on a object which is related to the record format for the store.
27800      * @param {Object} value the value to set as. or false on reset?
27801      */
27802     setFromData : function(o){
27803         Roo.log('setfrom data?');
27804          
27805         
27806         
27807     },
27808     // private
27809     reset : function(){
27810         this.clearValue();
27811     },
27812     // private
27813     findRecord : function(prop, value){
27814         
27815         return false;
27816     
27817         var record;
27818         if(this.store.getCount() > 0){
27819             this.store.each(function(r){
27820                 if(r.data[prop] == value){
27821                     record = r;
27822                     return false;
27823                 }
27824                 return true;
27825             });
27826         }
27827         return record;
27828     },
27829     
27830     getName: function()
27831     {
27832         // returns hidden if it's set..
27833         if (!this.rendered) {return ''};
27834         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
27835         
27836     },
27837      
27838
27839     
27840
27841     // private
27842     onEmptyResults : function(){
27843         Roo.log('empty results');
27844         //this.collapse();
27845     },
27846
27847     /**
27848      * Returns true if the dropdown list is expanded, else false.
27849      */
27850     isExpanded : function(){
27851         return false;
27852     },
27853
27854     /**
27855      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
27856      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27857      * @param {String} value The data value of the item to select
27858      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27859      * selected item if it is not currently in view (defaults to true)
27860      * @return {Boolean} True if the value matched an item in the list, else false
27861      */
27862     selectByValue : function(v, scrollIntoView){
27863         Roo.log('select By Value');
27864         return false;
27865     
27866         if(v !== undefined && v !== null){
27867             var r = this.findRecord(this.valueField || this.displayField, v);
27868             if(r){
27869                 this.select(this.store.indexOf(r), scrollIntoView);
27870                 return true;
27871             }
27872         }
27873         return false;
27874     },
27875
27876     /**
27877      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
27878      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27879      * @param {Number} index The zero-based index of the list item to select
27880      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27881      * selected item if it is not currently in view (defaults to true)
27882      */
27883     select : function(index, scrollIntoView){
27884         Roo.log('select ');
27885         return  ;
27886         
27887         this.selectedIndex = index;
27888         this.view.select(index);
27889         if(scrollIntoView !== false){
27890             var el = this.view.getNode(index);
27891             if(el){
27892                 this.innerList.scrollChildIntoView(el, false);
27893             }
27894         }
27895     },
27896
27897       
27898
27899     // private
27900     validateBlur : function(){
27901         
27902         return;
27903         
27904     },
27905
27906     // private
27907     initQuery : function(){
27908         this.doQuery(this.getRawValue());
27909     },
27910
27911     // private
27912     doForce : function(){
27913         if(this.el.dom.value.length > 0){
27914             this.el.dom.value =
27915                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
27916              
27917         }
27918     },
27919
27920     /**
27921      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
27922      * query allowing the query action to be canceled if needed.
27923      * @param {String} query The SQL query to execute
27924      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
27925      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
27926      * saved in the current store (defaults to false)
27927      */
27928     doQuery : function(q, forceAll){
27929         
27930         Roo.log('doQuery?');
27931         if(q === undefined || q === null){
27932             q = '';
27933         }
27934         var qe = {
27935             query: q,
27936             forceAll: forceAll,
27937             combo: this,
27938             cancel:false
27939         };
27940         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
27941             return false;
27942         }
27943         q = qe.query;
27944         forceAll = qe.forceAll;
27945         if(forceAll === true || (q.length >= this.minChars)){
27946             if(this.lastQuery != q || this.alwaysQuery){
27947                 this.lastQuery = q;
27948                 if(this.mode == 'local'){
27949                     this.selectedIndex = -1;
27950                     if(forceAll){
27951                         this.store.clearFilter();
27952                     }else{
27953                         this.store.filter(this.displayField, q);
27954                     }
27955                     this.onLoad();
27956                 }else{
27957                     this.store.baseParams[this.queryParam] = q;
27958                     this.store.load({
27959                         params: this.getParams(q)
27960                     });
27961                     this.expand();
27962                 }
27963             }else{
27964                 this.selectedIndex = -1;
27965                 this.onLoad();   
27966             }
27967         }
27968     },
27969
27970     // private
27971     getParams : function(q){
27972         var p = {};
27973         //p[this.queryParam] = q;
27974         if(this.pageSize){
27975             p.start = 0;
27976             p.limit = this.pageSize;
27977         }
27978         return p;
27979     },
27980
27981     /**
27982      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
27983      */
27984     collapse : function(){
27985         
27986     },
27987
27988     // private
27989     collapseIf : function(e){
27990         
27991     },
27992
27993     /**
27994      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
27995      */
27996     expand : function(){
27997         
27998     } ,
27999
28000     // private
28001      
28002
28003     /** 
28004     * @cfg {Boolean} grow 
28005     * @hide 
28006     */
28007     /** 
28008     * @cfg {Number} growMin 
28009     * @hide 
28010     */
28011     /** 
28012     * @cfg {Number} growMax 
28013     * @hide 
28014     */
28015     /**
28016      * @hide
28017      * @method autoSize
28018      */
28019     
28020     setWidth : function()
28021     {
28022         
28023     },
28024     getResizeEl : function(){
28025         return this.el;
28026     }
28027 });//<script type="text/javasscript">
28028  
28029
28030 /**
28031  * @class Roo.DDView
28032  * A DnD enabled version of Roo.View.
28033  * @param {Element/String} container The Element in which to create the View.
28034  * @param {String} tpl The template string used to create the markup for each element of the View
28035  * @param {Object} config The configuration properties. These include all the config options of
28036  * {@link Roo.View} plus some specific to this class.<br>
28037  * <p>
28038  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28039  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28040  * <p>
28041  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28042 .x-view-drag-insert-above {
28043         border-top:1px dotted #3366cc;
28044 }
28045 .x-view-drag-insert-below {
28046         border-bottom:1px dotted #3366cc;
28047 }
28048 </code></pre>
28049  * 
28050  */
28051  
28052 Roo.DDView = function(container, tpl, config) {
28053     Roo.DDView.superclass.constructor.apply(this, arguments);
28054     this.getEl().setStyle("outline", "0px none");
28055     this.getEl().unselectable();
28056     if (this.dragGroup) {
28057                 this.setDraggable(this.dragGroup.split(","));
28058     }
28059     if (this.dropGroup) {
28060                 this.setDroppable(this.dropGroup.split(","));
28061     }
28062     if (this.deletable) {
28063         this.setDeletable();
28064     }
28065     this.isDirtyFlag = false;
28066         this.addEvents({
28067                 "drop" : true
28068         });
28069 };
28070
28071 Roo.extend(Roo.DDView, Roo.View, {
28072 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28073 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28074 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28075 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28076
28077         isFormField: true,
28078
28079         reset: Roo.emptyFn,
28080         
28081         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28082
28083         validate: function() {
28084                 return true;
28085         },
28086         
28087         destroy: function() {
28088                 this.purgeListeners();
28089                 this.getEl.removeAllListeners();
28090                 this.getEl().remove();
28091                 if (this.dragZone) {
28092                         if (this.dragZone.destroy) {
28093                                 this.dragZone.destroy();
28094                         }
28095                 }
28096                 if (this.dropZone) {
28097                         if (this.dropZone.destroy) {
28098                                 this.dropZone.destroy();
28099                         }
28100                 }
28101         },
28102
28103 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28104         getName: function() {
28105                 return this.name;
28106         },
28107
28108 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28109         setValue: function(v) {
28110                 if (!this.store) {
28111                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28112                 }
28113                 var data = {};
28114                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28115                 this.store.proxy = new Roo.data.MemoryProxy(data);
28116                 this.store.load();
28117         },
28118
28119 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28120         getValue: function() {
28121                 var result = '(';
28122                 this.store.each(function(rec) {
28123                         result += rec.id + ',';
28124                 });
28125                 return result.substr(0, result.length - 1) + ')';
28126         },
28127         
28128         getIds: function() {
28129                 var i = 0, result = new Array(this.store.getCount());
28130                 this.store.each(function(rec) {
28131                         result[i++] = rec.id;
28132                 });
28133                 return result;
28134         },
28135         
28136         isDirty: function() {
28137                 return this.isDirtyFlag;
28138         },
28139
28140 /**
28141  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28142  *      whole Element becomes the target, and this causes the drop gesture to append.
28143  */
28144     getTargetFromEvent : function(e) {
28145                 var target = e.getTarget();
28146                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28147                 target = target.parentNode;
28148                 }
28149                 if (!target) {
28150                         target = this.el.dom.lastChild || this.el.dom;
28151                 }
28152                 return target;
28153     },
28154
28155 /**
28156  *      Create the drag data which consists of an object which has the property "ddel" as
28157  *      the drag proxy element. 
28158  */
28159     getDragData : function(e) {
28160         var target = this.findItemFromChild(e.getTarget());
28161                 if(target) {
28162                         this.handleSelection(e);
28163                         var selNodes = this.getSelectedNodes();
28164             var dragData = {
28165                 source: this,
28166                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28167                 nodes: selNodes,
28168                 records: []
28169                         };
28170                         var selectedIndices = this.getSelectedIndexes();
28171                         for (var i = 0; i < selectedIndices.length; i++) {
28172                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28173                         }
28174                         if (selNodes.length == 1) {
28175                                 dragData.ddel = target.cloneNode(true); // the div element
28176                         } else {
28177                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28178                                 div.className = 'multi-proxy';
28179                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28180                                         div.appendChild(selNodes[i].cloneNode(true));
28181                                 }
28182                                 dragData.ddel = div;
28183                         }
28184             //console.log(dragData)
28185             //console.log(dragData.ddel.innerHTML)
28186                         return dragData;
28187                 }
28188         //console.log('nodragData')
28189                 return false;
28190     },
28191     
28192 /**     Specify to which ddGroup items in this DDView may be dragged. */
28193     setDraggable: function(ddGroup) {
28194         if (ddGroup instanceof Array) {
28195                 Roo.each(ddGroup, this.setDraggable, this);
28196                 return;
28197         }
28198         if (this.dragZone) {
28199                 this.dragZone.addToGroup(ddGroup);
28200         } else {
28201                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28202                                 containerScroll: true,
28203                                 ddGroup: ddGroup 
28204
28205                         });
28206 //                      Draggability implies selection. DragZone's mousedown selects the element.
28207                         if (!this.multiSelect) { this.singleSelect = true; }
28208
28209 //                      Wire the DragZone's handlers up to methods in *this*
28210                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28211                 }
28212     },
28213
28214 /**     Specify from which ddGroup this DDView accepts drops. */
28215     setDroppable: function(ddGroup) {
28216         if (ddGroup instanceof Array) {
28217                 Roo.each(ddGroup, this.setDroppable, this);
28218                 return;
28219         }
28220         if (this.dropZone) {
28221                 this.dropZone.addToGroup(ddGroup);
28222         } else {
28223                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28224                                 containerScroll: true,
28225                                 ddGroup: ddGroup
28226                         });
28227
28228 //                      Wire the DropZone's handlers up to methods in *this*
28229                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28230                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28231                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28232                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28233                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28234                 }
28235     },
28236
28237 /**     Decide whether to drop above or below a View node. */
28238     getDropPoint : function(e, n, dd){
28239         if (n == this.el.dom) { return "above"; }
28240                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28241                 var c = t + (b - t) / 2;
28242                 var y = Roo.lib.Event.getPageY(e);
28243                 if(y <= c) {
28244                         return "above";
28245                 }else{
28246                         return "below";
28247                 }
28248     },
28249
28250     onNodeEnter : function(n, dd, e, data){
28251                 return false;
28252     },
28253     
28254     onNodeOver : function(n, dd, e, data){
28255                 var pt = this.getDropPoint(e, n, dd);
28256                 // set the insert point style on the target node
28257                 var dragElClass = this.dropNotAllowed;
28258                 if (pt) {
28259                         var targetElClass;
28260                         if (pt == "above"){
28261                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28262                                 targetElClass = "x-view-drag-insert-above";
28263                         } else {
28264                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28265                                 targetElClass = "x-view-drag-insert-below";
28266                         }
28267                         if (this.lastInsertClass != targetElClass){
28268                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28269                                 this.lastInsertClass = targetElClass;
28270                         }
28271                 }
28272                 return dragElClass;
28273         },
28274
28275     onNodeOut : function(n, dd, e, data){
28276                 this.removeDropIndicators(n);
28277     },
28278
28279     onNodeDrop : function(n, dd, e, data){
28280         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28281                 return false;
28282         }
28283         var pt = this.getDropPoint(e, n, dd);
28284                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28285                 if (pt == "below") { insertAt++; }
28286                 for (var i = 0; i < data.records.length; i++) {
28287                         var r = data.records[i];
28288                         var dup = this.store.getById(r.id);
28289                         if (dup && (dd != this.dragZone)) {
28290                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28291                         } else {
28292                                 if (data.copy) {
28293                                         this.store.insert(insertAt++, r.copy());
28294                                 } else {
28295                                         data.source.isDirtyFlag = true;
28296                                         r.store.remove(r);
28297                                         this.store.insert(insertAt++, r);
28298                                 }
28299                                 this.isDirtyFlag = true;
28300                         }
28301                 }
28302                 this.dragZone.cachedTarget = null;
28303                 return true;
28304     },
28305
28306     removeDropIndicators : function(n){
28307                 if(n){
28308                         Roo.fly(n).removeClass([
28309                                 "x-view-drag-insert-above",
28310                                 "x-view-drag-insert-below"]);
28311                         this.lastInsertClass = "_noclass";
28312                 }
28313     },
28314
28315 /**
28316  *      Utility method. Add a delete option to the DDView's context menu.
28317  *      @param {String} imageUrl The URL of the "delete" icon image.
28318  */
28319         setDeletable: function(imageUrl) {
28320                 if (!this.singleSelect && !this.multiSelect) {
28321                         this.singleSelect = true;
28322                 }
28323                 var c = this.getContextMenu();
28324                 this.contextMenu.on("itemclick", function(item) {
28325                         switch (item.id) {
28326                                 case "delete":
28327                                         this.remove(this.getSelectedIndexes());
28328                                         break;
28329                         }
28330                 }, this);
28331                 this.contextMenu.add({
28332                         icon: imageUrl,
28333                         id: "delete",
28334                         text: 'Delete'
28335                 });
28336         },
28337         
28338 /**     Return the context menu for this DDView. */
28339         getContextMenu: function() {
28340                 if (!this.contextMenu) {
28341 //                      Create the View's context menu
28342                         this.contextMenu = new Roo.menu.Menu({
28343                                 id: this.id + "-contextmenu"
28344                         });
28345                         this.el.on("contextmenu", this.showContextMenu, this);
28346                 }
28347                 return this.contextMenu;
28348         },
28349         
28350         disableContextMenu: function() {
28351                 if (this.contextMenu) {
28352                         this.el.un("contextmenu", this.showContextMenu, this);
28353                 }
28354         },
28355
28356         showContextMenu: function(e, item) {
28357         item = this.findItemFromChild(e.getTarget());
28358                 if (item) {
28359                         e.stopEvent();
28360                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28361                         this.contextMenu.showAt(e.getXY());
28362             }
28363     },
28364
28365 /**
28366  *      Remove {@link Roo.data.Record}s at the specified indices.
28367  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28368  */
28369     remove: function(selectedIndices) {
28370                 selectedIndices = [].concat(selectedIndices);
28371                 for (var i = 0; i < selectedIndices.length; i++) {
28372                         var rec = this.store.getAt(selectedIndices[i]);
28373                         this.store.remove(rec);
28374                 }
28375     },
28376
28377 /**
28378  *      Double click fires the event, but also, if this is draggable, and there is only one other
28379  *      related DropZone, it transfers the selected node.
28380  */
28381     onDblClick : function(e){
28382         var item = this.findItemFromChild(e.getTarget());
28383         if(item){
28384             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28385                 return false;
28386             }
28387             if (this.dragGroup) {
28388                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28389                     while (targets.indexOf(this.dropZone) > -1) {
28390                             targets.remove(this.dropZone);
28391                                 }
28392                     if (targets.length == 1) {
28393                                         this.dragZone.cachedTarget = null;
28394                         var el = Roo.get(targets[0].getEl());
28395                         var box = el.getBox(true);
28396                         targets[0].onNodeDrop(el.dom, {
28397                                 target: el.dom,
28398                                 xy: [box.x, box.y + box.height - 1]
28399                         }, null, this.getDragData(e));
28400                     }
28401                 }
28402         }
28403     },
28404     
28405     handleSelection: function(e) {
28406                 this.dragZone.cachedTarget = null;
28407         var item = this.findItemFromChild(e.getTarget());
28408         if (!item) {
28409                 this.clearSelections(true);
28410                 return;
28411         }
28412                 if (item && (this.multiSelect || this.singleSelect)){
28413                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28414                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28415                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28416                                 this.unselect(item);
28417                         } else {
28418                                 this.select(item, this.multiSelect && e.ctrlKey);
28419                                 this.lastSelection = item;
28420                         }
28421                 }
28422     },
28423
28424     onItemClick : function(item, index, e){
28425                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28426                         return false;
28427                 }
28428                 return true;
28429     },
28430
28431     unselect : function(nodeInfo, suppressEvent){
28432                 var node = this.getNode(nodeInfo);
28433                 if(node && this.isSelected(node)){
28434                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28435                                 Roo.fly(node).removeClass(this.selectedClass);
28436                                 this.selections.remove(node);
28437                                 if(!suppressEvent){
28438                                         this.fireEvent("selectionchange", this, this.selections);
28439                                 }
28440                         }
28441                 }
28442     }
28443 });
28444 /*
28445  * Based on:
28446  * Ext JS Library 1.1.1
28447  * Copyright(c) 2006-2007, Ext JS, LLC.
28448  *
28449  * Originally Released Under LGPL - original licence link has changed is not relivant.
28450  *
28451  * Fork - LGPL
28452  * <script type="text/javascript">
28453  */
28454  
28455 /**
28456  * @class Roo.LayoutManager
28457  * @extends Roo.util.Observable
28458  * Base class for layout managers.
28459  */
28460 Roo.LayoutManager = function(container, config){
28461     Roo.LayoutManager.superclass.constructor.call(this);
28462     this.el = Roo.get(container);
28463     // ie scrollbar fix
28464     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28465         document.body.scroll = "no";
28466     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28467         this.el.position('relative');
28468     }
28469     this.id = this.el.id;
28470     this.el.addClass("x-layout-container");
28471     /** false to disable window resize monitoring @type Boolean */
28472     this.monitorWindowResize = true;
28473     this.regions = {};
28474     this.addEvents({
28475         /**
28476          * @event layout
28477          * Fires when a layout is performed. 
28478          * @param {Roo.LayoutManager} this
28479          */
28480         "layout" : true,
28481         /**
28482          * @event regionresized
28483          * Fires when the user resizes a region. 
28484          * @param {Roo.LayoutRegion} region The resized region
28485          * @param {Number} newSize The new size (width for east/west, height for north/south)
28486          */
28487         "regionresized" : true,
28488         /**
28489          * @event regioncollapsed
28490          * Fires when a region is collapsed. 
28491          * @param {Roo.LayoutRegion} region The collapsed region
28492          */
28493         "regioncollapsed" : true,
28494         /**
28495          * @event regionexpanded
28496          * Fires when a region is expanded.  
28497          * @param {Roo.LayoutRegion} region The expanded region
28498          */
28499         "regionexpanded" : true
28500     });
28501     this.updating = false;
28502     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28503 };
28504
28505 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28506     /**
28507      * Returns true if this layout is currently being updated
28508      * @return {Boolean}
28509      */
28510     isUpdating : function(){
28511         return this.updating; 
28512     },
28513     
28514     /**
28515      * Suspend the LayoutManager from doing auto-layouts while
28516      * making multiple add or remove calls
28517      */
28518     beginUpdate : function(){
28519         this.updating = true;    
28520     },
28521     
28522     /**
28523      * Restore auto-layouts and optionally disable the manager from performing a layout
28524      * @param {Boolean} noLayout true to disable a layout update 
28525      */
28526     endUpdate : function(noLayout){
28527         this.updating = false;
28528         if(!noLayout){
28529             this.layout();
28530         }    
28531     },
28532     
28533     layout: function(){
28534         
28535     },
28536     
28537     onRegionResized : function(region, newSize){
28538         this.fireEvent("regionresized", region, newSize);
28539         this.layout();
28540     },
28541     
28542     onRegionCollapsed : function(region){
28543         this.fireEvent("regioncollapsed", region);
28544     },
28545     
28546     onRegionExpanded : function(region){
28547         this.fireEvent("regionexpanded", region);
28548     },
28549         
28550     /**
28551      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28552      * performs box-model adjustments.
28553      * @return {Object} The size as an object {width: (the width), height: (the height)}
28554      */
28555     getViewSize : function(){
28556         var size;
28557         if(this.el.dom != document.body){
28558             size = this.el.getSize();
28559         }else{
28560             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28561         }
28562         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28563         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28564         return size;
28565     },
28566     
28567     /**
28568      * Returns the Element this layout is bound to.
28569      * @return {Roo.Element}
28570      */
28571     getEl : function(){
28572         return this.el;
28573     },
28574     
28575     /**
28576      * Returns the specified region.
28577      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28578      * @return {Roo.LayoutRegion}
28579      */
28580     getRegion : function(target){
28581         return this.regions[target.toLowerCase()];
28582     },
28583     
28584     onWindowResize : function(){
28585         if(this.monitorWindowResize){
28586             this.layout();
28587         }
28588     }
28589 });/*
28590  * Based on:
28591  * Ext JS Library 1.1.1
28592  * Copyright(c) 2006-2007, Ext JS, LLC.
28593  *
28594  * Originally Released Under LGPL - original licence link has changed is not relivant.
28595  *
28596  * Fork - LGPL
28597  * <script type="text/javascript">
28598  */
28599 /**
28600  * @class Roo.BorderLayout
28601  * @extends Roo.LayoutManager
28602  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28603  * please see: <br><br>
28604  * <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>
28605  * <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>
28606  * Example:
28607  <pre><code>
28608  var layout = new Roo.BorderLayout(document.body, {
28609     north: {
28610         initialSize: 25,
28611         titlebar: false
28612     },
28613     west: {
28614         split:true,
28615         initialSize: 200,
28616         minSize: 175,
28617         maxSize: 400,
28618         titlebar: true,
28619         collapsible: true
28620     },
28621     east: {
28622         split:true,
28623         initialSize: 202,
28624         minSize: 175,
28625         maxSize: 400,
28626         titlebar: true,
28627         collapsible: true
28628     },
28629     south: {
28630         split:true,
28631         initialSize: 100,
28632         minSize: 100,
28633         maxSize: 200,
28634         titlebar: true,
28635         collapsible: true
28636     },
28637     center: {
28638         titlebar: true,
28639         autoScroll:true,
28640         resizeTabs: true,
28641         minTabWidth: 50,
28642         preferredTabWidth: 150
28643     }
28644 });
28645
28646 // shorthand
28647 var CP = Roo.ContentPanel;
28648
28649 layout.beginUpdate();
28650 layout.add("north", new CP("north", "North"));
28651 layout.add("south", new CP("south", {title: "South", closable: true}));
28652 layout.add("west", new CP("west", {title: "West"}));
28653 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28654 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28655 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28656 layout.getRegion("center").showPanel("center1");
28657 layout.endUpdate();
28658 </code></pre>
28659
28660 <b>The container the layout is rendered into can be either the body element or any other element.
28661 If it is not the body element, the container needs to either be an absolute positioned element,
28662 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28663 the container size if it is not the body element.</b>
28664
28665 * @constructor
28666 * Create a new BorderLayout
28667 * @param {String/HTMLElement/Element} container The container this layout is bound to
28668 * @param {Object} config Configuration options
28669  */
28670 Roo.BorderLayout = function(container, config){
28671     config = config || {};
28672     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28673     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28674     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28675         var target = this.factory.validRegions[i];
28676         if(config[target]){
28677             this.addRegion(target, config[target]);
28678         }
28679     }
28680 };
28681
28682 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28683     /**
28684      * Creates and adds a new region if it doesn't already exist.
28685      * @param {String} target The target region key (north, south, east, west or center).
28686      * @param {Object} config The regions config object
28687      * @return {BorderLayoutRegion} The new region
28688      */
28689     addRegion : function(target, config){
28690         if(!this.regions[target]){
28691             var r = this.factory.create(target, this, config);
28692             this.bindRegion(target, r);
28693         }
28694         return this.regions[target];
28695     },
28696
28697     // private (kinda)
28698     bindRegion : function(name, r){
28699         this.regions[name] = r;
28700         r.on("visibilitychange", this.layout, this);
28701         r.on("paneladded", this.layout, this);
28702         r.on("panelremoved", this.layout, this);
28703         r.on("invalidated", this.layout, this);
28704         r.on("resized", this.onRegionResized, this);
28705         r.on("collapsed", this.onRegionCollapsed, this);
28706         r.on("expanded", this.onRegionExpanded, this);
28707     },
28708
28709     /**
28710      * Performs a layout update.
28711      */
28712     layout : function(){
28713         if(this.updating) {
28714             return;
28715         }
28716         var size = this.getViewSize();
28717         var w = size.width;
28718         var h = size.height;
28719         var centerW = w;
28720         var centerH = h;
28721         var centerY = 0;
28722         var centerX = 0;
28723         //var x = 0, y = 0;
28724
28725         var rs = this.regions;
28726         var north = rs["north"];
28727         var south = rs["south"]; 
28728         var west = rs["west"];
28729         var east = rs["east"];
28730         var center = rs["center"];
28731         //if(this.hideOnLayout){ // not supported anymore
28732             //c.el.setStyle("display", "none");
28733         //}
28734         if(north && north.isVisible()){
28735             var b = north.getBox();
28736             var m = north.getMargins();
28737             b.width = w - (m.left+m.right);
28738             b.x = m.left;
28739             b.y = m.top;
28740             centerY = b.height + b.y + m.bottom;
28741             centerH -= centerY;
28742             north.updateBox(this.safeBox(b));
28743         }
28744         if(south && south.isVisible()){
28745             var b = south.getBox();
28746             var m = south.getMargins();
28747             b.width = w - (m.left+m.right);
28748             b.x = m.left;
28749             var totalHeight = (b.height + m.top + m.bottom);
28750             b.y = h - totalHeight + m.top;
28751             centerH -= totalHeight;
28752             south.updateBox(this.safeBox(b));
28753         }
28754         if(west && west.isVisible()){
28755             var b = west.getBox();
28756             var m = west.getMargins();
28757             b.height = centerH - (m.top+m.bottom);
28758             b.x = m.left;
28759             b.y = centerY + m.top;
28760             var totalWidth = (b.width + m.left + m.right);
28761             centerX += totalWidth;
28762             centerW -= totalWidth;
28763             west.updateBox(this.safeBox(b));
28764         }
28765         if(east && east.isVisible()){
28766             var b = east.getBox();
28767             var m = east.getMargins();
28768             b.height = centerH - (m.top+m.bottom);
28769             var totalWidth = (b.width + m.left + m.right);
28770             b.x = w - totalWidth + m.left;
28771             b.y = centerY + m.top;
28772             centerW -= totalWidth;
28773             east.updateBox(this.safeBox(b));
28774         }
28775         if(center){
28776             var m = center.getMargins();
28777             var centerBox = {
28778                 x: centerX + m.left,
28779                 y: centerY + m.top,
28780                 width: centerW - (m.left+m.right),
28781                 height: centerH - (m.top+m.bottom)
28782             };
28783             //if(this.hideOnLayout){
28784                 //center.el.setStyle("display", "block");
28785             //}
28786             center.updateBox(this.safeBox(centerBox));
28787         }
28788         this.el.repaint();
28789         this.fireEvent("layout", this);
28790     },
28791
28792     // private
28793     safeBox : function(box){
28794         box.width = Math.max(0, box.width);
28795         box.height = Math.max(0, box.height);
28796         return box;
28797     },
28798
28799     /**
28800      * Adds a ContentPanel (or subclass) to this layout.
28801      * @param {String} target The target region key (north, south, east, west or center).
28802      * @param {Roo.ContentPanel} panel The panel to add
28803      * @return {Roo.ContentPanel} The added panel
28804      */
28805     add : function(target, panel){
28806          
28807         target = target.toLowerCase();
28808         return this.regions[target].add(panel);
28809     },
28810
28811     /**
28812      * Remove a ContentPanel (or subclass) to this layout.
28813      * @param {String} target The target region key (north, south, east, west or center).
28814      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28815      * @return {Roo.ContentPanel} The removed panel
28816      */
28817     remove : function(target, panel){
28818         target = target.toLowerCase();
28819         return this.regions[target].remove(panel);
28820     },
28821
28822     /**
28823      * Searches all regions for a panel with the specified id
28824      * @param {String} panelId
28825      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28826      */
28827     findPanel : function(panelId){
28828         var rs = this.regions;
28829         for(var target in rs){
28830             if(typeof rs[target] != "function"){
28831                 var p = rs[target].getPanel(panelId);
28832                 if(p){
28833                     return p;
28834                 }
28835             }
28836         }
28837         return null;
28838     },
28839
28840     /**
28841      * Searches all regions for a panel with the specified id and activates (shows) it.
28842      * @param {String/ContentPanel} panelId The panels id or the panel itself
28843      * @return {Roo.ContentPanel} The shown panel or null
28844      */
28845     showPanel : function(panelId) {
28846       var rs = this.regions;
28847       for(var target in rs){
28848          var r = rs[target];
28849          if(typeof r != "function"){
28850             if(r.hasPanel(panelId)){
28851                return r.showPanel(panelId);
28852             }
28853          }
28854       }
28855       return null;
28856    },
28857
28858    /**
28859      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28860      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28861      */
28862     restoreState : function(provider){
28863         if(!provider){
28864             provider = Roo.state.Manager;
28865         }
28866         var sm = new Roo.LayoutStateManager();
28867         sm.init(this, provider);
28868     },
28869
28870     /**
28871      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28872      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28873      * a valid ContentPanel config object.  Example:
28874      * <pre><code>
28875 // Create the main layout
28876 var layout = new Roo.BorderLayout('main-ct', {
28877     west: {
28878         split:true,
28879         minSize: 175,
28880         titlebar: true
28881     },
28882     center: {
28883         title:'Components'
28884     }
28885 }, 'main-ct');
28886
28887 // Create and add multiple ContentPanels at once via configs
28888 layout.batchAdd({
28889    west: {
28890        id: 'source-files',
28891        autoCreate:true,
28892        title:'Ext Source Files',
28893        autoScroll:true,
28894        fitToFrame:true
28895    },
28896    center : {
28897        el: cview,
28898        autoScroll:true,
28899        fitToFrame:true,
28900        toolbar: tb,
28901        resizeEl:'cbody'
28902    }
28903 });
28904 </code></pre>
28905      * @param {Object} regions An object containing ContentPanel configs by region name
28906      */
28907     batchAdd : function(regions){
28908         this.beginUpdate();
28909         for(var rname in regions){
28910             var lr = this.regions[rname];
28911             if(lr){
28912                 this.addTypedPanels(lr, regions[rname]);
28913             }
28914         }
28915         this.endUpdate();
28916     },
28917
28918     // private
28919     addTypedPanels : function(lr, ps){
28920         if(typeof ps == 'string'){
28921             lr.add(new Roo.ContentPanel(ps));
28922         }
28923         else if(ps instanceof Array){
28924             for(var i =0, len = ps.length; i < len; i++){
28925                 this.addTypedPanels(lr, ps[i]);
28926             }
28927         }
28928         else if(!ps.events){ // raw config?
28929             var el = ps.el;
28930             delete ps.el; // prevent conflict
28931             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
28932         }
28933         else {  // panel object assumed!
28934             lr.add(ps);
28935         }
28936     },
28937     /**
28938      * Adds a xtype elements to the layout.
28939      * <pre><code>
28940
28941 layout.addxtype({
28942        xtype : 'ContentPanel',
28943        region: 'west',
28944        items: [ .... ]
28945    }
28946 );
28947
28948 layout.addxtype({
28949         xtype : 'NestedLayoutPanel',
28950         region: 'west',
28951         layout: {
28952            center: { },
28953            west: { }   
28954         },
28955         items : [ ... list of content panels or nested layout panels.. ]
28956    }
28957 );
28958 </code></pre>
28959      * @param {Object} cfg Xtype definition of item to add.
28960      */
28961     addxtype : function(cfg)
28962     {
28963         // basically accepts a pannel...
28964         // can accept a layout region..!?!?
28965         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
28966         
28967         if (!cfg.xtype.match(/Panel$/)) {
28968             return false;
28969         }
28970         var ret = false;
28971         
28972         if (typeof(cfg.region) == 'undefined') {
28973             Roo.log("Failed to add Panel, region was not set");
28974             Roo.log(cfg);
28975             return false;
28976         }
28977         var region = cfg.region;
28978         delete cfg.region;
28979         
28980           
28981         var xitems = [];
28982         if (cfg.items) {
28983             xitems = cfg.items;
28984             delete cfg.items;
28985         }
28986         var nb = false;
28987         
28988         switch(cfg.xtype) 
28989         {
28990             case 'ContentPanel':  // ContentPanel (el, cfg)
28991             case 'ScrollPanel':  // ContentPanel (el, cfg)
28992             case 'ViewPanel': 
28993                 if(cfg.autoCreate) {
28994                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28995                 } else {
28996                     var el = this.el.createChild();
28997                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
28998                 }
28999                 
29000                 this.add(region, ret);
29001                 break;
29002             
29003             
29004             case 'TreePanel': // our new panel!
29005                 cfg.el = this.el.createChild();
29006                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29007                 this.add(region, ret);
29008                 break;
29009             
29010             case 'NestedLayoutPanel': 
29011                 // create a new Layout (which is  a Border Layout...
29012                 var el = this.el.createChild();
29013                 var clayout = cfg.layout;
29014                 delete cfg.layout;
29015                 clayout.items   = clayout.items  || [];
29016                 // replace this exitems with the clayout ones..
29017                 xitems = clayout.items;
29018                  
29019                 
29020                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29021                     cfg.background = false;
29022                 }
29023                 var layout = new Roo.BorderLayout(el, clayout);
29024                 
29025                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29026                 //console.log('adding nested layout panel '  + cfg.toSource());
29027                 this.add(region, ret);
29028                 nb = {}; /// find first...
29029                 break;
29030                 
29031             case 'GridPanel': 
29032             
29033                 // needs grid and region
29034                 
29035                 //var el = this.getRegion(region).el.createChild();
29036                 var el = this.el.createChild();
29037                 // create the grid first...
29038                 
29039                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29040                 delete cfg.grid;
29041                 if (region == 'center' && this.active ) {
29042                     cfg.background = false;
29043                 }
29044                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29045                 
29046                 this.add(region, ret);
29047                 if (cfg.background) {
29048                     ret.on('activate', function(gp) {
29049                         if (!gp.grid.rendered) {
29050                             gp.grid.render();
29051                         }
29052                     });
29053                 } else {
29054                     grid.render();
29055                 }
29056                 break;
29057            
29058            
29059            
29060                 
29061                 
29062                 
29063             default:
29064                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29065                     
29066                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29067                     this.add(region, ret);
29068                 } else {
29069                 
29070                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29071                     return null;
29072                 }
29073                 
29074              // GridPanel (grid, cfg)
29075             
29076         }
29077         this.beginUpdate();
29078         // add children..
29079         var region = '';
29080         var abn = {};
29081         Roo.each(xitems, function(i)  {
29082             region = nb && i.region ? i.region : false;
29083             
29084             var add = ret.addxtype(i);
29085            
29086             if (region) {
29087                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29088                 if (!i.background) {
29089                     abn[region] = nb[region] ;
29090                 }
29091             }
29092             
29093         });
29094         this.endUpdate();
29095
29096         // make the last non-background panel active..
29097         //if (nb) { Roo.log(abn); }
29098         if (nb) {
29099             
29100             for(var r in abn) {
29101                 region = this.getRegion(r);
29102                 if (region) {
29103                     // tried using nb[r], but it does not work..
29104                      
29105                     region.showPanel(abn[r]);
29106                    
29107                 }
29108             }
29109         }
29110         return ret;
29111         
29112     }
29113 });
29114
29115 /**
29116  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29117  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29118  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29119  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29120  * <pre><code>
29121 // shorthand
29122 var CP = Roo.ContentPanel;
29123
29124 var layout = Roo.BorderLayout.create({
29125     north: {
29126         initialSize: 25,
29127         titlebar: false,
29128         panels: [new CP("north", "North")]
29129     },
29130     west: {
29131         split:true,
29132         initialSize: 200,
29133         minSize: 175,
29134         maxSize: 400,
29135         titlebar: true,
29136         collapsible: true,
29137         panels: [new CP("west", {title: "West"})]
29138     },
29139     east: {
29140         split:true,
29141         initialSize: 202,
29142         minSize: 175,
29143         maxSize: 400,
29144         titlebar: true,
29145         collapsible: true,
29146         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29147     },
29148     south: {
29149         split:true,
29150         initialSize: 100,
29151         minSize: 100,
29152         maxSize: 200,
29153         titlebar: true,
29154         collapsible: true,
29155         panels: [new CP("south", {title: "South", closable: true})]
29156     },
29157     center: {
29158         titlebar: true,
29159         autoScroll:true,
29160         resizeTabs: true,
29161         minTabWidth: 50,
29162         preferredTabWidth: 150,
29163         panels: [
29164             new CP("center1", {title: "Close Me", closable: true}),
29165             new CP("center2", {title: "Center Panel", closable: false})
29166         ]
29167     }
29168 }, document.body);
29169
29170 layout.getRegion("center").showPanel("center1");
29171 </code></pre>
29172  * @param config
29173  * @param targetEl
29174  */
29175 Roo.BorderLayout.create = function(config, targetEl){
29176     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29177     layout.beginUpdate();
29178     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29179     for(var j = 0, jlen = regions.length; j < jlen; j++){
29180         var lr = regions[j];
29181         if(layout.regions[lr] && config[lr].panels){
29182             var r = layout.regions[lr];
29183             var ps = config[lr].panels;
29184             layout.addTypedPanels(r, ps);
29185         }
29186     }
29187     layout.endUpdate();
29188     return layout;
29189 };
29190
29191 // private
29192 Roo.BorderLayout.RegionFactory = {
29193     // private
29194     validRegions : ["north","south","east","west","center"],
29195
29196     // private
29197     create : function(target, mgr, config){
29198         target = target.toLowerCase();
29199         if(config.lightweight || config.basic){
29200             return new Roo.BasicLayoutRegion(mgr, config, target);
29201         }
29202         switch(target){
29203             case "north":
29204                 return new Roo.NorthLayoutRegion(mgr, config);
29205             case "south":
29206                 return new Roo.SouthLayoutRegion(mgr, config);
29207             case "east":
29208                 return new Roo.EastLayoutRegion(mgr, config);
29209             case "west":
29210                 return new Roo.WestLayoutRegion(mgr, config);
29211             case "center":
29212                 return new Roo.CenterLayoutRegion(mgr, config);
29213         }
29214         throw 'Layout region "'+target+'" not supported.';
29215     }
29216 };/*
29217  * Based on:
29218  * Ext JS Library 1.1.1
29219  * Copyright(c) 2006-2007, Ext JS, LLC.
29220  *
29221  * Originally Released Under LGPL - original licence link has changed is not relivant.
29222  *
29223  * Fork - LGPL
29224  * <script type="text/javascript">
29225  */
29226  
29227 /**
29228  * @class Roo.BasicLayoutRegion
29229  * @extends Roo.util.Observable
29230  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29231  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29232  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29233  */
29234 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29235     this.mgr = mgr;
29236     this.position  = pos;
29237     this.events = {
29238         /**
29239          * @scope Roo.BasicLayoutRegion
29240          */
29241         
29242         /**
29243          * @event beforeremove
29244          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29245          * @param {Roo.LayoutRegion} this
29246          * @param {Roo.ContentPanel} panel The panel
29247          * @param {Object} e The cancel event object
29248          */
29249         "beforeremove" : true,
29250         /**
29251          * @event invalidated
29252          * Fires when the layout for this region is changed.
29253          * @param {Roo.LayoutRegion} this
29254          */
29255         "invalidated" : true,
29256         /**
29257          * @event visibilitychange
29258          * Fires when this region is shown or hidden 
29259          * @param {Roo.LayoutRegion} this
29260          * @param {Boolean} visibility true or false
29261          */
29262         "visibilitychange" : true,
29263         /**
29264          * @event paneladded
29265          * Fires when a panel is added. 
29266          * @param {Roo.LayoutRegion} this
29267          * @param {Roo.ContentPanel} panel The panel
29268          */
29269         "paneladded" : true,
29270         /**
29271          * @event panelremoved
29272          * Fires when a panel is removed. 
29273          * @param {Roo.LayoutRegion} this
29274          * @param {Roo.ContentPanel} panel The panel
29275          */
29276         "panelremoved" : true,
29277         /**
29278          * @event beforecollapse
29279          * Fires when this region before collapse.
29280          * @param {Roo.LayoutRegion} this
29281          */
29282         "beforecollapse" : true,
29283         /**
29284          * @event collapsed
29285          * Fires when this region is collapsed.
29286          * @param {Roo.LayoutRegion} this
29287          */
29288         "collapsed" : true,
29289         /**
29290          * @event expanded
29291          * Fires when this region is expanded.
29292          * @param {Roo.LayoutRegion} this
29293          */
29294         "expanded" : true,
29295         /**
29296          * @event slideshow
29297          * Fires when this region is slid into view.
29298          * @param {Roo.LayoutRegion} this
29299          */
29300         "slideshow" : true,
29301         /**
29302          * @event slidehide
29303          * Fires when this region slides out of view. 
29304          * @param {Roo.LayoutRegion} this
29305          */
29306         "slidehide" : true,
29307         /**
29308          * @event panelactivated
29309          * Fires when a panel is activated. 
29310          * @param {Roo.LayoutRegion} this
29311          * @param {Roo.ContentPanel} panel The activated panel
29312          */
29313         "panelactivated" : true,
29314         /**
29315          * @event resized
29316          * Fires when the user resizes this region. 
29317          * @param {Roo.LayoutRegion} this
29318          * @param {Number} newSize The new size (width for east/west, height for north/south)
29319          */
29320         "resized" : true
29321     };
29322     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29323     this.panels = new Roo.util.MixedCollection();
29324     this.panels.getKey = this.getPanelId.createDelegate(this);
29325     this.box = null;
29326     this.activePanel = null;
29327     // ensure listeners are added...
29328     
29329     if (config.listeners || config.events) {
29330         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29331             listeners : config.listeners || {},
29332             events : config.events || {}
29333         });
29334     }
29335     
29336     if(skipConfig !== true){
29337         this.applyConfig(config);
29338     }
29339 };
29340
29341 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29342     getPanelId : function(p){
29343         return p.getId();
29344     },
29345     
29346     applyConfig : function(config){
29347         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29348         this.config = config;
29349         
29350     },
29351     
29352     /**
29353      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29354      * the width, for horizontal (north, south) the height.
29355      * @param {Number} newSize The new width or height
29356      */
29357     resizeTo : function(newSize){
29358         var el = this.el ? this.el :
29359                  (this.activePanel ? this.activePanel.getEl() : null);
29360         if(el){
29361             switch(this.position){
29362                 case "east":
29363                 case "west":
29364                     el.setWidth(newSize);
29365                     this.fireEvent("resized", this, newSize);
29366                 break;
29367                 case "north":
29368                 case "south":
29369                     el.setHeight(newSize);
29370                     this.fireEvent("resized", this, newSize);
29371                 break;                
29372             }
29373         }
29374     },
29375     
29376     getBox : function(){
29377         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29378     },
29379     
29380     getMargins : function(){
29381         return this.margins;
29382     },
29383     
29384     updateBox : function(box){
29385         this.box = box;
29386         var el = this.activePanel.getEl();
29387         el.dom.style.left = box.x + "px";
29388         el.dom.style.top = box.y + "px";
29389         this.activePanel.setSize(box.width, box.height);
29390     },
29391     
29392     /**
29393      * Returns the container element for this region.
29394      * @return {Roo.Element}
29395      */
29396     getEl : function(){
29397         return this.activePanel;
29398     },
29399     
29400     /**
29401      * Returns true if this region is currently visible.
29402      * @return {Boolean}
29403      */
29404     isVisible : function(){
29405         return this.activePanel ? true : false;
29406     },
29407     
29408     setActivePanel : function(panel){
29409         panel = this.getPanel(panel);
29410         if(this.activePanel && this.activePanel != panel){
29411             this.activePanel.setActiveState(false);
29412             this.activePanel.getEl().setLeftTop(-10000,-10000);
29413         }
29414         this.activePanel = panel;
29415         panel.setActiveState(true);
29416         if(this.box){
29417             panel.setSize(this.box.width, this.box.height);
29418         }
29419         this.fireEvent("panelactivated", this, panel);
29420         this.fireEvent("invalidated");
29421     },
29422     
29423     /**
29424      * Show the specified panel.
29425      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29426      * @return {Roo.ContentPanel} The shown panel or null
29427      */
29428     showPanel : function(panel){
29429         if(panel = this.getPanel(panel)){
29430             this.setActivePanel(panel);
29431         }
29432         return panel;
29433     },
29434     
29435     /**
29436      * Get the active panel for this region.
29437      * @return {Roo.ContentPanel} The active panel or null
29438      */
29439     getActivePanel : function(){
29440         return this.activePanel;
29441     },
29442     
29443     /**
29444      * Add the passed ContentPanel(s)
29445      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29446      * @return {Roo.ContentPanel} The panel added (if only one was added)
29447      */
29448     add : function(panel){
29449         if(arguments.length > 1){
29450             for(var i = 0, len = arguments.length; i < len; i++) {
29451                 this.add(arguments[i]);
29452             }
29453             return null;
29454         }
29455         if(this.hasPanel(panel)){
29456             this.showPanel(panel);
29457             return panel;
29458         }
29459         var el = panel.getEl();
29460         if(el.dom.parentNode != this.mgr.el.dom){
29461             this.mgr.el.dom.appendChild(el.dom);
29462         }
29463         if(panel.setRegion){
29464             panel.setRegion(this);
29465         }
29466         this.panels.add(panel);
29467         el.setStyle("position", "absolute");
29468         if(!panel.background){
29469             this.setActivePanel(panel);
29470             if(this.config.initialSize && this.panels.getCount()==1){
29471                 this.resizeTo(this.config.initialSize);
29472             }
29473         }
29474         this.fireEvent("paneladded", this, panel);
29475         return panel;
29476     },
29477     
29478     /**
29479      * Returns true if the panel is in this region.
29480      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29481      * @return {Boolean}
29482      */
29483     hasPanel : function(panel){
29484         if(typeof panel == "object"){ // must be panel obj
29485             panel = panel.getId();
29486         }
29487         return this.getPanel(panel) ? true : false;
29488     },
29489     
29490     /**
29491      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29492      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29493      * @param {Boolean} preservePanel Overrides the config preservePanel option
29494      * @return {Roo.ContentPanel} The panel that was removed
29495      */
29496     remove : function(panel, preservePanel){
29497         panel = this.getPanel(panel);
29498         if(!panel){
29499             return null;
29500         }
29501         var e = {};
29502         this.fireEvent("beforeremove", this, panel, e);
29503         if(e.cancel === true){
29504             return null;
29505         }
29506         var panelId = panel.getId();
29507         this.panels.removeKey(panelId);
29508         return panel;
29509     },
29510     
29511     /**
29512      * Returns the panel specified or null if it's not in this region.
29513      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29514      * @return {Roo.ContentPanel}
29515      */
29516     getPanel : function(id){
29517         if(typeof id == "object"){ // must be panel obj
29518             return id;
29519         }
29520         return this.panels.get(id);
29521     },
29522     
29523     /**
29524      * Returns this regions position (north/south/east/west/center).
29525      * @return {String} 
29526      */
29527     getPosition: function(){
29528         return this.position;    
29529     }
29530 });/*
29531  * Based on:
29532  * Ext JS Library 1.1.1
29533  * Copyright(c) 2006-2007, Ext JS, LLC.
29534  *
29535  * Originally Released Under LGPL - original licence link has changed is not relivant.
29536  *
29537  * Fork - LGPL
29538  * <script type="text/javascript">
29539  */
29540  
29541 /**
29542  * @class Roo.LayoutRegion
29543  * @extends Roo.BasicLayoutRegion
29544  * This class represents a region in a layout manager.
29545  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29546  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29547  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29548  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29549  * @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})
29550  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29551  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29552  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29553  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29554  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29555  * @cfg {String}    title           The title for the region (overrides panel titles)
29556  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29557  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29558  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29559  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29560  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29561  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29562  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29563  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29564  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29565  * @cfg {Boolean}   showPin         True to show a pin button
29566  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29567  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29568  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29569  * @cfg {Number}    width           For East/West panels
29570  * @cfg {Number}    height          For North/South panels
29571  * @cfg {Boolean}   split           To show the splitter
29572  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29573  */
29574 Roo.LayoutRegion = function(mgr, config, pos){
29575     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29576     var dh = Roo.DomHelper;
29577     /** This region's container element 
29578     * @type Roo.Element */
29579     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29580     /** This region's title element 
29581     * @type Roo.Element */
29582
29583     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29584         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29585         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29586     ]}, true);
29587     this.titleEl.enableDisplayMode();
29588     /** This region's title text element 
29589     * @type HTMLElement */
29590     this.titleTextEl = this.titleEl.dom.firstChild;
29591     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29592     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29593     this.closeBtn.enableDisplayMode();
29594     this.closeBtn.on("click", this.closeClicked, this);
29595     this.closeBtn.hide();
29596
29597     this.createBody(config);
29598     this.visible = true;
29599     this.collapsed = false;
29600
29601     if(config.hideWhenEmpty){
29602         this.hide();
29603         this.on("paneladded", this.validateVisibility, this);
29604         this.on("panelremoved", this.validateVisibility, this);
29605     }
29606     this.applyConfig(config);
29607 };
29608
29609 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29610
29611     createBody : function(){
29612         /** This region's body element 
29613         * @type Roo.Element */
29614         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29615     },
29616
29617     applyConfig : function(c){
29618         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29619             var dh = Roo.DomHelper;
29620             if(c.titlebar !== false){
29621                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29622                 this.collapseBtn.on("click", this.collapse, this);
29623                 this.collapseBtn.enableDisplayMode();
29624
29625                 if(c.showPin === true || this.showPin){
29626                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29627                     this.stickBtn.enableDisplayMode();
29628                     this.stickBtn.on("click", this.expand, this);
29629                     this.stickBtn.hide();
29630                 }
29631             }
29632             /** This region's collapsed element
29633             * @type Roo.Element */
29634             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29635                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29636             ]}, true);
29637             if(c.floatable !== false){
29638                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29639                this.collapsedEl.on("click", this.collapseClick, this);
29640             }
29641
29642             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29643                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29644                    id: "message", unselectable: "on", style:{"float":"left"}});
29645                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29646              }
29647             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29648             this.expandBtn.on("click", this.expand, this);
29649         }
29650         if(this.collapseBtn){
29651             this.collapseBtn.setVisible(c.collapsible == true);
29652         }
29653         this.cmargins = c.cmargins || this.cmargins ||
29654                          (this.position == "west" || this.position == "east" ?
29655                              {top: 0, left: 2, right:2, bottom: 0} :
29656                              {top: 2, left: 0, right:0, bottom: 2});
29657         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29658         this.bottomTabs = c.tabPosition != "top";
29659         this.autoScroll = c.autoScroll || false;
29660         if(this.autoScroll){
29661             this.bodyEl.setStyle("overflow", "auto");
29662         }else{
29663             this.bodyEl.setStyle("overflow", "hidden");
29664         }
29665         //if(c.titlebar !== false){
29666             if((!c.titlebar && !c.title) || c.titlebar === false){
29667                 this.titleEl.hide();
29668             }else{
29669                 this.titleEl.show();
29670                 if(c.title){
29671                     this.titleTextEl.innerHTML = c.title;
29672                 }
29673             }
29674         //}
29675         this.duration = c.duration || .30;
29676         this.slideDuration = c.slideDuration || .45;
29677         this.config = c;
29678         if(c.collapsed){
29679             this.collapse(true);
29680         }
29681         if(c.hidden){
29682             this.hide();
29683         }
29684     },
29685     /**
29686      * Returns true if this region is currently visible.
29687      * @return {Boolean}
29688      */
29689     isVisible : function(){
29690         return this.visible;
29691     },
29692
29693     /**
29694      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29695      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29696      */
29697     setCollapsedTitle : function(title){
29698         title = title || "&#160;";
29699         if(this.collapsedTitleTextEl){
29700             this.collapsedTitleTextEl.innerHTML = title;
29701         }
29702     },
29703
29704     getBox : function(){
29705         var b;
29706         if(!this.collapsed){
29707             b = this.el.getBox(false, true);
29708         }else{
29709             b = this.collapsedEl.getBox(false, true);
29710         }
29711         return b;
29712     },
29713
29714     getMargins : function(){
29715         return this.collapsed ? this.cmargins : this.margins;
29716     },
29717
29718     highlight : function(){
29719         this.el.addClass("x-layout-panel-dragover");
29720     },
29721
29722     unhighlight : function(){
29723         this.el.removeClass("x-layout-panel-dragover");
29724     },
29725
29726     updateBox : function(box){
29727         this.box = box;
29728         if(!this.collapsed){
29729             this.el.dom.style.left = box.x + "px";
29730             this.el.dom.style.top = box.y + "px";
29731             this.updateBody(box.width, box.height);
29732         }else{
29733             this.collapsedEl.dom.style.left = box.x + "px";
29734             this.collapsedEl.dom.style.top = box.y + "px";
29735             this.collapsedEl.setSize(box.width, box.height);
29736         }
29737         if(this.tabs){
29738             this.tabs.autoSizeTabs();
29739         }
29740     },
29741
29742     updateBody : function(w, h){
29743         if(w !== null){
29744             this.el.setWidth(w);
29745             w -= this.el.getBorderWidth("rl");
29746             if(this.config.adjustments){
29747                 w += this.config.adjustments[0];
29748             }
29749         }
29750         if(h !== null){
29751             this.el.setHeight(h);
29752             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29753             h -= this.el.getBorderWidth("tb");
29754             if(this.config.adjustments){
29755                 h += this.config.adjustments[1];
29756             }
29757             this.bodyEl.setHeight(h);
29758             if(this.tabs){
29759                 h = this.tabs.syncHeight(h);
29760             }
29761         }
29762         if(this.panelSize){
29763             w = w !== null ? w : this.panelSize.width;
29764             h = h !== null ? h : this.panelSize.height;
29765         }
29766         if(this.activePanel){
29767             var el = this.activePanel.getEl();
29768             w = w !== null ? w : el.getWidth();
29769             h = h !== null ? h : el.getHeight();
29770             this.panelSize = {width: w, height: h};
29771             this.activePanel.setSize(w, h);
29772         }
29773         if(Roo.isIE && this.tabs){
29774             this.tabs.el.repaint();
29775         }
29776     },
29777
29778     /**
29779      * Returns the container element for this region.
29780      * @return {Roo.Element}
29781      */
29782     getEl : function(){
29783         return this.el;
29784     },
29785
29786     /**
29787      * Hides this region.
29788      */
29789     hide : function(){
29790         if(!this.collapsed){
29791             this.el.dom.style.left = "-2000px";
29792             this.el.hide();
29793         }else{
29794             this.collapsedEl.dom.style.left = "-2000px";
29795             this.collapsedEl.hide();
29796         }
29797         this.visible = false;
29798         this.fireEvent("visibilitychange", this, false);
29799     },
29800
29801     /**
29802      * Shows this region if it was previously hidden.
29803      */
29804     show : function(){
29805         if(!this.collapsed){
29806             this.el.show();
29807         }else{
29808             this.collapsedEl.show();
29809         }
29810         this.visible = true;
29811         this.fireEvent("visibilitychange", this, true);
29812     },
29813
29814     closeClicked : function(){
29815         if(this.activePanel){
29816             this.remove(this.activePanel);
29817         }
29818     },
29819
29820     collapseClick : function(e){
29821         if(this.isSlid){
29822            e.stopPropagation();
29823            this.slideIn();
29824         }else{
29825            e.stopPropagation();
29826            this.slideOut();
29827         }
29828     },
29829
29830     /**
29831      * Collapses this region.
29832      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29833      */
29834     collapse : function(skipAnim, skipCheck = false){
29835         if(this.collapsed) {
29836             return;
29837         }
29838         
29839         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
29840             
29841             this.collapsed = true;
29842             if(this.split){
29843                 this.split.el.hide();
29844             }
29845             if(this.config.animate && skipAnim !== true){
29846                 this.fireEvent("invalidated", this);
29847                 this.animateCollapse();
29848             }else{
29849                 this.el.setLocation(-20000,-20000);
29850                 this.el.hide();
29851                 this.collapsedEl.show();
29852                 this.fireEvent("collapsed", this);
29853                 this.fireEvent("invalidated", this);
29854             }
29855         }
29856         
29857     },
29858
29859     animateCollapse : function(){
29860         // overridden
29861     },
29862
29863     /**
29864      * Expands this region if it was previously collapsed.
29865      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29866      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29867      */
29868     expand : function(e, skipAnim){
29869         if(e) {
29870             e.stopPropagation();
29871         }
29872         if(!this.collapsed || this.el.hasActiveFx()) {
29873             return;
29874         }
29875         if(this.isSlid){
29876             this.afterSlideIn();
29877             skipAnim = true;
29878         }
29879         this.collapsed = false;
29880         if(this.config.animate && skipAnim !== true){
29881             this.animateExpand();
29882         }else{
29883             this.el.show();
29884             if(this.split){
29885                 this.split.el.show();
29886             }
29887             this.collapsedEl.setLocation(-2000,-2000);
29888             this.collapsedEl.hide();
29889             this.fireEvent("invalidated", this);
29890             this.fireEvent("expanded", this);
29891         }
29892     },
29893
29894     animateExpand : function(){
29895         // overridden
29896     },
29897
29898     initTabs : function()
29899     {
29900         this.bodyEl.setStyle("overflow", "hidden");
29901         var ts = new Roo.TabPanel(
29902                 this.bodyEl.dom,
29903                 {
29904                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
29905                     disableTooltips: this.config.disableTabTips,
29906                     toolbar : this.config.toolbar
29907                 }
29908         );
29909         if(this.config.hideTabs){
29910             ts.stripWrap.setDisplayed(false);
29911         }
29912         this.tabs = ts;
29913         ts.resizeTabs = this.config.resizeTabs === true;
29914         ts.minTabWidth = this.config.minTabWidth || 40;
29915         ts.maxTabWidth = this.config.maxTabWidth || 250;
29916         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29917         ts.monitorResize = false;
29918         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29919         ts.bodyEl.addClass('x-layout-tabs-body');
29920         this.panels.each(this.initPanelAsTab, this);
29921     },
29922
29923     initPanelAsTab : function(panel){
29924         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29925                     this.config.closeOnTab && panel.isClosable());
29926         if(panel.tabTip !== undefined){
29927             ti.setTooltip(panel.tabTip);
29928         }
29929         ti.on("activate", function(){
29930               this.setActivePanel(panel);
29931         }, this);
29932         if(this.config.closeOnTab){
29933             ti.on("beforeclose", function(t, e){
29934                 e.cancel = true;
29935                 this.remove(panel);
29936             }, this);
29937         }
29938         return ti;
29939     },
29940
29941     updatePanelTitle : function(panel, title){
29942         if(this.activePanel == panel){
29943             this.updateTitle(title);
29944         }
29945         if(this.tabs){
29946             var ti = this.tabs.getTab(panel.getEl().id);
29947             ti.setText(title);
29948             if(panel.tabTip !== undefined){
29949                 ti.setTooltip(panel.tabTip);
29950             }
29951         }
29952     },
29953
29954     updateTitle : function(title){
29955         if(this.titleTextEl && !this.config.title){
29956             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
29957         }
29958     },
29959
29960     setActivePanel : function(panel){
29961         panel = this.getPanel(panel);
29962         if(this.activePanel && this.activePanel != panel){
29963             this.activePanel.setActiveState(false);
29964         }
29965         this.activePanel = panel;
29966         panel.setActiveState(true);
29967         if(this.panelSize){
29968             panel.setSize(this.panelSize.width, this.panelSize.height);
29969         }
29970         if(this.closeBtn){
29971             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
29972         }
29973         this.updateTitle(panel.getTitle());
29974         if(this.tabs){
29975             this.fireEvent("invalidated", this);
29976         }
29977         this.fireEvent("panelactivated", this, panel);
29978     },
29979
29980     /**
29981      * Shows the specified panel.
29982      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
29983      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
29984      */
29985     showPanel : function(panel)
29986     {
29987         panel = this.getPanel(panel);
29988         if(panel){
29989             if(this.tabs){
29990                 var tab = this.tabs.getTab(panel.getEl().id);
29991                 if(tab.isHidden()){
29992                     this.tabs.unhideTab(tab.id);
29993                 }
29994                 tab.activate();
29995             }else{
29996                 this.setActivePanel(panel);
29997             }
29998         }
29999         return panel;
30000     },
30001
30002     /**
30003      * Get the active panel for this region.
30004      * @return {Roo.ContentPanel} The active panel or null
30005      */
30006     getActivePanel : function(){
30007         return this.activePanel;
30008     },
30009
30010     validateVisibility : function(){
30011         if(this.panels.getCount() < 1){
30012             this.updateTitle("&#160;");
30013             this.closeBtn.hide();
30014             this.hide();
30015         }else{
30016             if(!this.isVisible()){
30017                 this.show();
30018             }
30019         }
30020     },
30021
30022     /**
30023      * Adds the passed ContentPanel(s) to this region.
30024      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30025      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30026      */
30027     add : function(panel){
30028         if(arguments.length > 1){
30029             for(var i = 0, len = arguments.length; i < len; i++) {
30030                 this.add(arguments[i]);
30031             }
30032             return null;
30033         }
30034         if(this.hasPanel(panel)){
30035             this.showPanel(panel);
30036             return panel;
30037         }
30038         panel.setRegion(this);
30039         this.panels.add(panel);
30040         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30041             this.bodyEl.dom.appendChild(panel.getEl().dom);
30042             if(panel.background !== true){
30043                 this.setActivePanel(panel);
30044             }
30045             this.fireEvent("paneladded", this, panel);
30046             return panel;
30047         }
30048         if(!this.tabs){
30049             this.initTabs();
30050         }else{
30051             this.initPanelAsTab(panel);
30052         }
30053         if(panel.background !== true){
30054             this.tabs.activate(panel.getEl().id);
30055         }
30056         this.fireEvent("paneladded", this, panel);
30057         return panel;
30058     },
30059
30060     /**
30061      * Hides the tab for the specified panel.
30062      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30063      */
30064     hidePanel : function(panel){
30065         if(this.tabs && (panel = this.getPanel(panel))){
30066             this.tabs.hideTab(panel.getEl().id);
30067         }
30068     },
30069
30070     /**
30071      * Unhides the tab for a previously hidden panel.
30072      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30073      */
30074     unhidePanel : function(panel){
30075         if(this.tabs && (panel = this.getPanel(panel))){
30076             this.tabs.unhideTab(panel.getEl().id);
30077         }
30078     },
30079
30080     clearPanels : function(){
30081         while(this.panels.getCount() > 0){
30082              this.remove(this.panels.first());
30083         }
30084     },
30085
30086     /**
30087      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30088      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30089      * @param {Boolean} preservePanel Overrides the config preservePanel option
30090      * @return {Roo.ContentPanel} The panel that was removed
30091      */
30092     remove : function(panel, preservePanel){
30093         panel = this.getPanel(panel);
30094         if(!panel){
30095             return null;
30096         }
30097         var e = {};
30098         this.fireEvent("beforeremove", this, panel, e);
30099         if(e.cancel === true){
30100             return null;
30101         }
30102         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30103         var panelId = panel.getId();
30104         this.panels.removeKey(panelId);
30105         if(preservePanel){
30106             document.body.appendChild(panel.getEl().dom);
30107         }
30108         if(this.tabs){
30109             this.tabs.removeTab(panel.getEl().id);
30110         }else if (!preservePanel){
30111             this.bodyEl.dom.removeChild(panel.getEl().dom);
30112         }
30113         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30114             var p = this.panels.first();
30115             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30116             tempEl.appendChild(p.getEl().dom);
30117             this.bodyEl.update("");
30118             this.bodyEl.dom.appendChild(p.getEl().dom);
30119             tempEl = null;
30120             this.updateTitle(p.getTitle());
30121             this.tabs = null;
30122             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30123             this.setActivePanel(p);
30124         }
30125         panel.setRegion(null);
30126         if(this.activePanel == panel){
30127             this.activePanel = null;
30128         }
30129         if(this.config.autoDestroy !== false && preservePanel !== true){
30130             try{panel.destroy();}catch(e){}
30131         }
30132         this.fireEvent("panelremoved", this, panel);
30133         return panel;
30134     },
30135
30136     /**
30137      * Returns the TabPanel component used by this region
30138      * @return {Roo.TabPanel}
30139      */
30140     getTabs : function(){
30141         return this.tabs;
30142     },
30143
30144     createTool : function(parentEl, className){
30145         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30146             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30147         btn.addClassOnOver("x-layout-tools-button-over");
30148         return btn;
30149     }
30150 });/*
30151  * Based on:
30152  * Ext JS Library 1.1.1
30153  * Copyright(c) 2006-2007, Ext JS, LLC.
30154  *
30155  * Originally Released Under LGPL - original licence link has changed is not relivant.
30156  *
30157  * Fork - LGPL
30158  * <script type="text/javascript">
30159  */
30160  
30161
30162
30163 /**
30164  * @class Roo.SplitLayoutRegion
30165  * @extends Roo.LayoutRegion
30166  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30167  */
30168 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30169     this.cursor = cursor;
30170     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30171 };
30172
30173 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30174     splitTip : "Drag to resize.",
30175     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30176     useSplitTips : false,
30177
30178     applyConfig : function(config){
30179         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30180         if(config.split){
30181             if(!this.split){
30182                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30183                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30184                 /** The SplitBar for this region 
30185                 * @type Roo.SplitBar */
30186                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30187                 this.split.on("moved", this.onSplitMove, this);
30188                 this.split.useShim = config.useShim === true;
30189                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30190                 if(this.useSplitTips){
30191                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30192                 }
30193                 if(config.collapsible){
30194                     this.split.el.on("dblclick", this.collapse,  this);
30195                 }
30196             }
30197             if(typeof config.minSize != "undefined"){
30198                 this.split.minSize = config.minSize;
30199             }
30200             if(typeof config.maxSize != "undefined"){
30201                 this.split.maxSize = config.maxSize;
30202             }
30203             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30204                 this.hideSplitter();
30205             }
30206         }
30207     },
30208
30209     getHMaxSize : function(){
30210          var cmax = this.config.maxSize || 10000;
30211          var center = this.mgr.getRegion("center");
30212          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30213     },
30214
30215     getVMaxSize : function(){
30216          var cmax = this.config.maxSize || 10000;
30217          var center = this.mgr.getRegion("center");
30218          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30219     },
30220
30221     onSplitMove : function(split, newSize){
30222         this.fireEvent("resized", this, newSize);
30223     },
30224     
30225     /** 
30226      * Returns the {@link Roo.SplitBar} for this region.
30227      * @return {Roo.SplitBar}
30228      */
30229     getSplitBar : function(){
30230         return this.split;
30231     },
30232     
30233     hide : function(){
30234         this.hideSplitter();
30235         Roo.SplitLayoutRegion.superclass.hide.call(this);
30236     },
30237
30238     hideSplitter : function(){
30239         if(this.split){
30240             this.split.el.setLocation(-2000,-2000);
30241             this.split.el.hide();
30242         }
30243     },
30244
30245     show : function(){
30246         if(this.split){
30247             this.split.el.show();
30248         }
30249         Roo.SplitLayoutRegion.superclass.show.call(this);
30250     },
30251     
30252     beforeSlide: function(){
30253         if(Roo.isGecko){// firefox overflow auto bug workaround
30254             this.bodyEl.clip();
30255             if(this.tabs) {
30256                 this.tabs.bodyEl.clip();
30257             }
30258             if(this.activePanel){
30259                 this.activePanel.getEl().clip();
30260                 
30261                 if(this.activePanel.beforeSlide){
30262                     this.activePanel.beforeSlide();
30263                 }
30264             }
30265         }
30266     },
30267     
30268     afterSlide : function(){
30269         if(Roo.isGecko){// firefox overflow auto bug workaround
30270             this.bodyEl.unclip();
30271             if(this.tabs) {
30272                 this.tabs.bodyEl.unclip();
30273             }
30274             if(this.activePanel){
30275                 this.activePanel.getEl().unclip();
30276                 if(this.activePanel.afterSlide){
30277                     this.activePanel.afterSlide();
30278                 }
30279             }
30280         }
30281     },
30282
30283     initAutoHide : function(){
30284         if(this.autoHide !== false){
30285             if(!this.autoHideHd){
30286                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30287                 this.autoHideHd = {
30288                     "mouseout": function(e){
30289                         if(!e.within(this.el, true)){
30290                             st.delay(500);
30291                         }
30292                     },
30293                     "mouseover" : function(e){
30294                         st.cancel();
30295                     },
30296                     scope : this
30297                 };
30298             }
30299             this.el.on(this.autoHideHd);
30300         }
30301     },
30302
30303     clearAutoHide : function(){
30304         if(this.autoHide !== false){
30305             this.el.un("mouseout", this.autoHideHd.mouseout);
30306             this.el.un("mouseover", this.autoHideHd.mouseover);
30307         }
30308     },
30309
30310     clearMonitor : function(){
30311         Roo.get(document).un("click", this.slideInIf, this);
30312     },
30313
30314     // these names are backwards but not changed for compat
30315     slideOut : function(){
30316         if(this.isSlid || this.el.hasActiveFx()){
30317             return;
30318         }
30319         this.isSlid = true;
30320         if(this.collapseBtn){
30321             this.collapseBtn.hide();
30322         }
30323         this.closeBtnState = this.closeBtn.getStyle('display');
30324         this.closeBtn.hide();
30325         if(this.stickBtn){
30326             this.stickBtn.show();
30327         }
30328         this.el.show();
30329         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30330         this.beforeSlide();
30331         this.el.setStyle("z-index", 10001);
30332         this.el.slideIn(this.getSlideAnchor(), {
30333             callback: function(){
30334                 this.afterSlide();
30335                 this.initAutoHide();
30336                 Roo.get(document).on("click", this.slideInIf, this);
30337                 this.fireEvent("slideshow", this);
30338             },
30339             scope: this,
30340             block: true
30341         });
30342     },
30343
30344     afterSlideIn : function(){
30345         this.clearAutoHide();
30346         this.isSlid = false;
30347         this.clearMonitor();
30348         this.el.setStyle("z-index", "");
30349         if(this.collapseBtn){
30350             this.collapseBtn.show();
30351         }
30352         this.closeBtn.setStyle('display', this.closeBtnState);
30353         if(this.stickBtn){
30354             this.stickBtn.hide();
30355         }
30356         this.fireEvent("slidehide", this);
30357     },
30358
30359     slideIn : function(cb){
30360         if(!this.isSlid || this.el.hasActiveFx()){
30361             Roo.callback(cb);
30362             return;
30363         }
30364         this.isSlid = false;
30365         this.beforeSlide();
30366         this.el.slideOut(this.getSlideAnchor(), {
30367             callback: function(){
30368                 this.el.setLeftTop(-10000, -10000);
30369                 this.afterSlide();
30370                 this.afterSlideIn();
30371                 Roo.callback(cb);
30372             },
30373             scope: this,
30374             block: true
30375         });
30376     },
30377     
30378     slideInIf : function(e){
30379         if(!e.within(this.el)){
30380             this.slideIn();
30381         }
30382     },
30383
30384     animateCollapse : function(){
30385         this.beforeSlide();
30386         this.el.setStyle("z-index", 20000);
30387         var anchor = this.getSlideAnchor();
30388         this.el.slideOut(anchor, {
30389             callback : function(){
30390                 this.el.setStyle("z-index", "");
30391                 this.collapsedEl.slideIn(anchor, {duration:.3});
30392                 this.afterSlide();
30393                 this.el.setLocation(-10000,-10000);
30394                 this.el.hide();
30395                 this.fireEvent("collapsed", this);
30396             },
30397             scope: this,
30398             block: true
30399         });
30400     },
30401
30402     animateExpand : function(){
30403         this.beforeSlide();
30404         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30405         this.el.setStyle("z-index", 20000);
30406         this.collapsedEl.hide({
30407             duration:.1
30408         });
30409         this.el.slideIn(this.getSlideAnchor(), {
30410             callback : function(){
30411                 this.el.setStyle("z-index", "");
30412                 this.afterSlide();
30413                 if(this.split){
30414                     this.split.el.show();
30415                 }
30416                 this.fireEvent("invalidated", this);
30417                 this.fireEvent("expanded", this);
30418             },
30419             scope: this,
30420             block: true
30421         });
30422     },
30423
30424     anchors : {
30425         "west" : "left",
30426         "east" : "right",
30427         "north" : "top",
30428         "south" : "bottom"
30429     },
30430
30431     sanchors : {
30432         "west" : "l",
30433         "east" : "r",
30434         "north" : "t",
30435         "south" : "b"
30436     },
30437
30438     canchors : {
30439         "west" : "tl-tr",
30440         "east" : "tr-tl",
30441         "north" : "tl-bl",
30442         "south" : "bl-tl"
30443     },
30444
30445     getAnchor : function(){
30446         return this.anchors[this.position];
30447     },
30448
30449     getCollapseAnchor : function(){
30450         return this.canchors[this.position];
30451     },
30452
30453     getSlideAnchor : function(){
30454         return this.sanchors[this.position];
30455     },
30456
30457     getAlignAdj : function(){
30458         var cm = this.cmargins;
30459         switch(this.position){
30460             case "west":
30461                 return [0, 0];
30462             break;
30463             case "east":
30464                 return [0, 0];
30465             break;
30466             case "north":
30467                 return [0, 0];
30468             break;
30469             case "south":
30470                 return [0, 0];
30471             break;
30472         }
30473     },
30474
30475     getExpandAdj : function(){
30476         var c = this.collapsedEl, cm = this.cmargins;
30477         switch(this.position){
30478             case "west":
30479                 return [-(cm.right+c.getWidth()+cm.left), 0];
30480             break;
30481             case "east":
30482                 return [cm.right+c.getWidth()+cm.left, 0];
30483             break;
30484             case "north":
30485                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30486             break;
30487             case "south":
30488                 return [0, cm.top+cm.bottom+c.getHeight()];
30489             break;
30490         }
30491     }
30492 });/*
30493  * Based on:
30494  * Ext JS Library 1.1.1
30495  * Copyright(c) 2006-2007, Ext JS, LLC.
30496  *
30497  * Originally Released Under LGPL - original licence link has changed is not relivant.
30498  *
30499  * Fork - LGPL
30500  * <script type="text/javascript">
30501  */
30502 /*
30503  * These classes are private internal classes
30504  */
30505 Roo.CenterLayoutRegion = function(mgr, config){
30506     Roo.LayoutRegion.call(this, mgr, config, "center");
30507     this.visible = true;
30508     this.minWidth = config.minWidth || 20;
30509     this.minHeight = config.minHeight || 20;
30510 };
30511
30512 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30513     hide : function(){
30514         // center panel can't be hidden
30515     },
30516     
30517     show : function(){
30518         // center panel can't be hidden
30519     },
30520     
30521     getMinWidth: function(){
30522         return this.minWidth;
30523     },
30524     
30525     getMinHeight: function(){
30526         return this.minHeight;
30527     }
30528 });
30529
30530
30531 Roo.NorthLayoutRegion = function(mgr, config){
30532     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30533     if(this.split){
30534         this.split.placement = Roo.SplitBar.TOP;
30535         this.split.orientation = Roo.SplitBar.VERTICAL;
30536         this.split.el.addClass("x-layout-split-v");
30537     }
30538     var size = config.initialSize || config.height;
30539     if(typeof size != "undefined"){
30540         this.el.setHeight(size);
30541     }
30542 };
30543 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30544     orientation: Roo.SplitBar.VERTICAL,
30545     getBox : function(){
30546         if(this.collapsed){
30547             return this.collapsedEl.getBox();
30548         }
30549         var box = this.el.getBox();
30550         if(this.split){
30551             box.height += this.split.el.getHeight();
30552         }
30553         return box;
30554     },
30555     
30556     updateBox : function(box){
30557         if(this.split && !this.collapsed){
30558             box.height -= this.split.el.getHeight();
30559             this.split.el.setLeft(box.x);
30560             this.split.el.setTop(box.y+box.height);
30561             this.split.el.setWidth(box.width);
30562         }
30563         if(this.collapsed){
30564             this.updateBody(box.width, null);
30565         }
30566         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30567     }
30568 });
30569
30570 Roo.SouthLayoutRegion = function(mgr, config){
30571     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30572     if(this.split){
30573         this.split.placement = Roo.SplitBar.BOTTOM;
30574         this.split.orientation = Roo.SplitBar.VERTICAL;
30575         this.split.el.addClass("x-layout-split-v");
30576     }
30577     var size = config.initialSize || config.height;
30578     if(typeof size != "undefined"){
30579         this.el.setHeight(size);
30580     }
30581 };
30582 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30583     orientation: Roo.SplitBar.VERTICAL,
30584     getBox : function(){
30585         if(this.collapsed){
30586             return this.collapsedEl.getBox();
30587         }
30588         var box = this.el.getBox();
30589         if(this.split){
30590             var sh = this.split.el.getHeight();
30591             box.height += sh;
30592             box.y -= sh;
30593         }
30594         return box;
30595     },
30596     
30597     updateBox : function(box){
30598         if(this.split && !this.collapsed){
30599             var sh = this.split.el.getHeight();
30600             box.height -= sh;
30601             box.y += sh;
30602             this.split.el.setLeft(box.x);
30603             this.split.el.setTop(box.y-sh);
30604             this.split.el.setWidth(box.width);
30605         }
30606         if(this.collapsed){
30607             this.updateBody(box.width, null);
30608         }
30609         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30610     }
30611 });
30612
30613 Roo.EastLayoutRegion = function(mgr, config){
30614     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30615     if(this.split){
30616         this.split.placement = Roo.SplitBar.RIGHT;
30617         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30618         this.split.el.addClass("x-layout-split-h");
30619     }
30620     var size = config.initialSize || config.width;
30621     if(typeof size != "undefined"){
30622         this.el.setWidth(size);
30623     }
30624 };
30625 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30626     orientation: Roo.SplitBar.HORIZONTAL,
30627     getBox : function(){
30628         if(this.collapsed){
30629             return this.collapsedEl.getBox();
30630         }
30631         var box = this.el.getBox();
30632         if(this.split){
30633             var sw = this.split.el.getWidth();
30634             box.width += sw;
30635             box.x -= sw;
30636         }
30637         return box;
30638     },
30639
30640     updateBox : function(box){
30641         if(this.split && !this.collapsed){
30642             var sw = this.split.el.getWidth();
30643             box.width -= sw;
30644             this.split.el.setLeft(box.x);
30645             this.split.el.setTop(box.y);
30646             this.split.el.setHeight(box.height);
30647             box.x += sw;
30648         }
30649         if(this.collapsed){
30650             this.updateBody(null, box.height);
30651         }
30652         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30653     }
30654 });
30655
30656 Roo.WestLayoutRegion = function(mgr, config){
30657     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30658     if(this.split){
30659         this.split.placement = Roo.SplitBar.LEFT;
30660         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30661         this.split.el.addClass("x-layout-split-h");
30662     }
30663     var size = config.initialSize || config.width;
30664     if(typeof size != "undefined"){
30665         this.el.setWidth(size);
30666     }
30667 };
30668 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30669     orientation: Roo.SplitBar.HORIZONTAL,
30670     getBox : function(){
30671         if(this.collapsed){
30672             return this.collapsedEl.getBox();
30673         }
30674         var box = this.el.getBox();
30675         if(this.split){
30676             box.width += this.split.el.getWidth();
30677         }
30678         return box;
30679     },
30680     
30681     updateBox : function(box){
30682         if(this.split && !this.collapsed){
30683             var sw = this.split.el.getWidth();
30684             box.width -= sw;
30685             this.split.el.setLeft(box.x+box.width);
30686             this.split.el.setTop(box.y);
30687             this.split.el.setHeight(box.height);
30688         }
30689         if(this.collapsed){
30690             this.updateBody(null, box.height);
30691         }
30692         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30693     }
30694 });
30695 /*
30696  * Based on:
30697  * Ext JS Library 1.1.1
30698  * Copyright(c) 2006-2007, Ext JS, LLC.
30699  *
30700  * Originally Released Under LGPL - original licence link has changed is not relivant.
30701  *
30702  * Fork - LGPL
30703  * <script type="text/javascript">
30704  */
30705  
30706  
30707 /*
30708  * Private internal class for reading and applying state
30709  */
30710 Roo.LayoutStateManager = function(layout){
30711      // default empty state
30712      this.state = {
30713         north: {},
30714         south: {},
30715         east: {},
30716         west: {}       
30717     };
30718 };
30719
30720 Roo.LayoutStateManager.prototype = {
30721     init : function(layout, provider){
30722         this.provider = provider;
30723         var state = provider.get(layout.id+"-layout-state");
30724         if(state){
30725             var wasUpdating = layout.isUpdating();
30726             if(!wasUpdating){
30727                 layout.beginUpdate();
30728             }
30729             for(var key in state){
30730                 if(typeof state[key] != "function"){
30731                     var rstate = state[key];
30732                     var r = layout.getRegion(key);
30733                     if(r && rstate){
30734                         if(rstate.size){
30735                             r.resizeTo(rstate.size);
30736                         }
30737                         if(rstate.collapsed == true){
30738                             r.collapse(true);
30739                         }else{
30740                             r.expand(null, true);
30741                         }
30742                     }
30743                 }
30744             }
30745             if(!wasUpdating){
30746                 layout.endUpdate();
30747             }
30748             this.state = state; 
30749         }
30750         this.layout = layout;
30751         layout.on("regionresized", this.onRegionResized, this);
30752         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30753         layout.on("regionexpanded", this.onRegionExpanded, this);
30754     },
30755     
30756     storeState : function(){
30757         this.provider.set(this.layout.id+"-layout-state", this.state);
30758     },
30759     
30760     onRegionResized : function(region, newSize){
30761         this.state[region.getPosition()].size = newSize;
30762         this.storeState();
30763     },
30764     
30765     onRegionCollapsed : function(region){
30766         this.state[region.getPosition()].collapsed = true;
30767         this.storeState();
30768     },
30769     
30770     onRegionExpanded : function(region){
30771         this.state[region.getPosition()].collapsed = false;
30772         this.storeState();
30773     }
30774 };/*
30775  * Based on:
30776  * Ext JS Library 1.1.1
30777  * Copyright(c) 2006-2007, Ext JS, LLC.
30778  *
30779  * Originally Released Under LGPL - original licence link has changed is not relivant.
30780  *
30781  * Fork - LGPL
30782  * <script type="text/javascript">
30783  */
30784 /**
30785  * @class Roo.ContentPanel
30786  * @extends Roo.util.Observable
30787  * A basic ContentPanel element.
30788  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30789  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30790  * @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
30791  * @cfg {Boolean}   closable      True if the panel can be closed/removed
30792  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
30793  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30794  * @cfg {Toolbar}   toolbar       A toolbar for this panel
30795  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
30796  * @cfg {String} title          The title for this panel
30797  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30798  * @cfg {String} url            Calls {@link #setUrl} with this value
30799  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30800  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
30801  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
30802  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
30803
30804  * @constructor
30805  * Create a new ContentPanel.
30806  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30807  * @param {String/Object} config A string to set only the title or a config object
30808  * @param {String} content (optional) Set the HTML content for this panel
30809  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30810  */
30811 Roo.ContentPanel = function(el, config, content){
30812     
30813      
30814     /*
30815     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30816         config = el;
30817         el = Roo.id();
30818     }
30819     if (config && config.parentLayout) { 
30820         el = config.parentLayout.el.createChild(); 
30821     }
30822     */
30823     if(el.autoCreate){ // xtype is available if this is called from factory
30824         config = el;
30825         el = Roo.id();
30826     }
30827     this.el = Roo.get(el);
30828     if(!this.el && config && config.autoCreate){
30829         if(typeof config.autoCreate == "object"){
30830             if(!config.autoCreate.id){
30831                 config.autoCreate.id = config.id||el;
30832             }
30833             this.el = Roo.DomHelper.append(document.body,
30834                         config.autoCreate, true);
30835         }else{
30836             this.el = Roo.DomHelper.append(document.body,
30837                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30838         }
30839     }
30840     this.closable = false;
30841     this.loaded = false;
30842     this.active = false;
30843     if(typeof config == "string"){
30844         this.title = config;
30845     }else{
30846         Roo.apply(this, config);
30847     }
30848     
30849     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30850         this.wrapEl = this.el.wrap();
30851         this.toolbar.container = this.el.insertSibling(false, 'before');
30852         this.toolbar = new Roo.Toolbar(this.toolbar);
30853     }
30854     
30855     // xtype created footer. - not sure if will work as we normally have to render first..
30856     if (this.footer && !this.footer.el && this.footer.xtype) {
30857         if (!this.wrapEl) {
30858             this.wrapEl = this.el.wrap();
30859         }
30860     
30861         this.footer.container = this.wrapEl.createChild();
30862          
30863         this.footer = Roo.factory(this.footer, Roo);
30864         
30865     }
30866     
30867     if(this.resizeEl){
30868         this.resizeEl = Roo.get(this.resizeEl, true);
30869     }else{
30870         this.resizeEl = this.el;
30871     }
30872     // handle view.xtype
30873     
30874  
30875     
30876     
30877     this.addEvents({
30878         /**
30879          * @event activate
30880          * Fires when this panel is activated. 
30881          * @param {Roo.ContentPanel} this
30882          */
30883         "activate" : true,
30884         /**
30885          * @event deactivate
30886          * Fires when this panel is activated. 
30887          * @param {Roo.ContentPanel} this
30888          */
30889         "deactivate" : true,
30890
30891         /**
30892          * @event resize
30893          * Fires when this panel is resized if fitToFrame is true.
30894          * @param {Roo.ContentPanel} this
30895          * @param {Number} width The width after any component adjustments
30896          * @param {Number} height The height after any component adjustments
30897          */
30898         "resize" : true,
30899         
30900          /**
30901          * @event render
30902          * Fires when this tab is created
30903          * @param {Roo.ContentPanel} this
30904          */
30905         "render" : true
30906         
30907         
30908         
30909     });
30910     
30911
30912     
30913     
30914     if(this.autoScroll){
30915         this.resizeEl.setStyle("overflow", "auto");
30916     } else {
30917         // fix randome scrolling
30918         this.el.on('scroll', function() {
30919             Roo.log('fix random scolling');
30920             this.scrollTo('top',0); 
30921         });
30922     }
30923     content = content || this.content;
30924     if(content){
30925         this.setContent(content);
30926     }
30927     if(config && config.url){
30928         this.setUrl(this.url, this.params, this.loadOnce);
30929     }
30930     
30931     
30932     
30933     Roo.ContentPanel.superclass.constructor.call(this);
30934     
30935     if (this.view && typeof(this.view.xtype) != 'undefined') {
30936         this.view.el = this.el.appendChild(document.createElement("div"));
30937         this.view = Roo.factory(this.view); 
30938         this.view.render  &&  this.view.render(false, '');  
30939     }
30940     
30941     
30942     this.fireEvent('render', this);
30943 };
30944
30945 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30946     tabTip:'',
30947     setRegion : function(region){
30948         this.region = region;
30949         if(region){
30950            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30951         }else{
30952            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30953         } 
30954     },
30955     
30956     /**
30957      * Returns the toolbar for this Panel if one was configured. 
30958      * @return {Roo.Toolbar} 
30959      */
30960     getToolbar : function(){
30961         return this.toolbar;
30962     },
30963     
30964     setActiveState : function(active){
30965         this.active = active;
30966         if(!active){
30967             this.fireEvent("deactivate", this);
30968         }else{
30969             this.fireEvent("activate", this);
30970         }
30971     },
30972     /**
30973      * Updates this panel's element
30974      * @param {String} content The new content
30975      * @param {Boolean} loadScripts (optional) true to look for and process scripts
30976     */
30977     setContent : function(content, loadScripts){
30978         this.el.update(content, loadScripts);
30979     },
30980
30981     ignoreResize : function(w, h){
30982         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30983             return true;
30984         }else{
30985             this.lastSize = {width: w, height: h};
30986             return false;
30987         }
30988     },
30989     /**
30990      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30991      * @return {Roo.UpdateManager} The UpdateManager
30992      */
30993     getUpdateManager : function(){
30994         return this.el.getUpdateManager();
30995     },
30996      /**
30997      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
30998      * @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:
30999 <pre><code>
31000 panel.load({
31001     url: "your-url.php",
31002     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31003     callback: yourFunction,
31004     scope: yourObject, //(optional scope)
31005     discardUrl: false,
31006     nocache: false,
31007     text: "Loading...",
31008     timeout: 30,
31009     scripts: false
31010 });
31011 </code></pre>
31012      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31013      * 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.
31014      * @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}
31015      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31016      * @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.
31017      * @return {Roo.ContentPanel} this
31018      */
31019     load : function(){
31020         var um = this.el.getUpdateManager();
31021         um.update.apply(um, arguments);
31022         return this;
31023     },
31024
31025
31026     /**
31027      * 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.
31028      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31029      * @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)
31030      * @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)
31031      * @return {Roo.UpdateManager} The UpdateManager
31032      */
31033     setUrl : function(url, params, loadOnce){
31034         if(this.refreshDelegate){
31035             this.removeListener("activate", this.refreshDelegate);
31036         }
31037         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31038         this.on("activate", this.refreshDelegate);
31039         return this.el.getUpdateManager();
31040     },
31041     
31042     _handleRefresh : function(url, params, loadOnce){
31043         if(!loadOnce || !this.loaded){
31044             var updater = this.el.getUpdateManager();
31045             updater.update(url, params, this._setLoaded.createDelegate(this));
31046         }
31047     },
31048     
31049     _setLoaded : function(){
31050         this.loaded = true;
31051     }, 
31052     
31053     /**
31054      * Returns this panel's id
31055      * @return {String} 
31056      */
31057     getId : function(){
31058         return this.el.id;
31059     },
31060     
31061     /** 
31062      * Returns this panel's element - used by regiosn to add.
31063      * @return {Roo.Element} 
31064      */
31065     getEl : function(){
31066         return this.wrapEl || this.el;
31067     },
31068     
31069     adjustForComponents : function(width, height)
31070     {
31071         //Roo.log('adjustForComponents ');
31072         if(this.resizeEl != this.el){
31073             width -= this.el.getFrameWidth('lr');
31074             height -= this.el.getFrameWidth('tb');
31075         }
31076         if(this.toolbar){
31077             var te = this.toolbar.getEl();
31078             height -= te.getHeight();
31079             te.setWidth(width);
31080         }
31081         if(this.footer){
31082             var te = this.footer.getEl();
31083             Roo.log("footer:" + te.getHeight());
31084             
31085             height -= te.getHeight();
31086             te.setWidth(width);
31087         }
31088         
31089         
31090         if(this.adjustments){
31091             width += this.adjustments[0];
31092             height += this.adjustments[1];
31093         }
31094         return {"width": width, "height": height};
31095     },
31096     
31097     setSize : function(width, height){
31098         if(this.fitToFrame && !this.ignoreResize(width, height)){
31099             if(this.fitContainer && this.resizeEl != this.el){
31100                 this.el.setSize(width, height);
31101             }
31102             var size = this.adjustForComponents(width, height);
31103             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31104             this.fireEvent('resize', this, size.width, size.height);
31105         }
31106     },
31107     
31108     /**
31109      * Returns this panel's title
31110      * @return {String} 
31111      */
31112     getTitle : function(){
31113         return this.title;
31114     },
31115     
31116     /**
31117      * Set this panel's title
31118      * @param {String} title
31119      */
31120     setTitle : function(title){
31121         this.title = title;
31122         if(this.region){
31123             this.region.updatePanelTitle(this, title);
31124         }
31125     },
31126     
31127     /**
31128      * Returns true is this panel was configured to be closable
31129      * @return {Boolean} 
31130      */
31131     isClosable : function(){
31132         return this.closable;
31133     },
31134     
31135     beforeSlide : function(){
31136         this.el.clip();
31137         this.resizeEl.clip();
31138     },
31139     
31140     afterSlide : function(){
31141         this.el.unclip();
31142         this.resizeEl.unclip();
31143     },
31144     
31145     /**
31146      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31147      *   Will fail silently if the {@link #setUrl} method has not been called.
31148      *   This does not activate the panel, just updates its content.
31149      */
31150     refresh : function(){
31151         if(this.refreshDelegate){
31152            this.loaded = false;
31153            this.refreshDelegate();
31154         }
31155     },
31156     
31157     /**
31158      * Destroys this panel
31159      */
31160     destroy : function(){
31161         this.el.removeAllListeners();
31162         var tempEl = document.createElement("span");
31163         tempEl.appendChild(this.el.dom);
31164         tempEl.innerHTML = "";
31165         this.el.remove();
31166         this.el = null;
31167     },
31168     
31169     /**
31170      * form - if the content panel contains a form - this is a reference to it.
31171      * @type {Roo.form.Form}
31172      */
31173     form : false,
31174     /**
31175      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31176      *    This contains a reference to it.
31177      * @type {Roo.View}
31178      */
31179     view : false,
31180     
31181       /**
31182      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31183      * <pre><code>
31184
31185 layout.addxtype({
31186        xtype : 'Form',
31187        items: [ .... ]
31188    }
31189 );
31190
31191 </code></pre>
31192      * @param {Object} cfg Xtype definition of item to add.
31193      */
31194     
31195     addxtype : function(cfg) {
31196         // add form..
31197         if (cfg.xtype.match(/^Form$/)) {
31198             
31199             var el;
31200             //if (this.footer) {
31201             //    el = this.footer.container.insertSibling(false, 'before');
31202             //} else {
31203                 el = this.el.createChild();
31204             //}
31205
31206             this.form = new  Roo.form.Form(cfg);
31207             
31208             
31209             if ( this.form.allItems.length) {
31210                 this.form.render(el.dom);
31211             }
31212             return this.form;
31213         }
31214         // should only have one of theses..
31215         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31216             // views.. should not be just added - used named prop 'view''
31217             
31218             cfg.el = this.el.appendChild(document.createElement("div"));
31219             // factory?
31220             
31221             var ret = new Roo.factory(cfg);
31222              
31223              ret.render && ret.render(false, ''); // render blank..
31224             this.view = ret;
31225             return ret;
31226         }
31227         return false;
31228     }
31229 });
31230
31231 /**
31232  * @class Roo.GridPanel
31233  * @extends Roo.ContentPanel
31234  * @constructor
31235  * Create a new GridPanel.
31236  * @param {Roo.grid.Grid} grid The grid for this panel
31237  * @param {String/Object} config A string to set only the panel's title, or a config object
31238  */
31239 Roo.GridPanel = function(grid, config){
31240     
31241   
31242     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31243         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31244         
31245     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31246     
31247     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31248     
31249     if(this.toolbar){
31250         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31251     }
31252     // xtype created footer. - not sure if will work as we normally have to render first..
31253     if (this.footer && !this.footer.el && this.footer.xtype) {
31254         
31255         this.footer.container = this.grid.getView().getFooterPanel(true);
31256         this.footer.dataSource = this.grid.dataSource;
31257         this.footer = Roo.factory(this.footer, Roo);
31258         
31259     }
31260     
31261     grid.monitorWindowResize = false; // turn off autosizing
31262     grid.autoHeight = false;
31263     grid.autoWidth = false;
31264     this.grid = grid;
31265     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31266 };
31267
31268 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31269     getId : function(){
31270         return this.grid.id;
31271     },
31272     
31273     /**
31274      * Returns the grid for this panel
31275      * @return {Roo.grid.Grid} 
31276      */
31277     getGrid : function(){
31278         return this.grid;    
31279     },
31280     
31281     setSize : function(width, height){
31282         if(!this.ignoreResize(width, height)){
31283             var grid = this.grid;
31284             var size = this.adjustForComponents(width, height);
31285             grid.getGridEl().setSize(size.width, size.height);
31286             grid.autoSize();
31287         }
31288     },
31289     
31290     beforeSlide : function(){
31291         this.grid.getView().scroller.clip();
31292     },
31293     
31294     afterSlide : function(){
31295         this.grid.getView().scroller.unclip();
31296     },
31297     
31298     destroy : function(){
31299         this.grid.destroy();
31300         delete this.grid;
31301         Roo.GridPanel.superclass.destroy.call(this); 
31302     }
31303 });
31304
31305
31306 /**
31307  * @class Roo.NestedLayoutPanel
31308  * @extends Roo.ContentPanel
31309  * @constructor
31310  * Create a new NestedLayoutPanel.
31311  * 
31312  * 
31313  * @param {Roo.BorderLayout} layout The layout for this panel
31314  * @param {String/Object} config A string to set only the title or a config object
31315  */
31316 Roo.NestedLayoutPanel = function(layout, config)
31317 {
31318     // construct with only one argument..
31319     /* FIXME - implement nicer consturctors
31320     if (layout.layout) {
31321         config = layout;
31322         layout = config.layout;
31323         delete config.layout;
31324     }
31325     if (layout.xtype && !layout.getEl) {
31326         // then layout needs constructing..
31327         layout = Roo.factory(layout, Roo);
31328     }
31329     */
31330     
31331     
31332     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31333     
31334     layout.monitorWindowResize = false; // turn off autosizing
31335     this.layout = layout;
31336     this.layout.getEl().addClass("x-layout-nested-layout");
31337     
31338     
31339     
31340     
31341 };
31342
31343 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31344
31345     setSize : function(width, height){
31346         if(!this.ignoreResize(width, height)){
31347             var size = this.adjustForComponents(width, height);
31348             var el = this.layout.getEl();
31349             el.setSize(size.width, size.height);
31350             var touch = el.dom.offsetWidth;
31351             this.layout.layout();
31352             // ie requires a double layout on the first pass
31353             if(Roo.isIE && !this.initialized){
31354                 this.initialized = true;
31355                 this.layout.layout();
31356             }
31357         }
31358     },
31359     
31360     // activate all subpanels if not currently active..
31361     
31362     setActiveState : function(active){
31363         this.active = active;
31364         if(!active){
31365             this.fireEvent("deactivate", this);
31366             return;
31367         }
31368         
31369         this.fireEvent("activate", this);
31370         // not sure if this should happen before or after..
31371         if (!this.layout) {
31372             return; // should not happen..
31373         }
31374         var reg = false;
31375         for (var r in this.layout.regions) {
31376             reg = this.layout.getRegion(r);
31377             if (reg.getActivePanel()) {
31378                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31379                 reg.setActivePanel(reg.getActivePanel());
31380                 continue;
31381             }
31382             if (!reg.panels.length) {
31383                 continue;
31384             }
31385             reg.showPanel(reg.getPanel(0));
31386         }
31387         
31388         
31389         
31390         
31391     },
31392     
31393     /**
31394      * Returns the nested BorderLayout for this panel
31395      * @return {Roo.BorderLayout} 
31396      */
31397     getLayout : function(){
31398         return this.layout;
31399     },
31400     
31401      /**
31402      * Adds a xtype elements to the layout of the nested panel
31403      * <pre><code>
31404
31405 panel.addxtype({
31406        xtype : 'ContentPanel',
31407        region: 'west',
31408        items: [ .... ]
31409    }
31410 );
31411
31412 panel.addxtype({
31413         xtype : 'NestedLayoutPanel',
31414         region: 'west',
31415         layout: {
31416            center: { },
31417            west: { }   
31418         },
31419         items : [ ... list of content panels or nested layout panels.. ]
31420    }
31421 );
31422 </code></pre>
31423      * @param {Object} cfg Xtype definition of item to add.
31424      */
31425     addxtype : function(cfg) {
31426         return this.layout.addxtype(cfg);
31427     
31428     }
31429 });
31430
31431 Roo.ScrollPanel = function(el, config, content){
31432     config = config || {};
31433     config.fitToFrame = true;
31434     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31435     
31436     this.el.dom.style.overflow = "hidden";
31437     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31438     this.el.removeClass("x-layout-inactive-content");
31439     this.el.on("mousewheel", this.onWheel, this);
31440
31441     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31442     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31443     up.unselectable(); down.unselectable();
31444     up.on("click", this.scrollUp, this);
31445     down.on("click", this.scrollDown, this);
31446     up.addClassOnOver("x-scroller-btn-over");
31447     down.addClassOnOver("x-scroller-btn-over");
31448     up.addClassOnClick("x-scroller-btn-click");
31449     down.addClassOnClick("x-scroller-btn-click");
31450     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31451
31452     this.resizeEl = this.el;
31453     this.el = wrap; this.up = up; this.down = down;
31454 };
31455
31456 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31457     increment : 100,
31458     wheelIncrement : 5,
31459     scrollUp : function(){
31460         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31461     },
31462
31463     scrollDown : function(){
31464         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31465     },
31466
31467     afterScroll : function(){
31468         var el = this.resizeEl;
31469         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31470         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31471         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31472     },
31473
31474     setSize : function(){
31475         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31476         this.afterScroll();
31477     },
31478
31479     onWheel : function(e){
31480         var d = e.getWheelDelta();
31481         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31482         this.afterScroll();
31483         e.stopEvent();
31484     },
31485
31486     setContent : function(content, loadScripts){
31487         this.resizeEl.update(content, loadScripts);
31488     }
31489
31490 });
31491
31492
31493
31494
31495
31496
31497
31498
31499
31500 /**
31501  * @class Roo.TreePanel
31502  * @extends Roo.ContentPanel
31503  * @constructor
31504  * Create a new TreePanel. - defaults to fit/scoll contents.
31505  * @param {String/Object} config A string to set only the panel's title, or a config object
31506  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31507  */
31508 Roo.TreePanel = function(config){
31509     var el = config.el;
31510     var tree = config.tree;
31511     delete config.tree; 
31512     delete config.el; // hopefull!
31513     
31514     // wrapper for IE7 strict & safari scroll issue
31515     
31516     var treeEl = el.createChild();
31517     config.resizeEl = treeEl;
31518     
31519     
31520     
31521     Roo.TreePanel.superclass.constructor.call(this, el, config);
31522  
31523  
31524     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31525     //console.log(tree);
31526     this.on('activate', function()
31527     {
31528         if (this.tree.rendered) {
31529             return;
31530         }
31531         //console.log('render tree');
31532         this.tree.render();
31533     });
31534     // this should not be needed.. - it's actually the 'el' that resizes?
31535     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31536     
31537     //this.on('resize',  function (cp, w, h) {
31538     //        this.tree.innerCt.setWidth(w);
31539     //        this.tree.innerCt.setHeight(h);
31540     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31541     //});
31542
31543         
31544     
31545 };
31546
31547 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31548     fitToFrame : true,
31549     autoScroll : true
31550 });
31551
31552
31553
31554
31555
31556
31557
31558
31559
31560
31561
31562 /*
31563  * Based on:
31564  * Ext JS Library 1.1.1
31565  * Copyright(c) 2006-2007, Ext JS, LLC.
31566  *
31567  * Originally Released Under LGPL - original licence link has changed is not relivant.
31568  *
31569  * Fork - LGPL
31570  * <script type="text/javascript">
31571  */
31572  
31573
31574 /**
31575  * @class Roo.ReaderLayout
31576  * @extends Roo.BorderLayout
31577  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31578  * center region containing two nested regions (a top one for a list view and one for item preview below),
31579  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31580  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31581  * expedites the setup of the overall layout and regions for this common application style.
31582  * Example:
31583  <pre><code>
31584 var reader = new Roo.ReaderLayout();
31585 var CP = Roo.ContentPanel;  // shortcut for adding
31586
31587 reader.beginUpdate();
31588 reader.add("north", new CP("north", "North"));
31589 reader.add("west", new CP("west", {title: "West"}));
31590 reader.add("east", new CP("east", {title: "East"}));
31591
31592 reader.regions.listView.add(new CP("listView", "List"));
31593 reader.regions.preview.add(new CP("preview", "Preview"));
31594 reader.endUpdate();
31595 </code></pre>
31596 * @constructor
31597 * Create a new ReaderLayout
31598 * @param {Object} config Configuration options
31599 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31600 * document.body if omitted)
31601 */
31602 Roo.ReaderLayout = function(config, renderTo){
31603     var c = config || {size:{}};
31604     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31605         north: c.north !== false ? Roo.apply({
31606             split:false,
31607             initialSize: 32,
31608             titlebar: false
31609         }, c.north) : false,
31610         west: c.west !== false ? Roo.apply({
31611             split:true,
31612             initialSize: 200,
31613             minSize: 175,
31614             maxSize: 400,
31615             titlebar: true,
31616             collapsible: true,
31617             animate: true,
31618             margins:{left:5,right:0,bottom:5,top:5},
31619             cmargins:{left:5,right:5,bottom:5,top:5}
31620         }, c.west) : false,
31621         east: c.east !== false ? Roo.apply({
31622             split:true,
31623             initialSize: 200,
31624             minSize: 175,
31625             maxSize: 400,
31626             titlebar: true,
31627             collapsible: true,
31628             animate: true,
31629             margins:{left:0,right:5,bottom:5,top:5},
31630             cmargins:{left:5,right:5,bottom:5,top:5}
31631         }, c.east) : false,
31632         center: Roo.apply({
31633             tabPosition: 'top',
31634             autoScroll:false,
31635             closeOnTab: true,
31636             titlebar:false,
31637             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31638         }, c.center)
31639     });
31640
31641     this.el.addClass('x-reader');
31642
31643     this.beginUpdate();
31644
31645     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31646         south: c.preview !== false ? Roo.apply({
31647             split:true,
31648             initialSize: 200,
31649             minSize: 100,
31650             autoScroll:true,
31651             collapsible:true,
31652             titlebar: true,
31653             cmargins:{top:5,left:0, right:0, bottom:0}
31654         }, c.preview) : false,
31655         center: Roo.apply({
31656             autoScroll:false,
31657             titlebar:false,
31658             minHeight:200
31659         }, c.listView)
31660     });
31661     this.add('center', new Roo.NestedLayoutPanel(inner,
31662             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31663
31664     this.endUpdate();
31665
31666     this.regions.preview = inner.getRegion('south');
31667     this.regions.listView = inner.getRegion('center');
31668 };
31669
31670 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31671  * Based on:
31672  * Ext JS Library 1.1.1
31673  * Copyright(c) 2006-2007, Ext JS, LLC.
31674  *
31675  * Originally Released Under LGPL - original licence link has changed is not relivant.
31676  *
31677  * Fork - LGPL
31678  * <script type="text/javascript">
31679  */
31680  
31681 /**
31682  * @class Roo.grid.Grid
31683  * @extends Roo.util.Observable
31684  * This class represents the primary interface of a component based grid control.
31685  * <br><br>Usage:<pre><code>
31686  var grid = new Roo.grid.Grid("my-container-id", {
31687      ds: myDataStore,
31688      cm: myColModel,
31689      selModel: mySelectionModel,
31690      autoSizeColumns: true,
31691      monitorWindowResize: false,
31692      trackMouseOver: true
31693  });
31694  // set any options
31695  grid.render();
31696  * </code></pre>
31697  * <b>Common Problems:</b><br/>
31698  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31699  * element will correct this<br/>
31700  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31701  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31702  * are unpredictable.<br/>
31703  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31704  * grid to calculate dimensions/offsets.<br/>
31705   * @constructor
31706  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31707  * The container MUST have some type of size defined for the grid to fill. The container will be
31708  * automatically set to position relative if it isn't already.
31709  * @param {Object} config A config object that sets properties on this grid.
31710  */
31711 Roo.grid.Grid = function(container, config){
31712         // initialize the container
31713         this.container = Roo.get(container);
31714         this.container.update("");
31715         this.container.setStyle("overflow", "hidden");
31716     this.container.addClass('x-grid-container');
31717
31718     this.id = this.container.id;
31719
31720     Roo.apply(this, config);
31721     // check and correct shorthanded configs
31722     if(this.ds){
31723         this.dataSource = this.ds;
31724         delete this.ds;
31725     }
31726     if(this.cm){
31727         this.colModel = this.cm;
31728         delete this.cm;
31729     }
31730     if(this.sm){
31731         this.selModel = this.sm;
31732         delete this.sm;
31733     }
31734
31735     if (this.selModel) {
31736         this.selModel = Roo.factory(this.selModel, Roo.grid);
31737         this.sm = this.selModel;
31738         this.sm.xmodule = this.xmodule || false;
31739     }
31740     if (typeof(this.colModel.config) == 'undefined') {
31741         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31742         this.cm = this.colModel;
31743         this.cm.xmodule = this.xmodule || false;
31744     }
31745     if (this.dataSource) {
31746         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31747         this.ds = this.dataSource;
31748         this.ds.xmodule = this.xmodule || false;
31749          
31750     }
31751     
31752     
31753     
31754     if(this.width){
31755         this.container.setWidth(this.width);
31756     }
31757
31758     if(this.height){
31759         this.container.setHeight(this.height);
31760     }
31761     /** @private */
31762         this.addEvents({
31763         // raw events
31764         /**
31765          * @event click
31766          * The raw click event for the entire grid.
31767          * @param {Roo.EventObject} e
31768          */
31769         "click" : true,
31770         /**
31771          * @event dblclick
31772          * The raw dblclick event for the entire grid.
31773          * @param {Roo.EventObject} e
31774          */
31775         "dblclick" : true,
31776         /**
31777          * @event contextmenu
31778          * The raw contextmenu event for the entire grid.
31779          * @param {Roo.EventObject} e
31780          */
31781         "contextmenu" : true,
31782         /**
31783          * @event mousedown
31784          * The raw mousedown event for the entire grid.
31785          * @param {Roo.EventObject} e
31786          */
31787         "mousedown" : true,
31788         /**
31789          * @event mouseup
31790          * The raw mouseup event for the entire grid.
31791          * @param {Roo.EventObject} e
31792          */
31793         "mouseup" : true,
31794         /**
31795          * @event mouseover
31796          * The raw mouseover event for the entire grid.
31797          * @param {Roo.EventObject} e
31798          */
31799         "mouseover" : true,
31800         /**
31801          * @event mouseout
31802          * The raw mouseout event for the entire grid.
31803          * @param {Roo.EventObject} e
31804          */
31805         "mouseout" : true,
31806         /**
31807          * @event keypress
31808          * The raw keypress event for the entire grid.
31809          * @param {Roo.EventObject} e
31810          */
31811         "keypress" : true,
31812         /**
31813          * @event keydown
31814          * The raw keydown event for the entire grid.
31815          * @param {Roo.EventObject} e
31816          */
31817         "keydown" : true,
31818
31819         // custom events
31820
31821         /**
31822          * @event cellclick
31823          * Fires when a cell is clicked
31824          * @param {Grid} this
31825          * @param {Number} rowIndex
31826          * @param {Number} columnIndex
31827          * @param {Roo.EventObject} e
31828          */
31829         "cellclick" : true,
31830         /**
31831          * @event celldblclick
31832          * Fires when a cell is double clicked
31833          * @param {Grid} this
31834          * @param {Number} rowIndex
31835          * @param {Number} columnIndex
31836          * @param {Roo.EventObject} e
31837          */
31838         "celldblclick" : true,
31839         /**
31840          * @event rowclick
31841          * Fires when a row is clicked
31842          * @param {Grid} this
31843          * @param {Number} rowIndex
31844          * @param {Roo.EventObject} e
31845          */
31846         "rowclick" : true,
31847         /**
31848          * @event rowdblclick
31849          * Fires when a row is double clicked
31850          * @param {Grid} this
31851          * @param {Number} rowIndex
31852          * @param {Roo.EventObject} e
31853          */
31854         "rowdblclick" : true,
31855         /**
31856          * @event headerclick
31857          * Fires when a header is clicked
31858          * @param {Grid} this
31859          * @param {Number} columnIndex
31860          * @param {Roo.EventObject} e
31861          */
31862         "headerclick" : true,
31863         /**
31864          * @event headerdblclick
31865          * Fires when a header cell is double clicked
31866          * @param {Grid} this
31867          * @param {Number} columnIndex
31868          * @param {Roo.EventObject} e
31869          */
31870         "headerdblclick" : true,
31871         /**
31872          * @event rowcontextmenu
31873          * Fires when a row is right clicked
31874          * @param {Grid} this
31875          * @param {Number} rowIndex
31876          * @param {Roo.EventObject} e
31877          */
31878         "rowcontextmenu" : true,
31879         /**
31880          * @event cellcontextmenu
31881          * Fires when a cell is right clicked
31882          * @param {Grid} this
31883          * @param {Number} rowIndex
31884          * @param {Number} cellIndex
31885          * @param {Roo.EventObject} e
31886          */
31887          "cellcontextmenu" : true,
31888         /**
31889          * @event headercontextmenu
31890          * Fires when a header is right clicked
31891          * @param {Grid} this
31892          * @param {Number} columnIndex
31893          * @param {Roo.EventObject} e
31894          */
31895         "headercontextmenu" : true,
31896         /**
31897          * @event bodyscroll
31898          * Fires when the body element is scrolled
31899          * @param {Number} scrollLeft
31900          * @param {Number} scrollTop
31901          */
31902         "bodyscroll" : true,
31903         /**
31904          * @event columnresize
31905          * Fires when the user resizes a column
31906          * @param {Number} columnIndex
31907          * @param {Number} newSize
31908          */
31909         "columnresize" : true,
31910         /**
31911          * @event columnmove
31912          * Fires when the user moves a column
31913          * @param {Number} oldIndex
31914          * @param {Number} newIndex
31915          */
31916         "columnmove" : true,
31917         /**
31918          * @event startdrag
31919          * Fires when row(s) start being dragged
31920          * @param {Grid} this
31921          * @param {Roo.GridDD} dd The drag drop object
31922          * @param {event} e The raw browser event
31923          */
31924         "startdrag" : true,
31925         /**
31926          * @event enddrag
31927          * Fires when a drag operation is complete
31928          * @param {Grid} this
31929          * @param {Roo.GridDD} dd The drag drop object
31930          * @param {event} e The raw browser event
31931          */
31932         "enddrag" : true,
31933         /**
31934          * @event dragdrop
31935          * Fires when dragged row(s) are dropped on a valid DD target
31936          * @param {Grid} this
31937          * @param {Roo.GridDD} dd The drag drop object
31938          * @param {String} targetId The target drag drop object
31939          * @param {event} e The raw browser event
31940          */
31941         "dragdrop" : true,
31942         /**
31943          * @event dragover
31944          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31945          * @param {Grid} this
31946          * @param {Roo.GridDD} dd The drag drop object
31947          * @param {String} targetId The target drag drop object
31948          * @param {event} e The raw browser event
31949          */
31950         "dragover" : true,
31951         /**
31952          * @event dragenter
31953          *  Fires when the dragged row(s) first cross another DD target while being dragged
31954          * @param {Grid} this
31955          * @param {Roo.GridDD} dd The drag drop object
31956          * @param {String} targetId The target drag drop object
31957          * @param {event} e The raw browser event
31958          */
31959         "dragenter" : true,
31960         /**
31961          * @event dragout
31962          * Fires when the dragged row(s) leave another DD target while being dragged
31963          * @param {Grid} this
31964          * @param {Roo.GridDD} dd The drag drop object
31965          * @param {String} targetId The target drag drop object
31966          * @param {event} e The raw browser event
31967          */
31968         "dragout" : true,
31969         /**
31970          * @event rowclass
31971          * Fires when a row is rendered, so you can change add a style to it.
31972          * @param {GridView} gridview   The grid view
31973          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
31974          */
31975         'rowclass' : true,
31976
31977         /**
31978          * @event render
31979          * Fires when the grid is rendered
31980          * @param {Grid} grid
31981          */
31982         'render' : true
31983     });
31984
31985     Roo.grid.Grid.superclass.constructor.call(this);
31986 };
31987 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31988     
31989     /**
31990      * @cfg {String} ddGroup - drag drop group.
31991      */
31992
31993     /**
31994      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
31995      */
31996     minColumnWidth : 25,
31997
31998     /**
31999      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32000      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32001      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32002      */
32003     autoSizeColumns : false,
32004
32005     /**
32006      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32007      */
32008     autoSizeHeaders : true,
32009
32010     /**
32011      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32012      */
32013     monitorWindowResize : true,
32014
32015     /**
32016      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32017      * rows measured to get a columns size. Default is 0 (all rows).
32018      */
32019     maxRowsToMeasure : 0,
32020
32021     /**
32022      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32023      */
32024     trackMouseOver : true,
32025
32026     /**
32027     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32028     */
32029     
32030     /**
32031     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32032     */
32033     enableDragDrop : false,
32034     
32035     /**
32036     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32037     */
32038     enableColumnMove : true,
32039     
32040     /**
32041     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32042     */
32043     enableColumnHide : true,
32044     
32045     /**
32046     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32047     */
32048     enableRowHeightSync : false,
32049     
32050     /**
32051     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32052     */
32053     stripeRows : true,
32054     
32055     /**
32056     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32057     */
32058     autoHeight : false,
32059
32060     /**
32061      * @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.
32062      */
32063     autoExpandColumn : false,
32064
32065     /**
32066     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32067     * Default is 50.
32068     */
32069     autoExpandMin : 50,
32070
32071     /**
32072     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32073     */
32074     autoExpandMax : 1000,
32075
32076     /**
32077     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32078     */
32079     view : null,
32080
32081     /**
32082     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32083     */
32084     loadMask : false,
32085     /**
32086     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32087     */
32088     dropTarget: false,
32089     
32090    
32091     
32092     // private
32093     rendered : false,
32094
32095     /**
32096     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32097     * of a fixed width. Default is false.
32098     */
32099     /**
32100     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32101     */
32102     /**
32103      * Called once after all setup has been completed and the grid is ready to be rendered.
32104      * @return {Roo.grid.Grid} this
32105      */
32106     render : function()
32107     {
32108         var c = this.container;
32109         // try to detect autoHeight/width mode
32110         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32111             this.autoHeight = true;
32112         }
32113         var view = this.getView();
32114         view.init(this);
32115
32116         c.on("click", this.onClick, this);
32117         c.on("dblclick", this.onDblClick, this);
32118         c.on("contextmenu", this.onContextMenu, this);
32119         c.on("keydown", this.onKeyDown, this);
32120         if (Roo.isTouch) {
32121             c.on("touchstart", this.onTouchStart, this);
32122         }
32123
32124         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32125
32126         this.getSelectionModel().init(this);
32127
32128         view.render();
32129
32130         if(this.loadMask){
32131             this.loadMask = new Roo.LoadMask(this.container,
32132                     Roo.apply({store:this.dataSource}, this.loadMask));
32133         }
32134         
32135         
32136         if (this.toolbar && this.toolbar.xtype) {
32137             this.toolbar.container = this.getView().getHeaderPanel(true);
32138             this.toolbar = new Roo.Toolbar(this.toolbar);
32139         }
32140         if (this.footer && this.footer.xtype) {
32141             this.footer.dataSource = this.getDataSource();
32142             this.footer.container = this.getView().getFooterPanel(true);
32143             this.footer = Roo.factory(this.footer, Roo);
32144         }
32145         if (this.dropTarget && this.dropTarget.xtype) {
32146             delete this.dropTarget.xtype;
32147             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32148         }
32149         
32150         
32151         this.rendered = true;
32152         this.fireEvent('render', this);
32153         return this;
32154     },
32155
32156         /**
32157          * Reconfigures the grid to use a different Store and Column Model.
32158          * The View will be bound to the new objects and refreshed.
32159          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32160          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32161          */
32162     reconfigure : function(dataSource, colModel){
32163         if(this.loadMask){
32164             this.loadMask.destroy();
32165             this.loadMask = new Roo.LoadMask(this.container,
32166                     Roo.apply({store:dataSource}, this.loadMask));
32167         }
32168         this.view.bind(dataSource, colModel);
32169         this.dataSource = dataSource;
32170         this.colModel = colModel;
32171         this.view.refresh(true);
32172     },
32173
32174     // private
32175     onKeyDown : function(e){
32176         this.fireEvent("keydown", e);
32177     },
32178
32179     /**
32180      * Destroy this grid.
32181      * @param {Boolean} removeEl True to remove the element
32182      */
32183     destroy : function(removeEl, keepListeners){
32184         if(this.loadMask){
32185             this.loadMask.destroy();
32186         }
32187         var c = this.container;
32188         c.removeAllListeners();
32189         this.view.destroy();
32190         this.colModel.purgeListeners();
32191         if(!keepListeners){
32192             this.purgeListeners();
32193         }
32194         c.update("");
32195         if(removeEl === true){
32196             c.remove();
32197         }
32198     },
32199
32200     // private
32201     processEvent : function(name, e){
32202         // does this fire select???
32203         //Roo.log('grid:processEvent '  + name);
32204         
32205         if (name != 'touchstart' ) {
32206             this.fireEvent(name, e);    
32207         }
32208         
32209         var t = e.getTarget();
32210         var v = this.view;
32211         var header = v.findHeaderIndex(t);
32212         if(header !== false){
32213             var ename = name == 'touchstart' ? 'click' : name;
32214              
32215             this.fireEvent("header" + ename, this, header, e);
32216         }else{
32217             var row = v.findRowIndex(t);
32218             var cell = v.findCellIndex(t);
32219             if (name == 'touchstart') {
32220                 // first touch is always a click.
32221                 // hopefull this happens after selection is updated.?
32222                 name = false;
32223                 
32224                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32225                     var cs = this.selModel.getSelectedCell();
32226                     if (row == cs[0] && cell == cs[1]){
32227                         name = 'dblclick';
32228                     }
32229                 }
32230                 if (typeof(this.selModel.getSelections) != 'undefined') {
32231                     var cs = this.selModel.getSelections();
32232                     var ds = this.dataSource;
32233                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32234                         name = 'dblclick';
32235                     }
32236                 }
32237                 if (!name) {
32238                     return;
32239                 }
32240             }
32241             
32242             
32243             if(row !== false){
32244                 this.fireEvent("row" + name, this, row, e);
32245                 if(cell !== false){
32246                     this.fireEvent("cell" + name, this, row, cell, e);
32247                 }
32248             }
32249         }
32250     },
32251
32252     // private
32253     onClick : function(e){
32254         this.processEvent("click", e);
32255     },
32256    // private
32257     onTouchStart : function(e){
32258         this.processEvent("touchstart", e);
32259     },
32260
32261     // private
32262     onContextMenu : function(e, t){
32263         this.processEvent("contextmenu", e);
32264     },
32265
32266     // private
32267     onDblClick : function(e){
32268         this.processEvent("dblclick", e);
32269     },
32270
32271     // private
32272     walkCells : function(row, col, step, fn, scope){
32273         var cm = this.colModel, clen = cm.getColumnCount();
32274         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32275         if(step < 0){
32276             if(col < 0){
32277                 row--;
32278                 first = false;
32279             }
32280             while(row >= 0){
32281                 if(!first){
32282                     col = clen-1;
32283                 }
32284                 first = false;
32285                 while(col >= 0){
32286                     if(fn.call(scope || this, row, col, cm) === true){
32287                         return [row, col];
32288                     }
32289                     col--;
32290                 }
32291                 row--;
32292             }
32293         } else {
32294             if(col >= clen){
32295                 row++;
32296                 first = false;
32297             }
32298             while(row < rlen){
32299                 if(!first){
32300                     col = 0;
32301                 }
32302                 first = false;
32303                 while(col < clen){
32304                     if(fn.call(scope || this, row, col, cm) === true){
32305                         return [row, col];
32306                     }
32307                     col++;
32308                 }
32309                 row++;
32310             }
32311         }
32312         return null;
32313     },
32314
32315     // private
32316     getSelections : function(){
32317         return this.selModel.getSelections();
32318     },
32319
32320     /**
32321      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32322      * but if manual update is required this method will initiate it.
32323      */
32324     autoSize : function(){
32325         if(this.rendered){
32326             this.view.layout();
32327             if(this.view.adjustForScroll){
32328                 this.view.adjustForScroll();
32329             }
32330         }
32331     },
32332
32333     /**
32334      * Returns the grid's underlying element.
32335      * @return {Element} The element
32336      */
32337     getGridEl : function(){
32338         return this.container;
32339     },
32340
32341     // private for compatibility, overridden by editor grid
32342     stopEditing : function(){},
32343
32344     /**
32345      * Returns the grid's SelectionModel.
32346      * @return {SelectionModel}
32347      */
32348     getSelectionModel : function(){
32349         if(!this.selModel){
32350             this.selModel = new Roo.grid.RowSelectionModel();
32351         }
32352         return this.selModel;
32353     },
32354
32355     /**
32356      * Returns the grid's DataSource.
32357      * @return {DataSource}
32358      */
32359     getDataSource : function(){
32360         return this.dataSource;
32361     },
32362
32363     /**
32364      * Returns the grid's ColumnModel.
32365      * @return {ColumnModel}
32366      */
32367     getColumnModel : function(){
32368         return this.colModel;
32369     },
32370
32371     /**
32372      * Returns the grid's GridView object.
32373      * @return {GridView}
32374      */
32375     getView : function(){
32376         if(!this.view){
32377             this.view = new Roo.grid.GridView(this.viewConfig);
32378         }
32379         return this.view;
32380     },
32381     /**
32382      * Called to get grid's drag proxy text, by default returns this.ddText.
32383      * @return {String}
32384      */
32385     getDragDropText : function(){
32386         var count = this.selModel.getCount();
32387         return String.format(this.ddText, count, count == 1 ? '' : 's');
32388     }
32389 });
32390 /**
32391  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32392  * %0 is replaced with the number of selected rows.
32393  * @type String
32394  */
32395 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32396  * Based on:
32397  * Ext JS Library 1.1.1
32398  * Copyright(c) 2006-2007, Ext JS, LLC.
32399  *
32400  * Originally Released Under LGPL - original licence link has changed is not relivant.
32401  *
32402  * Fork - LGPL
32403  * <script type="text/javascript">
32404  */
32405  
32406 Roo.grid.AbstractGridView = function(){
32407         this.grid = null;
32408         
32409         this.events = {
32410             "beforerowremoved" : true,
32411             "beforerowsinserted" : true,
32412             "beforerefresh" : true,
32413             "rowremoved" : true,
32414             "rowsinserted" : true,
32415             "rowupdated" : true,
32416             "refresh" : true
32417         };
32418     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32419 };
32420
32421 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32422     rowClass : "x-grid-row",
32423     cellClass : "x-grid-cell",
32424     tdClass : "x-grid-td",
32425     hdClass : "x-grid-hd",
32426     splitClass : "x-grid-hd-split",
32427     
32428     init: function(grid){
32429         this.grid = grid;
32430                 var cid = this.grid.getGridEl().id;
32431         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32432         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32433         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32434         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32435         },
32436         
32437     getColumnRenderers : function(){
32438         var renderers = [];
32439         var cm = this.grid.colModel;
32440         var colCount = cm.getColumnCount();
32441         for(var i = 0; i < colCount; i++){
32442             renderers[i] = cm.getRenderer(i);
32443         }
32444         return renderers;
32445     },
32446     
32447     getColumnIds : function(){
32448         var ids = [];
32449         var cm = this.grid.colModel;
32450         var colCount = cm.getColumnCount();
32451         for(var i = 0; i < colCount; i++){
32452             ids[i] = cm.getColumnId(i);
32453         }
32454         return ids;
32455     },
32456     
32457     getDataIndexes : function(){
32458         if(!this.indexMap){
32459             this.indexMap = this.buildIndexMap();
32460         }
32461         return this.indexMap.colToData;
32462     },
32463     
32464     getColumnIndexByDataIndex : function(dataIndex){
32465         if(!this.indexMap){
32466             this.indexMap = this.buildIndexMap();
32467         }
32468         return this.indexMap.dataToCol[dataIndex];
32469     },
32470     
32471     /**
32472      * Set a css style for a column dynamically. 
32473      * @param {Number} colIndex The index of the column
32474      * @param {String} name The css property name
32475      * @param {String} value The css value
32476      */
32477     setCSSStyle : function(colIndex, name, value){
32478         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32479         Roo.util.CSS.updateRule(selector, name, value);
32480     },
32481     
32482     generateRules : function(cm){
32483         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32484         Roo.util.CSS.removeStyleSheet(rulesId);
32485         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32486             var cid = cm.getColumnId(i);
32487             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32488                          this.tdSelector, cid, " {\n}\n",
32489                          this.hdSelector, cid, " {\n}\n",
32490                          this.splitSelector, cid, " {\n}\n");
32491         }
32492         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32493     }
32494 });/*
32495  * Based on:
32496  * Ext JS Library 1.1.1
32497  * Copyright(c) 2006-2007, Ext JS, LLC.
32498  *
32499  * Originally Released Under LGPL - original licence link has changed is not relivant.
32500  *
32501  * Fork - LGPL
32502  * <script type="text/javascript">
32503  */
32504
32505 // private
32506 // This is a support class used internally by the Grid components
32507 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32508     this.grid = grid;
32509     this.view = grid.getView();
32510     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32511     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32512     if(hd2){
32513         this.setHandleElId(Roo.id(hd));
32514         this.setOuterHandleElId(Roo.id(hd2));
32515     }
32516     this.scroll = false;
32517 };
32518 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32519     maxDragWidth: 120,
32520     getDragData : function(e){
32521         var t = Roo.lib.Event.getTarget(e);
32522         var h = this.view.findHeaderCell(t);
32523         if(h){
32524             return {ddel: h.firstChild, header:h};
32525         }
32526         return false;
32527     },
32528
32529     onInitDrag : function(e){
32530         this.view.headersDisabled = true;
32531         var clone = this.dragData.ddel.cloneNode(true);
32532         clone.id = Roo.id();
32533         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32534         this.proxy.update(clone);
32535         return true;
32536     },
32537
32538     afterValidDrop : function(){
32539         var v = this.view;
32540         setTimeout(function(){
32541             v.headersDisabled = false;
32542         }, 50);
32543     },
32544
32545     afterInvalidDrop : function(){
32546         var v = this.view;
32547         setTimeout(function(){
32548             v.headersDisabled = false;
32549         }, 50);
32550     }
32551 });
32552 /*
32553  * Based on:
32554  * Ext JS Library 1.1.1
32555  * Copyright(c) 2006-2007, Ext JS, LLC.
32556  *
32557  * Originally Released Under LGPL - original licence link has changed is not relivant.
32558  *
32559  * Fork - LGPL
32560  * <script type="text/javascript">
32561  */
32562 // private
32563 // This is a support class used internally by the Grid components
32564 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32565     this.grid = grid;
32566     this.view = grid.getView();
32567     // split the proxies so they don't interfere with mouse events
32568     this.proxyTop = Roo.DomHelper.append(document.body, {
32569         cls:"col-move-top", html:"&#160;"
32570     }, true);
32571     this.proxyBottom = Roo.DomHelper.append(document.body, {
32572         cls:"col-move-bottom", html:"&#160;"
32573     }, true);
32574     this.proxyTop.hide = this.proxyBottom.hide = function(){
32575         this.setLeftTop(-100,-100);
32576         this.setStyle("visibility", "hidden");
32577     };
32578     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32579     // temporarily disabled
32580     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32581     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32582 };
32583 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32584     proxyOffsets : [-4, -9],
32585     fly: Roo.Element.fly,
32586
32587     getTargetFromEvent : function(e){
32588         var t = Roo.lib.Event.getTarget(e);
32589         var cindex = this.view.findCellIndex(t);
32590         if(cindex !== false){
32591             return this.view.getHeaderCell(cindex);
32592         }
32593         return null;
32594     },
32595
32596     nextVisible : function(h){
32597         var v = this.view, cm = this.grid.colModel;
32598         h = h.nextSibling;
32599         while(h){
32600             if(!cm.isHidden(v.getCellIndex(h))){
32601                 return h;
32602             }
32603             h = h.nextSibling;
32604         }
32605         return null;
32606     },
32607
32608     prevVisible : function(h){
32609         var v = this.view, cm = this.grid.colModel;
32610         h = h.prevSibling;
32611         while(h){
32612             if(!cm.isHidden(v.getCellIndex(h))){
32613                 return h;
32614             }
32615             h = h.prevSibling;
32616         }
32617         return null;
32618     },
32619
32620     positionIndicator : function(h, n, e){
32621         var x = Roo.lib.Event.getPageX(e);
32622         var r = Roo.lib.Dom.getRegion(n.firstChild);
32623         var px, pt, py = r.top + this.proxyOffsets[1];
32624         if((r.right - x) <= (r.right-r.left)/2){
32625             px = r.right+this.view.borderWidth;
32626             pt = "after";
32627         }else{
32628             px = r.left;
32629             pt = "before";
32630         }
32631         var oldIndex = this.view.getCellIndex(h);
32632         var newIndex = this.view.getCellIndex(n);
32633
32634         if(this.grid.colModel.isFixed(newIndex)){
32635             return false;
32636         }
32637
32638         var locked = this.grid.colModel.isLocked(newIndex);
32639
32640         if(pt == "after"){
32641             newIndex++;
32642         }
32643         if(oldIndex < newIndex){
32644             newIndex--;
32645         }
32646         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32647             return false;
32648         }
32649         px +=  this.proxyOffsets[0];
32650         this.proxyTop.setLeftTop(px, py);
32651         this.proxyTop.show();
32652         if(!this.bottomOffset){
32653             this.bottomOffset = this.view.mainHd.getHeight();
32654         }
32655         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32656         this.proxyBottom.show();
32657         return pt;
32658     },
32659
32660     onNodeEnter : function(n, dd, e, data){
32661         if(data.header != n){
32662             this.positionIndicator(data.header, n, e);
32663         }
32664     },
32665
32666     onNodeOver : function(n, dd, e, data){
32667         var result = false;
32668         if(data.header != n){
32669             result = this.positionIndicator(data.header, n, e);
32670         }
32671         if(!result){
32672             this.proxyTop.hide();
32673             this.proxyBottom.hide();
32674         }
32675         return result ? this.dropAllowed : this.dropNotAllowed;
32676     },
32677
32678     onNodeOut : function(n, dd, e, data){
32679         this.proxyTop.hide();
32680         this.proxyBottom.hide();
32681     },
32682
32683     onNodeDrop : function(n, dd, e, data){
32684         var h = data.header;
32685         if(h != n){
32686             var cm = this.grid.colModel;
32687             var x = Roo.lib.Event.getPageX(e);
32688             var r = Roo.lib.Dom.getRegion(n.firstChild);
32689             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32690             var oldIndex = this.view.getCellIndex(h);
32691             var newIndex = this.view.getCellIndex(n);
32692             var locked = cm.isLocked(newIndex);
32693             if(pt == "after"){
32694                 newIndex++;
32695             }
32696             if(oldIndex < newIndex){
32697                 newIndex--;
32698             }
32699             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32700                 return false;
32701             }
32702             cm.setLocked(oldIndex, locked, true);
32703             cm.moveColumn(oldIndex, newIndex);
32704             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32705             return true;
32706         }
32707         return false;
32708     }
32709 });
32710 /*
32711  * Based on:
32712  * Ext JS Library 1.1.1
32713  * Copyright(c) 2006-2007, Ext JS, LLC.
32714  *
32715  * Originally Released Under LGPL - original licence link has changed is not relivant.
32716  *
32717  * Fork - LGPL
32718  * <script type="text/javascript">
32719  */
32720   
32721 /**
32722  * @class Roo.grid.GridView
32723  * @extends Roo.util.Observable
32724  *
32725  * @constructor
32726  * @param {Object} config
32727  */
32728 Roo.grid.GridView = function(config){
32729     Roo.grid.GridView.superclass.constructor.call(this);
32730     this.el = null;
32731
32732     Roo.apply(this, config);
32733 };
32734
32735 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32736
32737     unselectable :  'unselectable="on"',
32738     unselectableCls :  'x-unselectable',
32739     
32740     
32741     rowClass : "x-grid-row",
32742
32743     cellClass : "x-grid-col",
32744
32745     tdClass : "x-grid-td",
32746
32747     hdClass : "x-grid-hd",
32748
32749     splitClass : "x-grid-split",
32750
32751     sortClasses : ["sort-asc", "sort-desc"],
32752
32753     enableMoveAnim : false,
32754
32755     hlColor: "C3DAF9",
32756
32757     dh : Roo.DomHelper,
32758
32759     fly : Roo.Element.fly,
32760
32761     css : Roo.util.CSS,
32762
32763     borderWidth: 1,
32764
32765     splitOffset: 3,
32766
32767     scrollIncrement : 22,
32768
32769     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32770
32771     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32772
32773     bind : function(ds, cm){
32774         if(this.ds){
32775             this.ds.un("load", this.onLoad, this);
32776             this.ds.un("datachanged", this.onDataChange, this);
32777             this.ds.un("add", this.onAdd, this);
32778             this.ds.un("remove", this.onRemove, this);
32779             this.ds.un("update", this.onUpdate, this);
32780             this.ds.un("clear", this.onClear, this);
32781         }
32782         if(ds){
32783             ds.on("load", this.onLoad, this);
32784             ds.on("datachanged", this.onDataChange, this);
32785             ds.on("add", this.onAdd, this);
32786             ds.on("remove", this.onRemove, this);
32787             ds.on("update", this.onUpdate, this);
32788             ds.on("clear", this.onClear, this);
32789         }
32790         this.ds = ds;
32791
32792         if(this.cm){
32793             this.cm.un("widthchange", this.onColWidthChange, this);
32794             this.cm.un("headerchange", this.onHeaderChange, this);
32795             this.cm.un("hiddenchange", this.onHiddenChange, this);
32796             this.cm.un("columnmoved", this.onColumnMove, this);
32797             this.cm.un("columnlockchange", this.onColumnLock, this);
32798         }
32799         if(cm){
32800             this.generateRules(cm);
32801             cm.on("widthchange", this.onColWidthChange, this);
32802             cm.on("headerchange", this.onHeaderChange, this);
32803             cm.on("hiddenchange", this.onHiddenChange, this);
32804             cm.on("columnmoved", this.onColumnMove, this);
32805             cm.on("columnlockchange", this.onColumnLock, this);
32806         }
32807         this.cm = cm;
32808     },
32809
32810     init: function(grid){
32811         Roo.grid.GridView.superclass.init.call(this, grid);
32812
32813         this.bind(grid.dataSource, grid.colModel);
32814
32815         grid.on("headerclick", this.handleHeaderClick, this);
32816
32817         if(grid.trackMouseOver){
32818             grid.on("mouseover", this.onRowOver, this);
32819             grid.on("mouseout", this.onRowOut, this);
32820         }
32821         grid.cancelTextSelection = function(){};
32822         this.gridId = grid.id;
32823
32824         var tpls = this.templates || {};
32825
32826         if(!tpls.master){
32827             tpls.master = new Roo.Template(
32828                '<div class="x-grid" hidefocus="true">',
32829                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32830                   '<div class="x-grid-topbar"></div>',
32831                   '<div class="x-grid-scroller"><div></div></div>',
32832                   '<div class="x-grid-locked">',
32833                       '<div class="x-grid-header">{lockedHeader}</div>',
32834                       '<div class="x-grid-body">{lockedBody}</div>',
32835                   "</div>",
32836                   '<div class="x-grid-viewport">',
32837                       '<div class="x-grid-header">{header}</div>',
32838                       '<div class="x-grid-body">{body}</div>',
32839                   "</div>",
32840                   '<div class="x-grid-bottombar"></div>',
32841                  
32842                   '<div class="x-grid-resize-proxy">&#160;</div>',
32843                "</div>"
32844             );
32845             tpls.master.disableformats = true;
32846         }
32847
32848         if(!tpls.header){
32849             tpls.header = new Roo.Template(
32850                '<table border="0" cellspacing="0" cellpadding="0">',
32851                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32852                "</table>{splits}"
32853             );
32854             tpls.header.disableformats = true;
32855         }
32856         tpls.header.compile();
32857
32858         if(!tpls.hcell){
32859             tpls.hcell = new Roo.Template(
32860                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32861                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32862                 "</div></td>"
32863              );
32864              tpls.hcell.disableFormats = true;
32865         }
32866         tpls.hcell.compile();
32867
32868         if(!tpls.hsplit){
32869             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
32870                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
32871             tpls.hsplit.disableFormats = true;
32872         }
32873         tpls.hsplit.compile();
32874
32875         if(!tpls.body){
32876             tpls.body = new Roo.Template(
32877                '<table border="0" cellspacing="0" cellpadding="0">',
32878                "<tbody>{rows}</tbody>",
32879                "</table>"
32880             );
32881             tpls.body.disableFormats = true;
32882         }
32883         tpls.body.compile();
32884
32885         if(!tpls.row){
32886             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32887             tpls.row.disableFormats = true;
32888         }
32889         tpls.row.compile();
32890
32891         if(!tpls.cell){
32892             tpls.cell = new Roo.Template(
32893                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32894                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
32895                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
32896                 "</td>"
32897             );
32898             tpls.cell.disableFormats = true;
32899         }
32900         tpls.cell.compile();
32901
32902         this.templates = tpls;
32903     },
32904
32905     // remap these for backwards compat
32906     onColWidthChange : function(){
32907         this.updateColumns.apply(this, arguments);
32908     },
32909     onHeaderChange : function(){
32910         this.updateHeaders.apply(this, arguments);
32911     }, 
32912     onHiddenChange : function(){
32913         this.handleHiddenChange.apply(this, arguments);
32914     },
32915     onColumnMove : function(){
32916         this.handleColumnMove.apply(this, arguments);
32917     },
32918     onColumnLock : function(){
32919         this.handleLockChange.apply(this, arguments);
32920     },
32921
32922     onDataChange : function(){
32923         this.refresh();
32924         this.updateHeaderSortState();
32925     },
32926
32927     onClear : function(){
32928         this.refresh();
32929     },
32930
32931     onUpdate : function(ds, record){
32932         this.refreshRow(record);
32933     },
32934
32935     refreshRow : function(record){
32936         var ds = this.ds, index;
32937         if(typeof record == 'number'){
32938             index = record;
32939             record = ds.getAt(index);
32940         }else{
32941             index = ds.indexOf(record);
32942         }
32943         this.insertRows(ds, index, index, true);
32944         this.onRemove(ds, record, index+1, true);
32945         this.syncRowHeights(index, index);
32946         this.layout();
32947         this.fireEvent("rowupdated", this, index, record);
32948     },
32949
32950     onAdd : function(ds, records, index){
32951         this.insertRows(ds, index, index + (records.length-1));
32952     },
32953
32954     onRemove : function(ds, record, index, isUpdate){
32955         if(isUpdate !== true){
32956             this.fireEvent("beforerowremoved", this, index, record);
32957         }
32958         var bt = this.getBodyTable(), lt = this.getLockedTable();
32959         if(bt.rows[index]){
32960             bt.firstChild.removeChild(bt.rows[index]);
32961         }
32962         if(lt.rows[index]){
32963             lt.firstChild.removeChild(lt.rows[index]);
32964         }
32965         if(isUpdate !== true){
32966             this.stripeRows(index);
32967             this.syncRowHeights(index, index);
32968             this.layout();
32969             this.fireEvent("rowremoved", this, index, record);
32970         }
32971     },
32972
32973     onLoad : function(){
32974         this.scrollToTop();
32975     },
32976
32977     /**
32978      * Scrolls the grid to the top
32979      */
32980     scrollToTop : function(){
32981         if(this.scroller){
32982             this.scroller.dom.scrollTop = 0;
32983             this.syncScroll();
32984         }
32985     },
32986
32987     /**
32988      * Gets a panel in the header of the grid that can be used for toolbars etc.
32989      * After modifying the contents of this panel a call to grid.autoSize() may be
32990      * required to register any changes in size.
32991      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32992      * @return Roo.Element
32993      */
32994     getHeaderPanel : function(doShow){
32995         if(doShow){
32996             this.headerPanel.show();
32997         }
32998         return this.headerPanel;
32999     },
33000
33001     /**
33002      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33003      * After modifying the contents of this panel a call to grid.autoSize() may be
33004      * required to register any changes in size.
33005      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33006      * @return Roo.Element
33007      */
33008     getFooterPanel : function(doShow){
33009         if(doShow){
33010             this.footerPanel.show();
33011         }
33012         return this.footerPanel;
33013     },
33014
33015     initElements : function(){
33016         var E = Roo.Element;
33017         var el = this.grid.getGridEl().dom.firstChild;
33018         var cs = el.childNodes;
33019
33020         this.el = new E(el);
33021         
33022          this.focusEl = new E(el.firstChild);
33023         this.focusEl.swallowEvent("click", true);
33024         
33025         this.headerPanel = new E(cs[1]);
33026         this.headerPanel.enableDisplayMode("block");
33027
33028         this.scroller = new E(cs[2]);
33029         this.scrollSizer = new E(this.scroller.dom.firstChild);
33030
33031         this.lockedWrap = new E(cs[3]);
33032         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33033         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33034
33035         this.mainWrap = new E(cs[4]);
33036         this.mainHd = new E(this.mainWrap.dom.firstChild);
33037         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33038
33039         this.footerPanel = new E(cs[5]);
33040         this.footerPanel.enableDisplayMode("block");
33041
33042         this.resizeProxy = new E(cs[6]);
33043
33044         this.headerSelector = String.format(
33045            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33046            this.lockedHd.id, this.mainHd.id
33047         );
33048
33049         this.splitterSelector = String.format(
33050            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33051            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33052         );
33053     },
33054     idToCssName : function(s)
33055     {
33056         return s.replace(/[^a-z0-9]+/ig, '-');
33057     },
33058
33059     getHeaderCell : function(index){
33060         return Roo.DomQuery.select(this.headerSelector)[index];
33061     },
33062
33063     getHeaderCellMeasure : function(index){
33064         return this.getHeaderCell(index).firstChild;
33065     },
33066
33067     getHeaderCellText : function(index){
33068         return this.getHeaderCell(index).firstChild.firstChild;
33069     },
33070
33071     getLockedTable : function(){
33072         return this.lockedBody.dom.firstChild;
33073     },
33074
33075     getBodyTable : function(){
33076         return this.mainBody.dom.firstChild;
33077     },
33078
33079     getLockedRow : function(index){
33080         return this.getLockedTable().rows[index];
33081     },
33082
33083     getRow : function(index){
33084         return this.getBodyTable().rows[index];
33085     },
33086
33087     getRowComposite : function(index){
33088         if(!this.rowEl){
33089             this.rowEl = new Roo.CompositeElementLite();
33090         }
33091         var els = [], lrow, mrow;
33092         if(lrow = this.getLockedRow(index)){
33093             els.push(lrow);
33094         }
33095         if(mrow = this.getRow(index)){
33096             els.push(mrow);
33097         }
33098         this.rowEl.elements = els;
33099         return this.rowEl;
33100     },
33101     /**
33102      * Gets the 'td' of the cell
33103      * 
33104      * @param {Integer} rowIndex row to select
33105      * @param {Integer} colIndex column to select
33106      * 
33107      * @return {Object} 
33108      */
33109     getCell : function(rowIndex, colIndex){
33110         var locked = this.cm.getLockedCount();
33111         var source;
33112         if(colIndex < locked){
33113             source = this.lockedBody.dom.firstChild;
33114         }else{
33115             source = this.mainBody.dom.firstChild;
33116             colIndex -= locked;
33117         }
33118         return source.rows[rowIndex].childNodes[colIndex];
33119     },
33120
33121     getCellText : function(rowIndex, colIndex){
33122         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33123     },
33124
33125     getCellBox : function(cell){
33126         var b = this.fly(cell).getBox();
33127         if(Roo.isOpera){ // opera fails to report the Y
33128             b.y = cell.offsetTop + this.mainBody.getY();
33129         }
33130         return b;
33131     },
33132
33133     getCellIndex : function(cell){
33134         var id = String(cell.className).match(this.cellRE);
33135         if(id){
33136             return parseInt(id[1], 10);
33137         }
33138         return 0;
33139     },
33140
33141     findHeaderIndex : function(n){
33142         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33143         return r ? this.getCellIndex(r) : false;
33144     },
33145
33146     findHeaderCell : function(n){
33147         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33148         return r ? r : false;
33149     },
33150
33151     findRowIndex : function(n){
33152         if(!n){
33153             return false;
33154         }
33155         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33156         return r ? r.rowIndex : false;
33157     },
33158
33159     findCellIndex : function(node){
33160         var stop = this.el.dom;
33161         while(node && node != stop){
33162             if(this.findRE.test(node.className)){
33163                 return this.getCellIndex(node);
33164             }
33165             node = node.parentNode;
33166         }
33167         return false;
33168     },
33169
33170     getColumnId : function(index){
33171         return this.cm.getColumnId(index);
33172     },
33173
33174     getSplitters : function()
33175     {
33176         if(this.splitterSelector){
33177            return Roo.DomQuery.select(this.splitterSelector);
33178         }else{
33179             return null;
33180       }
33181     },
33182
33183     getSplitter : function(index){
33184         return this.getSplitters()[index];
33185     },
33186
33187     onRowOver : function(e, t){
33188         var row;
33189         if((row = this.findRowIndex(t)) !== false){
33190             this.getRowComposite(row).addClass("x-grid-row-over");
33191         }
33192     },
33193
33194     onRowOut : function(e, t){
33195         var row;
33196         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33197             this.getRowComposite(row).removeClass("x-grid-row-over");
33198         }
33199     },
33200
33201     renderHeaders : function(){
33202         var cm = this.cm;
33203         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33204         var cb = [], lb = [], sb = [], lsb = [], p = {};
33205         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33206             p.cellId = "x-grid-hd-0-" + i;
33207             p.splitId = "x-grid-csplit-0-" + i;
33208             p.id = cm.getColumnId(i);
33209             p.value = cm.getColumnHeader(i) || "";
33210             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33211             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33212             if(!cm.isLocked(i)){
33213                 cb[cb.length] = ct.apply(p);
33214                 sb[sb.length] = st.apply(p);
33215             }else{
33216                 lb[lb.length] = ct.apply(p);
33217                 lsb[lsb.length] = st.apply(p);
33218             }
33219         }
33220         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33221                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33222     },
33223
33224     updateHeaders : function(){
33225         var html = this.renderHeaders();
33226         this.lockedHd.update(html[0]);
33227         this.mainHd.update(html[1]);
33228     },
33229
33230     /**
33231      * Focuses the specified row.
33232      * @param {Number} row The row index
33233      */
33234     focusRow : function(row)
33235     {
33236         //Roo.log('GridView.focusRow');
33237         var x = this.scroller.dom.scrollLeft;
33238         this.focusCell(row, 0, false);
33239         this.scroller.dom.scrollLeft = x;
33240     },
33241
33242     /**
33243      * Focuses the specified cell.
33244      * @param {Number} row The row index
33245      * @param {Number} col The column index
33246      * @param {Boolean} hscroll false to disable horizontal scrolling
33247      */
33248     focusCell : function(row, col, hscroll)
33249     {
33250         //Roo.log('GridView.focusCell');
33251         var el = this.ensureVisible(row, col, hscroll);
33252         this.focusEl.alignTo(el, "tl-tl");
33253         if(Roo.isGecko){
33254             this.focusEl.focus();
33255         }else{
33256             this.focusEl.focus.defer(1, this.focusEl);
33257         }
33258     },
33259
33260     /**
33261      * Scrolls the specified cell into view
33262      * @param {Number} row The row index
33263      * @param {Number} col The column index
33264      * @param {Boolean} hscroll false to disable horizontal scrolling
33265      */
33266     ensureVisible : function(row, col, hscroll)
33267     {
33268         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33269         //return null; //disable for testing.
33270         if(typeof row != "number"){
33271             row = row.rowIndex;
33272         }
33273         if(row < 0 && row >= this.ds.getCount()){
33274             return  null;
33275         }
33276         col = (col !== undefined ? col : 0);
33277         var cm = this.grid.colModel;
33278         while(cm.isHidden(col)){
33279             col++;
33280         }
33281
33282         var el = this.getCell(row, col);
33283         if(!el){
33284             return null;
33285         }
33286         var c = this.scroller.dom;
33287
33288         var ctop = parseInt(el.offsetTop, 10);
33289         var cleft = parseInt(el.offsetLeft, 10);
33290         var cbot = ctop + el.offsetHeight;
33291         var cright = cleft + el.offsetWidth;
33292         
33293         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33294         var stop = parseInt(c.scrollTop, 10);
33295         var sleft = parseInt(c.scrollLeft, 10);
33296         var sbot = stop + ch;
33297         var sright = sleft + c.clientWidth;
33298         /*
33299         Roo.log('GridView.ensureVisible:' +
33300                 ' ctop:' + ctop +
33301                 ' c.clientHeight:' + c.clientHeight +
33302                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33303                 ' stop:' + stop +
33304                 ' cbot:' + cbot +
33305                 ' sbot:' + sbot +
33306                 ' ch:' + ch  
33307                 );
33308         */
33309         if(ctop < stop){
33310              c.scrollTop = ctop;
33311             //Roo.log("set scrolltop to ctop DISABLE?");
33312         }else if(cbot > sbot){
33313             //Roo.log("set scrolltop to cbot-ch");
33314             c.scrollTop = cbot-ch;
33315         }
33316         
33317         if(hscroll !== false){
33318             if(cleft < sleft){
33319                 c.scrollLeft = cleft;
33320             }else if(cright > sright){
33321                 c.scrollLeft = cright-c.clientWidth;
33322             }
33323         }
33324          
33325         return el;
33326     },
33327
33328     updateColumns : function(){
33329         this.grid.stopEditing();
33330         var cm = this.grid.colModel, colIds = this.getColumnIds();
33331         //var totalWidth = cm.getTotalWidth();
33332         var pos = 0;
33333         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33334             //if(cm.isHidden(i)) continue;
33335             var w = cm.getColumnWidth(i);
33336             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33337             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33338         }
33339         this.updateSplitters();
33340     },
33341
33342     generateRules : function(cm){
33343         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33344         Roo.util.CSS.removeStyleSheet(rulesId);
33345         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33346             var cid = cm.getColumnId(i);
33347             var align = '';
33348             if(cm.config[i].align){
33349                 align = 'text-align:'+cm.config[i].align+';';
33350             }
33351             var hidden = '';
33352             if(cm.isHidden(i)){
33353                 hidden = 'display:none;';
33354             }
33355             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33356             ruleBuf.push(
33357                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33358                     this.hdSelector, cid, " {\n", align, width, "}\n",
33359                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33360                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33361         }
33362         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33363     },
33364
33365     updateSplitters : function(){
33366         var cm = this.cm, s = this.getSplitters();
33367         if(s){ // splitters not created yet
33368             var pos = 0, locked = true;
33369             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33370                 if(cm.isHidden(i)) {
33371                     continue;
33372                 }
33373                 var w = cm.getColumnWidth(i); // make sure it's a number
33374                 if(!cm.isLocked(i) && locked){
33375                     pos = 0;
33376                     locked = false;
33377                 }
33378                 pos += w;
33379                 s[i].style.left = (pos-this.splitOffset) + "px";
33380             }
33381         }
33382     },
33383
33384     handleHiddenChange : function(colModel, colIndex, hidden){
33385         if(hidden){
33386             this.hideColumn(colIndex);
33387         }else{
33388             this.unhideColumn(colIndex);
33389         }
33390     },
33391
33392     hideColumn : function(colIndex){
33393         var cid = this.getColumnId(colIndex);
33394         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33395         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33396         if(Roo.isSafari){
33397             this.updateHeaders();
33398         }
33399         this.updateSplitters();
33400         this.layout();
33401     },
33402
33403     unhideColumn : function(colIndex){
33404         var cid = this.getColumnId(colIndex);
33405         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33406         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33407
33408         if(Roo.isSafari){
33409             this.updateHeaders();
33410         }
33411         this.updateSplitters();
33412         this.layout();
33413     },
33414
33415     insertRows : function(dm, firstRow, lastRow, isUpdate){
33416         if(firstRow == 0 && lastRow == dm.getCount()-1){
33417             this.refresh();
33418         }else{
33419             if(!isUpdate){
33420                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33421             }
33422             var s = this.getScrollState();
33423             var markup = this.renderRows(firstRow, lastRow);
33424             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33425             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33426             this.restoreScroll(s);
33427             if(!isUpdate){
33428                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33429                 this.syncRowHeights(firstRow, lastRow);
33430                 this.stripeRows(firstRow);
33431                 this.layout();
33432             }
33433         }
33434     },
33435
33436     bufferRows : function(markup, target, index){
33437         var before = null, trows = target.rows, tbody = target.tBodies[0];
33438         if(index < trows.length){
33439             before = trows[index];
33440         }
33441         var b = document.createElement("div");
33442         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33443         var rows = b.firstChild.rows;
33444         for(var i = 0, len = rows.length; i < len; i++){
33445             if(before){
33446                 tbody.insertBefore(rows[0], before);
33447             }else{
33448                 tbody.appendChild(rows[0]);
33449             }
33450         }
33451         b.innerHTML = "";
33452         b = null;
33453     },
33454
33455     deleteRows : function(dm, firstRow, lastRow){
33456         if(dm.getRowCount()<1){
33457             this.fireEvent("beforerefresh", this);
33458             this.mainBody.update("");
33459             this.lockedBody.update("");
33460             this.fireEvent("refresh", this);
33461         }else{
33462             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33463             var bt = this.getBodyTable();
33464             var tbody = bt.firstChild;
33465             var rows = bt.rows;
33466             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33467                 tbody.removeChild(rows[firstRow]);
33468             }
33469             this.stripeRows(firstRow);
33470             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33471         }
33472     },
33473
33474     updateRows : function(dataSource, firstRow, lastRow){
33475         var s = this.getScrollState();
33476         this.refresh();
33477         this.restoreScroll(s);
33478     },
33479
33480     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33481         if(!noRefresh){
33482            this.refresh();
33483         }
33484         this.updateHeaderSortState();
33485     },
33486
33487     getScrollState : function(){
33488         
33489         var sb = this.scroller.dom;
33490         return {left: sb.scrollLeft, top: sb.scrollTop};
33491     },
33492
33493     stripeRows : function(startRow){
33494         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33495             return;
33496         }
33497         startRow = startRow || 0;
33498         var rows = this.getBodyTable().rows;
33499         var lrows = this.getLockedTable().rows;
33500         var cls = ' x-grid-row-alt ';
33501         for(var i = startRow, len = rows.length; i < len; i++){
33502             var row = rows[i], lrow = lrows[i];
33503             var isAlt = ((i+1) % 2 == 0);
33504             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33505             if(isAlt == hasAlt){
33506                 continue;
33507             }
33508             if(isAlt){
33509                 row.className += " x-grid-row-alt";
33510             }else{
33511                 row.className = row.className.replace("x-grid-row-alt", "");
33512             }
33513             if(lrow){
33514                 lrow.className = row.className;
33515             }
33516         }
33517     },
33518
33519     restoreScroll : function(state){
33520         //Roo.log('GridView.restoreScroll');
33521         var sb = this.scroller.dom;
33522         sb.scrollLeft = state.left;
33523         sb.scrollTop = state.top;
33524         this.syncScroll();
33525     },
33526
33527     syncScroll : function(){
33528         //Roo.log('GridView.syncScroll');
33529         var sb = this.scroller.dom;
33530         var sh = this.mainHd.dom;
33531         var bs = this.mainBody.dom;
33532         var lv = this.lockedBody.dom;
33533         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33534         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33535     },
33536
33537     handleScroll : function(e){
33538         this.syncScroll();
33539         var sb = this.scroller.dom;
33540         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33541         e.stopEvent();
33542     },
33543
33544     handleWheel : function(e){
33545         var d = e.getWheelDelta();
33546         this.scroller.dom.scrollTop -= d*22;
33547         // set this here to prevent jumpy scrolling on large tables
33548         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33549         e.stopEvent();
33550     },
33551
33552     renderRows : function(startRow, endRow){
33553         // pull in all the crap needed to render rows
33554         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33555         var colCount = cm.getColumnCount();
33556
33557         if(ds.getCount() < 1){
33558             return ["", ""];
33559         }
33560
33561         // build a map for all the columns
33562         var cs = [];
33563         for(var i = 0; i < colCount; i++){
33564             var name = cm.getDataIndex(i);
33565             cs[i] = {
33566                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33567                 renderer : cm.getRenderer(i),
33568                 id : cm.getColumnId(i),
33569                 locked : cm.isLocked(i),
33570                 has_editor : cm.isCellEditable(i)
33571             };
33572         }
33573
33574         startRow = startRow || 0;
33575         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33576
33577         // records to render
33578         var rs = ds.getRange(startRow, endRow);
33579
33580         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33581     },
33582
33583     // As much as I hate to duplicate code, this was branched because FireFox really hates
33584     // [].join("") on strings. The performance difference was substantial enough to
33585     // branch this function
33586     doRender : Roo.isGecko ?
33587             function(cs, rs, ds, startRow, colCount, stripe){
33588                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33589                 // buffers
33590                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33591                 
33592                 var hasListener = this.grid.hasListener('rowclass');
33593                 var rowcfg = {};
33594                 for(var j = 0, len = rs.length; j < len; j++){
33595                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33596                     for(var i = 0; i < colCount; i++){
33597                         c = cs[i];
33598                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33599                         p.id = c.id;
33600                         p.css = p.attr = "";
33601                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33602                         if(p.value == undefined || p.value === "") {
33603                             p.value = "&#160;";
33604                         }
33605                         if(c.has_editor){
33606                             p.css += ' x-grid-editable-cell';
33607                         }
33608                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33609                             p.css +=  ' x-grid-dirty-cell';
33610                         }
33611                         var markup = ct.apply(p);
33612                         if(!c.locked){
33613                             cb+= markup;
33614                         }else{
33615                             lcb+= markup;
33616                         }
33617                     }
33618                     var alt = [];
33619                     if(stripe && ((rowIndex+1) % 2 == 0)){
33620                         alt.push("x-grid-row-alt")
33621                     }
33622                     if(r.dirty){
33623                         alt.push(  " x-grid-dirty-row");
33624                     }
33625                     rp.cells = lcb;
33626                     if(this.getRowClass){
33627                         alt.push(this.getRowClass(r, rowIndex));
33628                     }
33629                     if (hasListener) {
33630                         rowcfg = {
33631                              
33632                             record: r,
33633                             rowIndex : rowIndex,
33634                             rowClass : ''
33635                         };
33636                         this.grid.fireEvent('rowclass', this, rowcfg);
33637                         alt.push(rowcfg.rowClass);
33638                     }
33639                     rp.alt = alt.join(" ");
33640                     lbuf+= rt.apply(rp);
33641                     rp.cells = cb;
33642                     buf+=  rt.apply(rp);
33643                 }
33644                 return [lbuf, buf];
33645             } :
33646             function(cs, rs, ds, startRow, colCount, stripe){
33647                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33648                 // buffers
33649                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33650                 var hasListener = this.grid.hasListener('rowclass');
33651  
33652                 var rowcfg = {};
33653                 for(var j = 0, len = rs.length; j < len; j++){
33654                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33655                     for(var i = 0; i < colCount; i++){
33656                         c = cs[i];
33657                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33658                         p.id = c.id;
33659                         p.css = p.attr = "";
33660                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33661                         if(p.value == undefined || p.value === "") {
33662                             p.value = "&#160;";
33663                         }
33664                         //Roo.log(c);
33665                          if(c.has_editor){
33666                             p.css += ' x-grid-editable-cell';
33667                         }
33668                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33669                             p.css += ' x-grid-dirty-cell' 
33670                         }
33671                         
33672                         var markup = ct.apply(p);
33673                         if(!c.locked){
33674                             cb[cb.length] = markup;
33675                         }else{
33676                             lcb[lcb.length] = markup;
33677                         }
33678                     }
33679                     var alt = [];
33680                     if(stripe && ((rowIndex+1) % 2 == 0)){
33681                         alt.push( "x-grid-row-alt");
33682                     }
33683                     if(r.dirty){
33684                         alt.push(" x-grid-dirty-row");
33685                     }
33686                     rp.cells = lcb;
33687                     if(this.getRowClass){
33688                         alt.push( this.getRowClass(r, rowIndex));
33689                     }
33690                     if (hasListener) {
33691                         rowcfg = {
33692                              
33693                             record: r,
33694                             rowIndex : rowIndex,
33695                             rowClass : ''
33696                         };
33697                         this.grid.fireEvent('rowclass', this, rowcfg);
33698                         alt.push(rowcfg.rowClass);
33699                     }
33700                     
33701                     rp.alt = alt.join(" ");
33702                     rp.cells = lcb.join("");
33703                     lbuf[lbuf.length] = rt.apply(rp);
33704                     rp.cells = cb.join("");
33705                     buf[buf.length] =  rt.apply(rp);
33706                 }
33707                 return [lbuf.join(""), buf.join("")];
33708             },
33709
33710     renderBody : function(){
33711         var markup = this.renderRows();
33712         var bt = this.templates.body;
33713         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33714     },
33715
33716     /**
33717      * Refreshes the grid
33718      * @param {Boolean} headersToo
33719      */
33720     refresh : function(headersToo){
33721         this.fireEvent("beforerefresh", this);
33722         this.grid.stopEditing();
33723         var result = this.renderBody();
33724         this.lockedBody.update(result[0]);
33725         this.mainBody.update(result[1]);
33726         if(headersToo === true){
33727             this.updateHeaders();
33728             this.updateColumns();
33729             this.updateSplitters();
33730             this.updateHeaderSortState();
33731         }
33732         this.syncRowHeights();
33733         this.layout();
33734         this.fireEvent("refresh", this);
33735     },
33736
33737     handleColumnMove : function(cm, oldIndex, newIndex){
33738         this.indexMap = null;
33739         var s = this.getScrollState();
33740         this.refresh(true);
33741         this.restoreScroll(s);
33742         this.afterMove(newIndex);
33743     },
33744
33745     afterMove : function(colIndex){
33746         if(this.enableMoveAnim && Roo.enableFx){
33747             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33748         }
33749         // if multisort - fix sortOrder, and reload..
33750         if (this.grid.dataSource.multiSort) {
33751             // the we can call sort again..
33752             var dm = this.grid.dataSource;
33753             var cm = this.grid.colModel;
33754             var so = [];
33755             for(var i = 0; i < cm.config.length; i++ ) {
33756                 
33757                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33758                     continue; // dont' bother, it's not in sort list or being set.
33759                 }
33760                 
33761                 so.push(cm.config[i].dataIndex);
33762             };
33763             dm.sortOrder = so;
33764             dm.load(dm.lastOptions);
33765             
33766             
33767         }
33768         
33769     },
33770
33771     updateCell : function(dm, rowIndex, dataIndex){
33772         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33773         if(typeof colIndex == "undefined"){ // not present in grid
33774             return;
33775         }
33776         var cm = this.grid.colModel;
33777         var cell = this.getCell(rowIndex, colIndex);
33778         var cellText = this.getCellText(rowIndex, colIndex);
33779
33780         var p = {
33781             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33782             id : cm.getColumnId(colIndex),
33783             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33784         };
33785         var renderer = cm.getRenderer(colIndex);
33786         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33787         if(typeof val == "undefined" || val === "") {
33788             val = "&#160;";
33789         }
33790         cellText.innerHTML = val;
33791         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33792         this.syncRowHeights(rowIndex, rowIndex);
33793     },
33794
33795     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33796         var maxWidth = 0;
33797         if(this.grid.autoSizeHeaders){
33798             var h = this.getHeaderCellMeasure(colIndex);
33799             maxWidth = Math.max(maxWidth, h.scrollWidth);
33800         }
33801         var tb, index;
33802         if(this.cm.isLocked(colIndex)){
33803             tb = this.getLockedTable();
33804             index = colIndex;
33805         }else{
33806             tb = this.getBodyTable();
33807             index = colIndex - this.cm.getLockedCount();
33808         }
33809         if(tb && tb.rows){
33810             var rows = tb.rows;
33811             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33812             for(var i = 0; i < stopIndex; i++){
33813                 var cell = rows[i].childNodes[index].firstChild;
33814                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33815             }
33816         }
33817         return maxWidth + /*margin for error in IE*/ 5;
33818     },
33819     /**
33820      * Autofit a column to its content.
33821      * @param {Number} colIndex
33822      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33823      */
33824      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33825          if(this.cm.isHidden(colIndex)){
33826              return; // can't calc a hidden column
33827          }
33828         if(forceMinSize){
33829             var cid = this.cm.getColumnId(colIndex);
33830             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33831            if(this.grid.autoSizeHeaders){
33832                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33833            }
33834         }
33835         var newWidth = this.calcColumnWidth(colIndex);
33836         this.cm.setColumnWidth(colIndex,
33837             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33838         if(!suppressEvent){
33839             this.grid.fireEvent("columnresize", colIndex, newWidth);
33840         }
33841     },
33842
33843     /**
33844      * Autofits all columns to their content and then expands to fit any extra space in the grid
33845      */
33846      autoSizeColumns : function(){
33847         var cm = this.grid.colModel;
33848         var colCount = cm.getColumnCount();
33849         for(var i = 0; i < colCount; i++){
33850             this.autoSizeColumn(i, true, true);
33851         }
33852         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33853             this.fitColumns();
33854         }else{
33855             this.updateColumns();
33856             this.layout();
33857         }
33858     },
33859
33860     /**
33861      * Autofits all columns to the grid's width proportionate with their current size
33862      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33863      */
33864     fitColumns : function(reserveScrollSpace){
33865         var cm = this.grid.colModel;
33866         var colCount = cm.getColumnCount();
33867         var cols = [];
33868         var width = 0;
33869         var i, w;
33870         for (i = 0; i < colCount; i++){
33871             if(!cm.isHidden(i) && !cm.isFixed(i)){
33872                 w = cm.getColumnWidth(i);
33873                 cols.push(i);
33874                 cols.push(w);
33875                 width += w;
33876             }
33877         }
33878         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33879         if(reserveScrollSpace){
33880             avail -= 17;
33881         }
33882         var frac = (avail - cm.getTotalWidth())/width;
33883         while (cols.length){
33884             w = cols.pop();
33885             i = cols.pop();
33886             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33887         }
33888         this.updateColumns();
33889         this.layout();
33890     },
33891
33892     onRowSelect : function(rowIndex){
33893         var row = this.getRowComposite(rowIndex);
33894         row.addClass("x-grid-row-selected");
33895     },
33896
33897     onRowDeselect : function(rowIndex){
33898         var row = this.getRowComposite(rowIndex);
33899         row.removeClass("x-grid-row-selected");
33900     },
33901
33902     onCellSelect : function(row, col){
33903         var cell = this.getCell(row, col);
33904         if(cell){
33905             Roo.fly(cell).addClass("x-grid-cell-selected");
33906         }
33907     },
33908
33909     onCellDeselect : function(row, col){
33910         var cell = this.getCell(row, col);
33911         if(cell){
33912             Roo.fly(cell).removeClass("x-grid-cell-selected");
33913         }
33914     },
33915
33916     updateHeaderSortState : function(){
33917         
33918         // sort state can be single { field: xxx, direction : yyy}
33919         // or   { xxx=>ASC , yyy : DESC ..... }
33920         
33921         var mstate = {};
33922         if (!this.ds.multiSort) { 
33923             var state = this.ds.getSortState();
33924             if(!state){
33925                 return;
33926             }
33927             mstate[state.field] = state.direction;
33928             // FIXME... - this is not used here.. but might be elsewhere..
33929             this.sortState = state;
33930             
33931         } else {
33932             mstate = this.ds.sortToggle;
33933         }
33934         //remove existing sort classes..
33935         
33936         var sc = this.sortClasses;
33937         var hds = this.el.select(this.headerSelector).removeClass(sc);
33938         
33939         for(var f in mstate) {
33940         
33941             var sortColumn = this.cm.findColumnIndex(f);
33942             
33943             if(sortColumn != -1){
33944                 var sortDir = mstate[f];        
33945                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33946             }
33947         }
33948         
33949          
33950         
33951     },
33952
33953
33954     handleHeaderClick : function(g, index,e){
33955         
33956         Roo.log("header click");
33957         
33958         if (Roo.isTouch) {
33959             // touch events on header are handled by context
33960             this.handleHdCtx(g,index,e);
33961             return;
33962         }
33963         
33964         
33965         if(this.headersDisabled){
33966             return;
33967         }
33968         var dm = g.dataSource, cm = g.colModel;
33969         if(!cm.isSortable(index)){
33970             return;
33971         }
33972         g.stopEditing();
33973         
33974         if (dm.multiSort) {
33975             // update the sortOrder
33976             var so = [];
33977             for(var i = 0; i < cm.config.length; i++ ) {
33978                 
33979                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
33980                     continue; // dont' bother, it's not in sort list or being set.
33981                 }
33982                 
33983                 so.push(cm.config[i].dataIndex);
33984             };
33985             dm.sortOrder = so;
33986         }
33987         
33988         
33989         dm.sort(cm.getDataIndex(index));
33990     },
33991
33992
33993     destroy : function(){
33994         if(this.colMenu){
33995             this.colMenu.removeAll();
33996             Roo.menu.MenuMgr.unregister(this.colMenu);
33997             this.colMenu.getEl().remove();
33998             delete this.colMenu;
33999         }
34000         if(this.hmenu){
34001             this.hmenu.removeAll();
34002             Roo.menu.MenuMgr.unregister(this.hmenu);
34003             this.hmenu.getEl().remove();
34004             delete this.hmenu;
34005         }
34006         if(this.grid.enableColumnMove){
34007             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34008             if(dds){
34009                 for(var dd in dds){
34010                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34011                         var elid = dds[dd].dragElId;
34012                         dds[dd].unreg();
34013                         Roo.get(elid).remove();
34014                     } else if(dds[dd].config.isTarget){
34015                         dds[dd].proxyTop.remove();
34016                         dds[dd].proxyBottom.remove();
34017                         dds[dd].unreg();
34018                     }
34019                     if(Roo.dd.DDM.locationCache[dd]){
34020                         delete Roo.dd.DDM.locationCache[dd];
34021                     }
34022                 }
34023                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34024             }
34025         }
34026         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34027         this.bind(null, null);
34028         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34029     },
34030
34031     handleLockChange : function(){
34032         this.refresh(true);
34033     },
34034
34035     onDenyColumnLock : function(){
34036
34037     },
34038
34039     onDenyColumnHide : function(){
34040
34041     },
34042
34043     handleHdMenuClick : function(item){
34044         var index = this.hdCtxIndex;
34045         var cm = this.cm, ds = this.ds;
34046         switch(item.id){
34047             case "asc":
34048                 ds.sort(cm.getDataIndex(index), "ASC");
34049                 break;
34050             case "desc":
34051                 ds.sort(cm.getDataIndex(index), "DESC");
34052                 break;
34053             case "lock":
34054                 var lc = cm.getLockedCount();
34055                 if(cm.getColumnCount(true) <= lc+1){
34056                     this.onDenyColumnLock();
34057                     return;
34058                 }
34059                 if(lc != index){
34060                     cm.setLocked(index, true, true);
34061                     cm.moveColumn(index, lc);
34062                     this.grid.fireEvent("columnmove", index, lc);
34063                 }else{
34064                     cm.setLocked(index, true);
34065                 }
34066             break;
34067             case "unlock":
34068                 var lc = cm.getLockedCount();
34069                 if((lc-1) != index){
34070                     cm.setLocked(index, false, true);
34071                     cm.moveColumn(index, lc-1);
34072                     this.grid.fireEvent("columnmove", index, lc-1);
34073                 }else{
34074                     cm.setLocked(index, false);
34075                 }
34076             break;
34077             case 'wider': // used to expand cols on touch..
34078             case 'narrow':
34079                 var cw = cm.getColumnWidth(index);
34080                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34081                 cw = Math.max(0, cw);
34082                 cw = Math.min(cw,4000);
34083                 cm.setColumnWidth(index, cw);
34084                 break;
34085                 
34086             default:
34087                 index = cm.getIndexById(item.id.substr(4));
34088                 if(index != -1){
34089                     if(item.checked && cm.getColumnCount(true) <= 1){
34090                         this.onDenyColumnHide();
34091                         return false;
34092                     }
34093                     cm.setHidden(index, item.checked);
34094                 }
34095         }
34096         return true;
34097     },
34098
34099     beforeColMenuShow : function(){
34100         var cm = this.cm,  colCount = cm.getColumnCount();
34101         this.colMenu.removeAll();
34102         for(var i = 0; i < colCount; i++){
34103             this.colMenu.add(new Roo.menu.CheckItem({
34104                 id: "col-"+cm.getColumnId(i),
34105                 text: cm.getColumnHeader(i),
34106                 checked: !cm.isHidden(i),
34107                 hideOnClick:false
34108             }));
34109         }
34110     },
34111
34112     handleHdCtx : function(g, index, e){
34113         e.stopEvent();
34114         var hd = this.getHeaderCell(index);
34115         this.hdCtxIndex = index;
34116         var ms = this.hmenu.items, cm = this.cm;
34117         ms.get("asc").setDisabled(!cm.isSortable(index));
34118         ms.get("desc").setDisabled(!cm.isSortable(index));
34119         if(this.grid.enableColLock !== false){
34120             ms.get("lock").setDisabled(cm.isLocked(index));
34121             ms.get("unlock").setDisabled(!cm.isLocked(index));
34122         }
34123         this.hmenu.show(hd, "tl-bl");
34124     },
34125
34126     handleHdOver : function(e){
34127         var hd = this.findHeaderCell(e.getTarget());
34128         if(hd && !this.headersDisabled){
34129             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34130                this.fly(hd).addClass("x-grid-hd-over");
34131             }
34132         }
34133     },
34134
34135     handleHdOut : function(e){
34136         var hd = this.findHeaderCell(e.getTarget());
34137         if(hd){
34138             this.fly(hd).removeClass("x-grid-hd-over");
34139         }
34140     },
34141
34142     handleSplitDblClick : function(e, t){
34143         var i = this.getCellIndex(t);
34144         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34145             this.autoSizeColumn(i, true);
34146             this.layout();
34147         }
34148     },
34149
34150     render : function(){
34151
34152         var cm = this.cm;
34153         var colCount = cm.getColumnCount();
34154
34155         if(this.grid.monitorWindowResize === true){
34156             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34157         }
34158         var header = this.renderHeaders();
34159         var body = this.templates.body.apply({rows:""});
34160         var html = this.templates.master.apply({
34161             lockedBody: body,
34162             body: body,
34163             lockedHeader: header[0],
34164             header: header[1]
34165         });
34166
34167         //this.updateColumns();
34168
34169         this.grid.getGridEl().dom.innerHTML = html;
34170
34171         this.initElements();
34172         
34173         // a kludge to fix the random scolling effect in webkit
34174         this.el.on("scroll", function() {
34175             this.el.dom.scrollTop=0; // hopefully not recursive..
34176         },this);
34177
34178         this.scroller.on("scroll", this.handleScroll, this);
34179         this.lockedBody.on("mousewheel", this.handleWheel, this);
34180         this.mainBody.on("mousewheel", this.handleWheel, this);
34181
34182         this.mainHd.on("mouseover", this.handleHdOver, this);
34183         this.mainHd.on("mouseout", this.handleHdOut, this);
34184         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34185                 {delegate: "."+this.splitClass});
34186
34187         this.lockedHd.on("mouseover", this.handleHdOver, this);
34188         this.lockedHd.on("mouseout", this.handleHdOut, this);
34189         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34190                 {delegate: "."+this.splitClass});
34191
34192         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34193             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34194         }
34195
34196         this.updateSplitters();
34197
34198         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34199             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34200             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34201         }
34202
34203         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34204             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34205             this.hmenu.add(
34206                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34207                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34208             );
34209             if(this.grid.enableColLock !== false){
34210                 this.hmenu.add('-',
34211                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34212                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34213                 );
34214             }
34215             if (Roo.isTouch) {
34216                  this.hmenu.add('-',
34217                     {id:"wider", text: this.columnsWiderText},
34218                     {id:"narrow", text: this.columnsNarrowText }
34219                 );
34220                 
34221                  
34222             }
34223             
34224             if(this.grid.enableColumnHide !== false){
34225
34226                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34227                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34228                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34229
34230                 this.hmenu.add('-',
34231                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34232                 );
34233             }
34234             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34235
34236             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34237         }
34238
34239         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34240             this.dd = new Roo.grid.GridDragZone(this.grid, {
34241                 ddGroup : this.grid.ddGroup || 'GridDD'
34242             });
34243             
34244         }
34245
34246         /*
34247         for(var i = 0; i < colCount; i++){
34248             if(cm.isHidden(i)){
34249                 this.hideColumn(i);
34250             }
34251             if(cm.config[i].align){
34252                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34253                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34254             }
34255         }*/
34256         
34257         this.updateHeaderSortState();
34258
34259         this.beforeInitialResize();
34260         this.layout(true);
34261
34262         // two part rendering gives faster view to the user
34263         this.renderPhase2.defer(1, this);
34264     },
34265
34266     renderPhase2 : function(){
34267         // render the rows now
34268         this.refresh();
34269         if(this.grid.autoSizeColumns){
34270             this.autoSizeColumns();
34271         }
34272     },
34273
34274     beforeInitialResize : function(){
34275
34276     },
34277
34278     onColumnSplitterMoved : function(i, w){
34279         this.userResized = true;
34280         var cm = this.grid.colModel;
34281         cm.setColumnWidth(i, w, true);
34282         var cid = cm.getColumnId(i);
34283         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34284         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34285         this.updateSplitters();
34286         this.layout();
34287         this.grid.fireEvent("columnresize", i, w);
34288     },
34289
34290     syncRowHeights : function(startIndex, endIndex){
34291         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34292             startIndex = startIndex || 0;
34293             var mrows = this.getBodyTable().rows;
34294             var lrows = this.getLockedTable().rows;
34295             var len = mrows.length-1;
34296             endIndex = Math.min(endIndex || len, len);
34297             for(var i = startIndex; i <= endIndex; i++){
34298                 var m = mrows[i], l = lrows[i];
34299                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34300                 m.style.height = l.style.height = h + "px";
34301             }
34302         }
34303     },
34304
34305     layout : function(initialRender, is2ndPass){
34306         var g = this.grid;
34307         var auto = g.autoHeight;
34308         var scrollOffset = 16;
34309         var c = g.getGridEl(), cm = this.cm,
34310                 expandCol = g.autoExpandColumn,
34311                 gv = this;
34312         //c.beginMeasure();
34313
34314         if(!c.dom.offsetWidth){ // display:none?
34315             if(initialRender){
34316                 this.lockedWrap.show();
34317                 this.mainWrap.show();
34318             }
34319             return;
34320         }
34321
34322         var hasLock = this.cm.isLocked(0);
34323
34324         var tbh = this.headerPanel.getHeight();
34325         var bbh = this.footerPanel.getHeight();
34326
34327         if(auto){
34328             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34329             var newHeight = ch + c.getBorderWidth("tb");
34330             if(g.maxHeight){
34331                 newHeight = Math.min(g.maxHeight, newHeight);
34332             }
34333             c.setHeight(newHeight);
34334         }
34335
34336         if(g.autoWidth){
34337             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34338         }
34339
34340         var s = this.scroller;
34341
34342         var csize = c.getSize(true);
34343
34344         this.el.setSize(csize.width, csize.height);
34345
34346         this.headerPanel.setWidth(csize.width);
34347         this.footerPanel.setWidth(csize.width);
34348
34349         var hdHeight = this.mainHd.getHeight();
34350         var vw = csize.width;
34351         var vh = csize.height - (tbh + bbh);
34352
34353         s.setSize(vw, vh);
34354
34355         var bt = this.getBodyTable();
34356         
34357         if(cm.getLockedCount() == cm.config.length){
34358             bt = this.getLockedTable();
34359         }
34360         
34361         var ltWidth = hasLock ?
34362                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34363
34364         var scrollHeight = bt.offsetHeight;
34365         var scrollWidth = ltWidth + bt.offsetWidth;
34366         var vscroll = false, hscroll = false;
34367
34368         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34369
34370         var lw = this.lockedWrap, mw = this.mainWrap;
34371         var lb = this.lockedBody, mb = this.mainBody;
34372
34373         setTimeout(function(){
34374             var t = s.dom.offsetTop;
34375             var w = s.dom.clientWidth,
34376                 h = s.dom.clientHeight;
34377
34378             lw.setTop(t);
34379             lw.setSize(ltWidth, h);
34380
34381             mw.setLeftTop(ltWidth, t);
34382             mw.setSize(w-ltWidth, h);
34383
34384             lb.setHeight(h-hdHeight);
34385             mb.setHeight(h-hdHeight);
34386
34387             if(is2ndPass !== true && !gv.userResized && expandCol){
34388                 // high speed resize without full column calculation
34389                 
34390                 var ci = cm.getIndexById(expandCol);
34391                 if (ci < 0) {
34392                     ci = cm.findColumnIndex(expandCol);
34393                 }
34394                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34395                 var expandId = cm.getColumnId(ci);
34396                 var  tw = cm.getTotalWidth(false);
34397                 var currentWidth = cm.getColumnWidth(ci);
34398                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34399                 if(currentWidth != cw){
34400                     cm.setColumnWidth(ci, cw, true);
34401                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34402                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34403                     gv.updateSplitters();
34404                     gv.layout(false, true);
34405                 }
34406             }
34407
34408             if(initialRender){
34409                 lw.show();
34410                 mw.show();
34411             }
34412             //c.endMeasure();
34413         }, 10);
34414     },
34415
34416     onWindowResize : function(){
34417         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34418             return;
34419         }
34420         this.layout();
34421     },
34422
34423     appendFooter : function(parentEl){
34424         return null;
34425     },
34426
34427     sortAscText : "Sort Ascending",
34428     sortDescText : "Sort Descending",
34429     lockText : "Lock Column",
34430     unlockText : "Unlock Column",
34431     columnsText : "Columns",
34432  
34433     columnsWiderText : "Wider",
34434     columnsNarrowText : "Thinner"
34435 });
34436
34437
34438 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34439     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34440     this.proxy.el.addClass('x-grid3-col-dd');
34441 };
34442
34443 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34444     handleMouseDown : function(e){
34445
34446     },
34447
34448     callHandleMouseDown : function(e){
34449         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34450     }
34451 });
34452 /*
34453  * Based on:
34454  * Ext JS Library 1.1.1
34455  * Copyright(c) 2006-2007, Ext JS, LLC.
34456  *
34457  * Originally Released Under LGPL - original licence link has changed is not relivant.
34458  *
34459  * Fork - LGPL
34460  * <script type="text/javascript">
34461  */
34462  
34463 // private
34464 // This is a support class used internally by the Grid components
34465 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34466     this.grid = grid;
34467     this.view = grid.getView();
34468     this.proxy = this.view.resizeProxy;
34469     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34470         "gridSplitters" + this.grid.getGridEl().id, {
34471         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34472     });
34473     this.setHandleElId(Roo.id(hd));
34474     this.setOuterHandleElId(Roo.id(hd2));
34475     this.scroll = false;
34476 };
34477 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34478     fly: Roo.Element.fly,
34479
34480     b4StartDrag : function(x, y){
34481         this.view.headersDisabled = true;
34482         this.proxy.setHeight(this.view.mainWrap.getHeight());
34483         var w = this.cm.getColumnWidth(this.cellIndex);
34484         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34485         this.resetConstraints();
34486         this.setXConstraint(minw, 1000);
34487         this.setYConstraint(0, 0);
34488         this.minX = x - minw;
34489         this.maxX = x + 1000;
34490         this.startPos = x;
34491         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34492     },
34493
34494
34495     handleMouseDown : function(e){
34496         ev = Roo.EventObject.setEvent(e);
34497         var t = this.fly(ev.getTarget());
34498         if(t.hasClass("x-grid-split")){
34499             this.cellIndex = this.view.getCellIndex(t.dom);
34500             this.split = t.dom;
34501             this.cm = this.grid.colModel;
34502             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34503                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34504             }
34505         }
34506     },
34507
34508     endDrag : function(e){
34509         this.view.headersDisabled = false;
34510         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34511         var diff = endX - this.startPos;
34512         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34513     },
34514
34515     autoOffset : function(){
34516         this.setDelta(0,0);
34517     }
34518 });/*
34519  * Based on:
34520  * Ext JS Library 1.1.1
34521  * Copyright(c) 2006-2007, Ext JS, LLC.
34522  *
34523  * Originally Released Under LGPL - original licence link has changed is not relivant.
34524  *
34525  * Fork - LGPL
34526  * <script type="text/javascript">
34527  */
34528  
34529 // private
34530 // This is a support class used internally by the Grid components
34531 Roo.grid.GridDragZone = function(grid, config){
34532     this.view = grid.getView();
34533     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34534     if(this.view.lockedBody){
34535         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34536         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34537     }
34538     this.scroll = false;
34539     this.grid = grid;
34540     this.ddel = document.createElement('div');
34541     this.ddel.className = 'x-grid-dd-wrap';
34542 };
34543
34544 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34545     ddGroup : "GridDD",
34546
34547     getDragData : function(e){
34548         var t = Roo.lib.Event.getTarget(e);
34549         var rowIndex = this.view.findRowIndex(t);
34550         var sm = this.grid.selModel;
34551             
34552         //Roo.log(rowIndex);
34553         
34554         if (sm.getSelectedCell) {
34555             // cell selection..
34556             if (!sm.getSelectedCell()) {
34557                 return false;
34558             }
34559             if (rowIndex != sm.getSelectedCell()[0]) {
34560                 return false;
34561             }
34562         
34563         }
34564         
34565         if(rowIndex !== false){
34566             
34567             // if editorgrid.. 
34568             
34569             
34570             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34571                
34572             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34573               //  
34574             //}
34575             if (e.hasModifier()){
34576                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34577             }
34578             
34579             Roo.log("getDragData");
34580             
34581             return {
34582                 grid: this.grid,
34583                 ddel: this.ddel,
34584                 rowIndex: rowIndex,
34585                 selections:sm.getSelections ? sm.getSelections() : (
34586                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34587                 )
34588             };
34589         }
34590         return false;
34591     },
34592
34593     onInitDrag : function(e){
34594         var data = this.dragData;
34595         this.ddel.innerHTML = this.grid.getDragDropText();
34596         this.proxy.update(this.ddel);
34597         // fire start drag?
34598     },
34599
34600     afterRepair : function(){
34601         this.dragging = false;
34602     },
34603
34604     getRepairXY : function(e, data){
34605         return false;
34606     },
34607
34608     onEndDrag : function(data, e){
34609         // fire end drag?
34610     },
34611
34612     onValidDrop : function(dd, e, id){
34613         // fire drag drop?
34614         this.hideProxy();
34615     },
34616
34617     beforeInvalidDrop : function(e, id){
34618
34619     }
34620 });/*
34621  * Based on:
34622  * Ext JS Library 1.1.1
34623  * Copyright(c) 2006-2007, Ext JS, LLC.
34624  *
34625  * Originally Released Under LGPL - original licence link has changed is not relivant.
34626  *
34627  * Fork - LGPL
34628  * <script type="text/javascript">
34629  */
34630  
34631
34632 /**
34633  * @class Roo.grid.ColumnModel
34634  * @extends Roo.util.Observable
34635  * This is the default implementation of a ColumnModel used by the Grid. It defines
34636  * the columns in the grid.
34637  * <br>Usage:<br>
34638  <pre><code>
34639  var colModel = new Roo.grid.ColumnModel([
34640         {header: "Ticker", width: 60, sortable: true, locked: true},
34641         {header: "Company Name", width: 150, sortable: true},
34642         {header: "Market Cap.", width: 100, sortable: true},
34643         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34644         {header: "Employees", width: 100, sortable: true, resizable: false}
34645  ]);
34646  </code></pre>
34647  * <p>
34648  
34649  * The config options listed for this class are options which may appear in each
34650  * individual column definition.
34651  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34652  * @constructor
34653  * @param {Object} config An Array of column config objects. See this class's
34654  * config objects for details.
34655 */
34656 Roo.grid.ColumnModel = function(config){
34657         /**
34658      * The config passed into the constructor
34659      */
34660     this.config = config;
34661     this.lookup = {};
34662
34663     // if no id, create one
34664     // if the column does not have a dataIndex mapping,
34665     // map it to the order it is in the config
34666     for(var i = 0, len = config.length; i < len; i++){
34667         var c = config[i];
34668         if(typeof c.dataIndex == "undefined"){
34669             c.dataIndex = i;
34670         }
34671         if(typeof c.renderer == "string"){
34672             c.renderer = Roo.util.Format[c.renderer];
34673         }
34674         if(typeof c.id == "undefined"){
34675             c.id = Roo.id();
34676         }
34677         if(c.editor && c.editor.xtype){
34678             c.editor  = Roo.factory(c.editor, Roo.grid);
34679         }
34680         if(c.editor && c.editor.isFormField){
34681             c.editor = new Roo.grid.GridEditor(c.editor);
34682         }
34683         this.lookup[c.id] = c;
34684     }
34685
34686     /**
34687      * The width of columns which have no width specified (defaults to 100)
34688      * @type Number
34689      */
34690     this.defaultWidth = 100;
34691
34692     /**
34693      * Default sortable of columns which have no sortable specified (defaults to false)
34694      * @type Boolean
34695      */
34696     this.defaultSortable = false;
34697
34698     this.addEvents({
34699         /**
34700              * @event widthchange
34701              * Fires when the width of a column changes.
34702              * @param {ColumnModel} this
34703              * @param {Number} columnIndex The column index
34704              * @param {Number} newWidth The new width
34705              */
34706             "widthchange": true,
34707         /**
34708              * @event headerchange
34709              * Fires when the text of a header changes.
34710              * @param {ColumnModel} this
34711              * @param {Number} columnIndex The column index
34712              * @param {Number} newText The new header text
34713              */
34714             "headerchange": true,
34715         /**
34716              * @event hiddenchange
34717              * Fires when a column is hidden or "unhidden".
34718              * @param {ColumnModel} this
34719              * @param {Number} columnIndex The column index
34720              * @param {Boolean} hidden true if hidden, false otherwise
34721              */
34722             "hiddenchange": true,
34723             /**
34724          * @event columnmoved
34725          * Fires when a column is moved.
34726          * @param {ColumnModel} this
34727          * @param {Number} oldIndex
34728          * @param {Number} newIndex
34729          */
34730         "columnmoved" : true,
34731         /**
34732          * @event columlockchange
34733          * Fires when a column's locked state is changed
34734          * @param {ColumnModel} this
34735          * @param {Number} colIndex
34736          * @param {Boolean} locked true if locked
34737          */
34738         "columnlockchange" : true
34739     });
34740     Roo.grid.ColumnModel.superclass.constructor.call(this);
34741 };
34742 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34743     /**
34744      * @cfg {String} header The header text to display in the Grid view.
34745      */
34746     /**
34747      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34748      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34749      * specified, the column's index is used as an index into the Record's data Array.
34750      */
34751     /**
34752      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34753      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34754      */
34755     /**
34756      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34757      * Defaults to the value of the {@link #defaultSortable} property.
34758      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34759      */
34760     /**
34761      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34762      */
34763     /**
34764      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34765      */
34766     /**
34767      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34768      */
34769     /**
34770      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34771      */
34772     /**
34773      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34774      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34775      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
34776      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
34777      */
34778        /**
34779      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34780      */
34781     /**
34782      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34783      */
34784     /**
34785      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
34786      */
34787     /**
34788      * @cfg {String} cursor (Optional)
34789      */
34790     /**
34791      * @cfg {String} tooltip (Optional)
34792      */
34793     /**
34794      * @cfg {Number} xs (Optional)
34795      */
34796     /**
34797      * @cfg {Number} sm (Optional)
34798      */
34799     /**
34800      * @cfg {Number} md (Optional)
34801      */
34802     /**
34803      * @cfg {Number} lg (Optional)
34804      */
34805     /**
34806      * Returns the id of the column at the specified index.
34807      * @param {Number} index The column index
34808      * @return {String} the id
34809      */
34810     getColumnId : function(index){
34811         return this.config[index].id;
34812     },
34813
34814     /**
34815      * Returns the column for a specified id.
34816      * @param {String} id The column id
34817      * @return {Object} the column
34818      */
34819     getColumnById : function(id){
34820         return this.lookup[id];
34821     },
34822
34823     
34824     /**
34825      * Returns the column for a specified dataIndex.
34826      * @param {String} dataIndex The column dataIndex
34827      * @return {Object|Boolean} the column or false if not found
34828      */
34829     getColumnByDataIndex: function(dataIndex){
34830         var index = this.findColumnIndex(dataIndex);
34831         return index > -1 ? this.config[index] : false;
34832     },
34833     
34834     /**
34835      * Returns the index for a specified column id.
34836      * @param {String} id The column id
34837      * @return {Number} the index, or -1 if not found
34838      */
34839     getIndexById : function(id){
34840         for(var i = 0, len = this.config.length; i < len; i++){
34841             if(this.config[i].id == id){
34842                 return i;
34843             }
34844         }
34845         return -1;
34846     },
34847     
34848     /**
34849      * Returns the index for a specified column dataIndex.
34850      * @param {String} dataIndex The column dataIndex
34851      * @return {Number} the index, or -1 if not found
34852      */
34853     
34854     findColumnIndex : function(dataIndex){
34855         for(var i = 0, len = this.config.length; i < len; i++){
34856             if(this.config[i].dataIndex == dataIndex){
34857                 return i;
34858             }
34859         }
34860         return -1;
34861     },
34862     
34863     
34864     moveColumn : function(oldIndex, newIndex){
34865         var c = this.config[oldIndex];
34866         this.config.splice(oldIndex, 1);
34867         this.config.splice(newIndex, 0, c);
34868         this.dataMap = null;
34869         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34870     },
34871
34872     isLocked : function(colIndex){
34873         return this.config[colIndex].locked === true;
34874     },
34875
34876     setLocked : function(colIndex, value, suppressEvent){
34877         if(this.isLocked(colIndex) == value){
34878             return;
34879         }
34880         this.config[colIndex].locked = value;
34881         if(!suppressEvent){
34882             this.fireEvent("columnlockchange", this, colIndex, value);
34883         }
34884     },
34885
34886     getTotalLockedWidth : function(){
34887         var totalWidth = 0;
34888         for(var i = 0; i < this.config.length; i++){
34889             if(this.isLocked(i) && !this.isHidden(i)){
34890                 this.totalWidth += this.getColumnWidth(i);
34891             }
34892         }
34893         return totalWidth;
34894     },
34895
34896     getLockedCount : function(){
34897         for(var i = 0, len = this.config.length; i < len; i++){
34898             if(!this.isLocked(i)){
34899                 return i;
34900             }
34901         }
34902         
34903         return this.config.length;
34904     },
34905
34906     /**
34907      * Returns the number of columns.
34908      * @return {Number}
34909      */
34910     getColumnCount : function(visibleOnly){
34911         if(visibleOnly === true){
34912             var c = 0;
34913             for(var i = 0, len = this.config.length; i < len; i++){
34914                 if(!this.isHidden(i)){
34915                     c++;
34916                 }
34917             }
34918             return c;
34919         }
34920         return this.config.length;
34921     },
34922
34923     /**
34924      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34925      * @param {Function} fn
34926      * @param {Object} scope (optional)
34927      * @return {Array} result
34928      */
34929     getColumnsBy : function(fn, scope){
34930         var r = [];
34931         for(var i = 0, len = this.config.length; i < len; i++){
34932             var c = this.config[i];
34933             if(fn.call(scope||this, c, i) === true){
34934                 r[r.length] = c;
34935             }
34936         }
34937         return r;
34938     },
34939
34940     /**
34941      * Returns true if the specified column is sortable.
34942      * @param {Number} col The column index
34943      * @return {Boolean}
34944      */
34945     isSortable : function(col){
34946         if(typeof this.config[col].sortable == "undefined"){
34947             return this.defaultSortable;
34948         }
34949         return this.config[col].sortable;
34950     },
34951
34952     /**
34953      * Returns the rendering (formatting) function defined for the column.
34954      * @param {Number} col The column index.
34955      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34956      */
34957     getRenderer : function(col){
34958         if(!this.config[col].renderer){
34959             return Roo.grid.ColumnModel.defaultRenderer;
34960         }
34961         return this.config[col].renderer;
34962     },
34963
34964     /**
34965      * Sets the rendering (formatting) function for a column.
34966      * @param {Number} col The column index
34967      * @param {Function} fn The function to use to process the cell's raw data
34968      * to return HTML markup for the grid view. The render function is called with
34969      * the following parameters:<ul>
34970      * <li>Data value.</li>
34971      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34972      * <li>css A CSS style string to apply to the table cell.</li>
34973      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34974      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34975      * <li>Row index</li>
34976      * <li>Column index</li>
34977      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34978      */
34979     setRenderer : function(col, fn){
34980         this.config[col].renderer = fn;
34981     },
34982
34983     /**
34984      * Returns the width for the specified column.
34985      * @param {Number} col The column index
34986      * @return {Number}
34987      */
34988     getColumnWidth : function(col){
34989         return this.config[col].width * 1 || this.defaultWidth;
34990     },
34991
34992     /**
34993      * Sets the width for a column.
34994      * @param {Number} col The column index
34995      * @param {Number} width The new width
34996      */
34997     setColumnWidth : function(col, width, suppressEvent){
34998         this.config[col].width = width;
34999         this.totalWidth = null;
35000         if(!suppressEvent){
35001              this.fireEvent("widthchange", this, col, width);
35002         }
35003     },
35004
35005     /**
35006      * Returns the total width of all columns.
35007      * @param {Boolean} includeHidden True to include hidden column widths
35008      * @return {Number}
35009      */
35010     getTotalWidth : function(includeHidden){
35011         if(!this.totalWidth){
35012             this.totalWidth = 0;
35013             for(var i = 0, len = this.config.length; i < len; i++){
35014                 if(includeHidden || !this.isHidden(i)){
35015                     this.totalWidth += this.getColumnWidth(i);
35016                 }
35017             }
35018         }
35019         return this.totalWidth;
35020     },
35021
35022     /**
35023      * Returns the header for the specified column.
35024      * @param {Number} col The column index
35025      * @return {String}
35026      */
35027     getColumnHeader : function(col){
35028         return this.config[col].header;
35029     },
35030
35031     /**
35032      * Sets the header for a column.
35033      * @param {Number} col The column index
35034      * @param {String} header The new header
35035      */
35036     setColumnHeader : function(col, header){
35037         this.config[col].header = header;
35038         this.fireEvent("headerchange", this, col, header);
35039     },
35040
35041     /**
35042      * Returns the tooltip for the specified column.
35043      * @param {Number} col The column index
35044      * @return {String}
35045      */
35046     getColumnTooltip : function(col){
35047             return this.config[col].tooltip;
35048     },
35049     /**
35050      * Sets the tooltip for a column.
35051      * @param {Number} col The column index
35052      * @param {String} tooltip The new tooltip
35053      */
35054     setColumnTooltip : function(col, tooltip){
35055             this.config[col].tooltip = tooltip;
35056     },
35057
35058     /**
35059      * Returns the dataIndex for the specified column.
35060      * @param {Number} col The column index
35061      * @return {Number}
35062      */
35063     getDataIndex : function(col){
35064         return this.config[col].dataIndex;
35065     },
35066
35067     /**
35068      * Sets the dataIndex for a column.
35069      * @param {Number} col The column index
35070      * @param {Number} dataIndex The new dataIndex
35071      */
35072     setDataIndex : function(col, dataIndex){
35073         this.config[col].dataIndex = dataIndex;
35074     },
35075
35076     
35077     
35078     /**
35079      * Returns true if the cell is editable.
35080      * @param {Number} colIndex The column index
35081      * @param {Number} rowIndex The row index - this is nto actually used..?
35082      * @return {Boolean}
35083      */
35084     isCellEditable : function(colIndex, rowIndex){
35085         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35086     },
35087
35088     /**
35089      * Returns the editor defined for the cell/column.
35090      * return false or null to disable editing.
35091      * @param {Number} colIndex The column index
35092      * @param {Number} rowIndex The row index
35093      * @return {Object}
35094      */
35095     getCellEditor : function(colIndex, rowIndex){
35096         return this.config[colIndex].editor;
35097     },
35098
35099     /**
35100      * Sets if a column is editable.
35101      * @param {Number} col The column index
35102      * @param {Boolean} editable True if the column is editable
35103      */
35104     setEditable : function(col, editable){
35105         this.config[col].editable = editable;
35106     },
35107
35108
35109     /**
35110      * Returns true if the column is hidden.
35111      * @param {Number} colIndex The column index
35112      * @return {Boolean}
35113      */
35114     isHidden : function(colIndex){
35115         return this.config[colIndex].hidden;
35116     },
35117
35118
35119     /**
35120      * Returns true if the column width cannot be changed
35121      */
35122     isFixed : function(colIndex){
35123         return this.config[colIndex].fixed;
35124     },
35125
35126     /**
35127      * Returns true if the column can be resized
35128      * @return {Boolean}
35129      */
35130     isResizable : function(colIndex){
35131         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35132     },
35133     /**
35134      * Sets if a column is hidden.
35135      * @param {Number} colIndex The column index
35136      * @param {Boolean} hidden True if the column is hidden
35137      */
35138     setHidden : function(colIndex, hidden){
35139         this.config[colIndex].hidden = hidden;
35140         this.totalWidth = null;
35141         this.fireEvent("hiddenchange", this, colIndex, hidden);
35142     },
35143
35144     /**
35145      * Sets the editor for a column.
35146      * @param {Number} col The column index
35147      * @param {Object} editor The editor object
35148      */
35149     setEditor : function(col, editor){
35150         this.config[col].editor = editor;
35151     }
35152 });
35153
35154 Roo.grid.ColumnModel.defaultRenderer = function(value)
35155 {
35156     if(typeof value == "object") {
35157         return value;
35158     }
35159         if(typeof value == "string" && value.length < 1){
35160             return "&#160;";
35161         }
35162     
35163         return String.format("{0}", value);
35164 };
35165
35166 // Alias for backwards compatibility
35167 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35168 /*
35169  * Based on:
35170  * Ext JS Library 1.1.1
35171  * Copyright(c) 2006-2007, Ext JS, LLC.
35172  *
35173  * Originally Released Under LGPL - original licence link has changed is not relivant.
35174  *
35175  * Fork - LGPL
35176  * <script type="text/javascript">
35177  */
35178
35179 /**
35180  * @class Roo.grid.AbstractSelectionModel
35181  * @extends Roo.util.Observable
35182  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35183  * implemented by descendant classes.  This class should not be directly instantiated.
35184  * @constructor
35185  */
35186 Roo.grid.AbstractSelectionModel = function(){
35187     this.locked = false;
35188     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35189 };
35190
35191 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35192     /** @ignore Called by the grid automatically. Do not call directly. */
35193     init : function(grid){
35194         this.grid = grid;
35195         this.initEvents();
35196     },
35197
35198     /**
35199      * Locks the selections.
35200      */
35201     lock : function(){
35202         this.locked = true;
35203     },
35204
35205     /**
35206      * Unlocks the selections.
35207      */
35208     unlock : function(){
35209         this.locked = false;
35210     },
35211
35212     /**
35213      * Returns true if the selections are locked.
35214      * @return {Boolean}
35215      */
35216     isLocked : function(){
35217         return this.locked;
35218     }
35219 });/*
35220  * Based on:
35221  * Ext JS Library 1.1.1
35222  * Copyright(c) 2006-2007, Ext JS, LLC.
35223  *
35224  * Originally Released Under LGPL - original licence link has changed is not relivant.
35225  *
35226  * Fork - LGPL
35227  * <script type="text/javascript">
35228  */
35229 /**
35230  * @extends Roo.grid.AbstractSelectionModel
35231  * @class Roo.grid.RowSelectionModel
35232  * The default SelectionModel used by {@link Roo.grid.Grid}.
35233  * It supports multiple selections and keyboard selection/navigation. 
35234  * @constructor
35235  * @param {Object} config
35236  */
35237 Roo.grid.RowSelectionModel = function(config){
35238     Roo.apply(this, config);
35239     this.selections = new Roo.util.MixedCollection(false, function(o){
35240         return o.id;
35241     });
35242
35243     this.last = false;
35244     this.lastActive = false;
35245
35246     this.addEvents({
35247         /**
35248              * @event selectionchange
35249              * Fires when the selection changes
35250              * @param {SelectionModel} this
35251              */
35252             "selectionchange" : true,
35253         /**
35254              * @event afterselectionchange
35255              * Fires after the selection changes (eg. by key press or clicking)
35256              * @param {SelectionModel} this
35257              */
35258             "afterselectionchange" : true,
35259         /**
35260              * @event beforerowselect
35261              * Fires when a row is selected being selected, return false to cancel.
35262              * @param {SelectionModel} this
35263              * @param {Number} rowIndex The selected index
35264              * @param {Boolean} keepExisting False if other selections will be cleared
35265              */
35266             "beforerowselect" : true,
35267         /**
35268              * @event rowselect
35269              * Fires when a row is selected.
35270              * @param {SelectionModel} this
35271              * @param {Number} rowIndex The selected index
35272              * @param {Roo.data.Record} r The record
35273              */
35274             "rowselect" : true,
35275         /**
35276              * @event rowdeselect
35277              * Fires when a row is deselected.
35278              * @param {SelectionModel} this
35279              * @param {Number} rowIndex The selected index
35280              */
35281         "rowdeselect" : true
35282     });
35283     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35284     this.locked = false;
35285 };
35286
35287 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35288     /**
35289      * @cfg {Boolean} singleSelect
35290      * True to allow selection of only one row at a time (defaults to false)
35291      */
35292     singleSelect : false,
35293
35294     // private
35295     initEvents : function(){
35296
35297         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35298             this.grid.on("mousedown", this.handleMouseDown, this);
35299         }else{ // allow click to work like normal
35300             this.grid.on("rowclick", this.handleDragableRowClick, this);
35301         }
35302
35303         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35304             "up" : function(e){
35305                 if(!e.shiftKey){
35306                     this.selectPrevious(e.shiftKey);
35307                 }else if(this.last !== false && this.lastActive !== false){
35308                     var last = this.last;
35309                     this.selectRange(this.last,  this.lastActive-1);
35310                     this.grid.getView().focusRow(this.lastActive);
35311                     if(last !== false){
35312                         this.last = last;
35313                     }
35314                 }else{
35315                     this.selectFirstRow();
35316                 }
35317                 this.fireEvent("afterselectionchange", this);
35318             },
35319             "down" : function(e){
35320                 if(!e.shiftKey){
35321                     this.selectNext(e.shiftKey);
35322                 }else if(this.last !== false && this.lastActive !== false){
35323                     var last = this.last;
35324                     this.selectRange(this.last,  this.lastActive+1);
35325                     this.grid.getView().focusRow(this.lastActive);
35326                     if(last !== false){
35327                         this.last = last;
35328                     }
35329                 }else{
35330                     this.selectFirstRow();
35331                 }
35332                 this.fireEvent("afterselectionchange", this);
35333             },
35334             scope: this
35335         });
35336
35337         var view = this.grid.view;
35338         view.on("refresh", this.onRefresh, this);
35339         view.on("rowupdated", this.onRowUpdated, this);
35340         view.on("rowremoved", this.onRemove, this);
35341     },
35342
35343     // private
35344     onRefresh : function(){
35345         var ds = this.grid.dataSource, i, v = this.grid.view;
35346         var s = this.selections;
35347         s.each(function(r){
35348             if((i = ds.indexOfId(r.id)) != -1){
35349                 v.onRowSelect(i);
35350                 s.add(ds.getAt(i)); // updating the selection relate data
35351             }else{
35352                 s.remove(r);
35353             }
35354         });
35355     },
35356
35357     // private
35358     onRemove : function(v, index, r){
35359         this.selections.remove(r);
35360     },
35361
35362     // private
35363     onRowUpdated : function(v, index, r){
35364         if(this.isSelected(r)){
35365             v.onRowSelect(index);
35366         }
35367     },
35368
35369     /**
35370      * Select records.
35371      * @param {Array} records The records to select
35372      * @param {Boolean} keepExisting (optional) True to keep existing selections
35373      */
35374     selectRecords : function(records, keepExisting){
35375         if(!keepExisting){
35376             this.clearSelections();
35377         }
35378         var ds = this.grid.dataSource;
35379         for(var i = 0, len = records.length; i < len; i++){
35380             this.selectRow(ds.indexOf(records[i]), true);
35381         }
35382     },
35383
35384     /**
35385      * Gets the number of selected rows.
35386      * @return {Number}
35387      */
35388     getCount : function(){
35389         return this.selections.length;
35390     },
35391
35392     /**
35393      * Selects the first row in the grid.
35394      */
35395     selectFirstRow : function(){
35396         this.selectRow(0);
35397     },
35398
35399     /**
35400      * Select the last row.
35401      * @param {Boolean} keepExisting (optional) True to keep existing selections
35402      */
35403     selectLastRow : function(keepExisting){
35404         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35405     },
35406
35407     /**
35408      * Selects the row immediately following the last selected row.
35409      * @param {Boolean} keepExisting (optional) True to keep existing selections
35410      */
35411     selectNext : function(keepExisting){
35412         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35413             this.selectRow(this.last+1, keepExisting);
35414             this.grid.getView().focusRow(this.last);
35415         }
35416     },
35417
35418     /**
35419      * Selects the row that precedes the last selected row.
35420      * @param {Boolean} keepExisting (optional) True to keep existing selections
35421      */
35422     selectPrevious : function(keepExisting){
35423         if(this.last){
35424             this.selectRow(this.last-1, keepExisting);
35425             this.grid.getView().focusRow(this.last);
35426         }
35427     },
35428
35429     /**
35430      * Returns the selected records
35431      * @return {Array} Array of selected records
35432      */
35433     getSelections : function(){
35434         return [].concat(this.selections.items);
35435     },
35436
35437     /**
35438      * Returns the first selected record.
35439      * @return {Record}
35440      */
35441     getSelected : function(){
35442         return this.selections.itemAt(0);
35443     },
35444
35445
35446     /**
35447      * Clears all selections.
35448      */
35449     clearSelections : function(fast){
35450         if(this.locked) {
35451             return;
35452         }
35453         if(fast !== true){
35454             var ds = this.grid.dataSource;
35455             var s = this.selections;
35456             s.each(function(r){
35457                 this.deselectRow(ds.indexOfId(r.id));
35458             }, this);
35459             s.clear();
35460         }else{
35461             this.selections.clear();
35462         }
35463         this.last = false;
35464     },
35465
35466
35467     /**
35468      * Selects all rows.
35469      */
35470     selectAll : function(){
35471         if(this.locked) {
35472             return;
35473         }
35474         this.selections.clear();
35475         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35476             this.selectRow(i, true);
35477         }
35478     },
35479
35480     /**
35481      * Returns True if there is a selection.
35482      * @return {Boolean}
35483      */
35484     hasSelection : function(){
35485         return this.selections.length > 0;
35486     },
35487
35488     /**
35489      * Returns True if the specified row is selected.
35490      * @param {Number/Record} record The record or index of the record to check
35491      * @return {Boolean}
35492      */
35493     isSelected : function(index){
35494         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35495         return (r && this.selections.key(r.id) ? true : false);
35496     },
35497
35498     /**
35499      * Returns True if the specified record id is selected.
35500      * @param {String} id The id of record to check
35501      * @return {Boolean}
35502      */
35503     isIdSelected : function(id){
35504         return (this.selections.key(id) ? true : false);
35505     },
35506
35507     // private
35508     handleMouseDown : function(e, t){
35509         var view = this.grid.getView(), rowIndex;
35510         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35511             return;
35512         };
35513         if(e.shiftKey && this.last !== false){
35514             var last = this.last;
35515             this.selectRange(last, rowIndex, e.ctrlKey);
35516             this.last = last; // reset the last
35517             view.focusRow(rowIndex);
35518         }else{
35519             var isSelected = this.isSelected(rowIndex);
35520             if(e.button !== 0 && isSelected){
35521                 view.focusRow(rowIndex);
35522             }else if(e.ctrlKey && isSelected){
35523                 this.deselectRow(rowIndex);
35524             }else if(!isSelected){
35525                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35526                 view.focusRow(rowIndex);
35527             }
35528         }
35529         this.fireEvent("afterselectionchange", this);
35530     },
35531     // private
35532     handleDragableRowClick :  function(grid, rowIndex, e) 
35533     {
35534         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35535             this.selectRow(rowIndex, false);
35536             grid.view.focusRow(rowIndex);
35537              this.fireEvent("afterselectionchange", this);
35538         }
35539     },
35540     
35541     /**
35542      * Selects multiple rows.
35543      * @param {Array} rows Array of the indexes of the row to select
35544      * @param {Boolean} keepExisting (optional) True to keep existing selections
35545      */
35546     selectRows : function(rows, keepExisting){
35547         if(!keepExisting){
35548             this.clearSelections();
35549         }
35550         for(var i = 0, len = rows.length; i < len; i++){
35551             this.selectRow(rows[i], true);
35552         }
35553     },
35554
35555     /**
35556      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35557      * @param {Number} startRow The index of the first row in the range
35558      * @param {Number} endRow The index of the last row in the range
35559      * @param {Boolean} keepExisting (optional) True to retain existing selections
35560      */
35561     selectRange : function(startRow, endRow, keepExisting){
35562         if(this.locked) {
35563             return;
35564         }
35565         if(!keepExisting){
35566             this.clearSelections();
35567         }
35568         if(startRow <= endRow){
35569             for(var i = startRow; i <= endRow; i++){
35570                 this.selectRow(i, true);
35571             }
35572         }else{
35573             for(var i = startRow; i >= endRow; i--){
35574                 this.selectRow(i, true);
35575             }
35576         }
35577     },
35578
35579     /**
35580      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35581      * @param {Number} startRow The index of the first row in the range
35582      * @param {Number} endRow The index of the last row in the range
35583      */
35584     deselectRange : function(startRow, endRow, preventViewNotify){
35585         if(this.locked) {
35586             return;
35587         }
35588         for(var i = startRow; i <= endRow; i++){
35589             this.deselectRow(i, preventViewNotify);
35590         }
35591     },
35592
35593     /**
35594      * Selects a row.
35595      * @param {Number} row The index of the row to select
35596      * @param {Boolean} keepExisting (optional) True to keep existing selections
35597      */
35598     selectRow : function(index, keepExisting, preventViewNotify){
35599         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35600             return;
35601         }
35602         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35603             if(!keepExisting || this.singleSelect){
35604                 this.clearSelections();
35605             }
35606             var r = this.grid.dataSource.getAt(index);
35607             this.selections.add(r);
35608             this.last = this.lastActive = index;
35609             if(!preventViewNotify){
35610                 this.grid.getView().onRowSelect(index);
35611             }
35612             this.fireEvent("rowselect", this, index, r);
35613             this.fireEvent("selectionchange", this);
35614         }
35615     },
35616
35617     /**
35618      * Deselects a row.
35619      * @param {Number} row The index of the row to deselect
35620      */
35621     deselectRow : function(index, preventViewNotify){
35622         if(this.locked) {
35623             return;
35624         }
35625         if(this.last == index){
35626             this.last = false;
35627         }
35628         if(this.lastActive == index){
35629             this.lastActive = false;
35630         }
35631         var r = this.grid.dataSource.getAt(index);
35632         this.selections.remove(r);
35633         if(!preventViewNotify){
35634             this.grid.getView().onRowDeselect(index);
35635         }
35636         this.fireEvent("rowdeselect", this, index);
35637         this.fireEvent("selectionchange", this);
35638     },
35639
35640     // private
35641     restoreLast : function(){
35642         if(this._last){
35643             this.last = this._last;
35644         }
35645     },
35646
35647     // private
35648     acceptsNav : function(row, col, cm){
35649         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35650     },
35651
35652     // private
35653     onEditorKey : function(field, e){
35654         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35655         if(k == e.TAB){
35656             e.stopEvent();
35657             ed.completeEdit();
35658             if(e.shiftKey){
35659                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35660             }else{
35661                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35662             }
35663         }else if(k == e.ENTER && !e.ctrlKey){
35664             e.stopEvent();
35665             ed.completeEdit();
35666             if(e.shiftKey){
35667                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35668             }else{
35669                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35670             }
35671         }else if(k == e.ESC){
35672             ed.cancelEdit();
35673         }
35674         if(newCell){
35675             g.startEditing(newCell[0], newCell[1]);
35676         }
35677     }
35678 });/*
35679  * Based on:
35680  * Ext JS Library 1.1.1
35681  * Copyright(c) 2006-2007, Ext JS, LLC.
35682  *
35683  * Originally Released Under LGPL - original licence link has changed is not relivant.
35684  *
35685  * Fork - LGPL
35686  * <script type="text/javascript">
35687  */
35688 /**
35689  * @class Roo.grid.CellSelectionModel
35690  * @extends Roo.grid.AbstractSelectionModel
35691  * This class provides the basic implementation for cell selection in a grid.
35692  * @constructor
35693  * @param {Object} config The object containing the configuration of this model.
35694  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
35695  */
35696 Roo.grid.CellSelectionModel = function(config){
35697     Roo.apply(this, config);
35698
35699     this.selection = null;
35700
35701     this.addEvents({
35702         /**
35703              * @event beforerowselect
35704              * Fires before a cell is selected.
35705              * @param {SelectionModel} this
35706              * @param {Number} rowIndex The selected row index
35707              * @param {Number} colIndex The selected cell index
35708              */
35709             "beforecellselect" : true,
35710         /**
35711              * @event cellselect
35712              * Fires when a cell is selected.
35713              * @param {SelectionModel} this
35714              * @param {Number} rowIndex The selected row index
35715              * @param {Number} colIndex The selected cell index
35716              */
35717             "cellselect" : true,
35718         /**
35719              * @event selectionchange
35720              * Fires when the active selection changes.
35721              * @param {SelectionModel} this
35722              * @param {Object} selection null for no selection or an object (o) with two properties
35723                 <ul>
35724                 <li>o.record: the record object for the row the selection is in</li>
35725                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35726                 </ul>
35727              */
35728             "selectionchange" : true,
35729         /**
35730              * @event tabend
35731              * Fires when the tab (or enter) was pressed on the last editable cell
35732              * You can use this to trigger add new row.
35733              * @param {SelectionModel} this
35734              */
35735             "tabend" : true,
35736          /**
35737              * @event beforeeditnext
35738              * Fires before the next editable sell is made active
35739              * You can use this to skip to another cell or fire the tabend
35740              *    if you set cell to false
35741              * @param {Object} eventdata object : { cell : [ row, col ] } 
35742              */
35743             "beforeeditnext" : true
35744     });
35745     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35746 };
35747
35748 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35749     
35750     enter_is_tab: false,
35751
35752     /** @ignore */
35753     initEvents : function(){
35754         this.grid.on("mousedown", this.handleMouseDown, this);
35755         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35756         var view = this.grid.view;
35757         view.on("refresh", this.onViewChange, this);
35758         view.on("rowupdated", this.onRowUpdated, this);
35759         view.on("beforerowremoved", this.clearSelections, this);
35760         view.on("beforerowsinserted", this.clearSelections, this);
35761         if(this.grid.isEditor){
35762             this.grid.on("beforeedit", this.beforeEdit,  this);
35763         }
35764     },
35765
35766         //private
35767     beforeEdit : function(e){
35768         this.select(e.row, e.column, false, true, e.record);
35769     },
35770
35771         //private
35772     onRowUpdated : function(v, index, r){
35773         if(this.selection && this.selection.record == r){
35774             v.onCellSelect(index, this.selection.cell[1]);
35775         }
35776     },
35777
35778         //private
35779     onViewChange : function(){
35780         this.clearSelections(true);
35781     },
35782
35783         /**
35784          * Returns the currently selected cell,.
35785          * @return {Array} The selected cell (row, column) or null if none selected.
35786          */
35787     getSelectedCell : function(){
35788         return this.selection ? this.selection.cell : null;
35789     },
35790
35791     /**
35792      * Clears all selections.
35793      * @param {Boolean} true to prevent the gridview from being notified about the change.
35794      */
35795     clearSelections : function(preventNotify){
35796         var s = this.selection;
35797         if(s){
35798             if(preventNotify !== true){
35799                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35800             }
35801             this.selection = null;
35802             this.fireEvent("selectionchange", this, null);
35803         }
35804     },
35805
35806     /**
35807      * Returns true if there is a selection.
35808      * @return {Boolean}
35809      */
35810     hasSelection : function(){
35811         return this.selection ? true : false;
35812     },
35813
35814     /** @ignore */
35815     handleMouseDown : function(e, t){
35816         var v = this.grid.getView();
35817         if(this.isLocked()){
35818             return;
35819         };
35820         var row = v.findRowIndex(t);
35821         var cell = v.findCellIndex(t);
35822         if(row !== false && cell !== false){
35823             this.select(row, cell);
35824         }
35825     },
35826
35827     /**
35828      * Selects a cell.
35829      * @param {Number} rowIndex
35830      * @param {Number} collIndex
35831      */
35832     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35833         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35834             this.clearSelections();
35835             r = r || this.grid.dataSource.getAt(rowIndex);
35836             this.selection = {
35837                 record : r,
35838                 cell : [rowIndex, colIndex]
35839             };
35840             if(!preventViewNotify){
35841                 var v = this.grid.getView();
35842                 v.onCellSelect(rowIndex, colIndex);
35843                 if(preventFocus !== true){
35844                     v.focusCell(rowIndex, colIndex);
35845                 }
35846             }
35847             this.fireEvent("cellselect", this, rowIndex, colIndex);
35848             this.fireEvent("selectionchange", this, this.selection);
35849         }
35850     },
35851
35852         //private
35853     isSelectable : function(rowIndex, colIndex, cm){
35854         return !cm.isHidden(colIndex);
35855     },
35856
35857     /** @ignore */
35858     handleKeyDown : function(e){
35859         //Roo.log('Cell Sel Model handleKeyDown');
35860         if(!e.isNavKeyPress()){
35861             return;
35862         }
35863         var g = this.grid, s = this.selection;
35864         if(!s){
35865             e.stopEvent();
35866             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35867             if(cell){
35868                 this.select(cell[0], cell[1]);
35869             }
35870             return;
35871         }
35872         var sm = this;
35873         var walk = function(row, col, step){
35874             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35875         };
35876         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35877         var newCell;
35878
35879       
35880
35881         switch(k){
35882             case e.TAB:
35883                 // handled by onEditorKey
35884                 if (g.isEditor && g.editing) {
35885                     return;
35886                 }
35887                 if(e.shiftKey) {
35888                     newCell = walk(r, c-1, -1);
35889                 } else {
35890                     newCell = walk(r, c+1, 1);
35891                 }
35892                 break;
35893             
35894             case e.DOWN:
35895                newCell = walk(r+1, c, 1);
35896                 break;
35897             
35898             case e.UP:
35899                 newCell = walk(r-1, c, -1);
35900                 break;
35901             
35902             case e.RIGHT:
35903                 newCell = walk(r, c+1, 1);
35904                 break;
35905             
35906             case e.LEFT:
35907                 newCell = walk(r, c-1, -1);
35908                 break;
35909             
35910             case e.ENTER:
35911                 
35912                 if(g.isEditor && !g.editing){
35913                    g.startEditing(r, c);
35914                    e.stopEvent();
35915                    return;
35916                 }
35917                 
35918                 
35919              break;
35920         };
35921         if(newCell){
35922             this.select(newCell[0], newCell[1]);
35923             e.stopEvent();
35924             
35925         }
35926     },
35927
35928     acceptsNav : function(row, col, cm){
35929         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35930     },
35931     /**
35932      * Selects a cell.
35933      * @param {Number} field (not used) - as it's normally used as a listener
35934      * @param {Number} e - event - fake it by using
35935      *
35936      * var e = Roo.EventObjectImpl.prototype;
35937      * e.keyCode = e.TAB
35938      *
35939      * 
35940      */
35941     onEditorKey : function(field, e){
35942         
35943         var k = e.getKey(),
35944             newCell,
35945             g = this.grid,
35946             ed = g.activeEditor,
35947             forward = false;
35948         ///Roo.log('onEditorKey' + k);
35949         
35950         
35951         if (this.enter_is_tab && k == e.ENTER) {
35952             k = e.TAB;
35953         }
35954         
35955         if(k == e.TAB){
35956             if(e.shiftKey){
35957                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35958             }else{
35959                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35960                 forward = true;
35961             }
35962             
35963             e.stopEvent();
35964             
35965         } else if(k == e.ENTER &&  !e.ctrlKey){
35966             ed.completeEdit();
35967             e.stopEvent();
35968             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35969         
35970                 } else if(k == e.ESC){
35971             ed.cancelEdit();
35972         }
35973                 
35974         if (newCell) {
35975             var ecall = { cell : newCell, forward : forward };
35976             this.fireEvent('beforeeditnext', ecall );
35977             newCell = ecall.cell;
35978                         forward = ecall.forward;
35979         }
35980                 
35981         if(newCell){
35982             //Roo.log('next cell after edit');
35983             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35984         } else if (forward) {
35985             // tabbed past last
35986             this.fireEvent.defer(100, this, ['tabend',this]);
35987         }
35988     }
35989 });/*
35990  * Based on:
35991  * Ext JS Library 1.1.1
35992  * Copyright(c) 2006-2007, Ext JS, LLC.
35993  *
35994  * Originally Released Under LGPL - original licence link has changed is not relivant.
35995  *
35996  * Fork - LGPL
35997  * <script type="text/javascript">
35998  */
35999  
36000 /**
36001  * @class Roo.grid.EditorGrid
36002  * @extends Roo.grid.Grid
36003  * Class for creating and editable grid.
36004  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36005  * The container MUST have some type of size defined for the grid to fill. The container will be 
36006  * automatically set to position relative if it isn't already.
36007  * @param {Object} dataSource The data model to bind to
36008  * @param {Object} colModel The column model with info about this grid's columns
36009  */
36010 Roo.grid.EditorGrid = function(container, config){
36011     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36012     this.getGridEl().addClass("xedit-grid");
36013
36014     if(!this.selModel){
36015         this.selModel = new Roo.grid.CellSelectionModel();
36016     }
36017
36018     this.activeEditor = null;
36019
36020         this.addEvents({
36021             /**
36022              * @event beforeedit
36023              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36024              * <ul style="padding:5px;padding-left:16px;">
36025              * <li>grid - This grid</li>
36026              * <li>record - The record being edited</li>
36027              * <li>field - The field name being edited</li>
36028              * <li>value - The value for the field being edited.</li>
36029              * <li>row - The grid row index</li>
36030              * <li>column - The grid column index</li>
36031              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36032              * </ul>
36033              * @param {Object} e An edit event (see above for description)
36034              */
36035             "beforeedit" : true,
36036             /**
36037              * @event afteredit
36038              * Fires after a cell is edited. <br />
36039              * <ul style="padding:5px;padding-left:16px;">
36040              * <li>grid - This grid</li>
36041              * <li>record - The record being edited</li>
36042              * <li>field - The field name being edited</li>
36043              * <li>value - The value being set</li>
36044              * <li>originalValue - The original value for the field, before the edit.</li>
36045              * <li>row - The grid row index</li>
36046              * <li>column - The grid column index</li>
36047              * </ul>
36048              * @param {Object} e An edit event (see above for description)
36049              */
36050             "afteredit" : true,
36051             /**
36052              * @event validateedit
36053              * Fires after a cell is edited, but before the value is set in the record. 
36054          * You can use this to modify the value being set in the field, Return false
36055              * to cancel the change. The edit event object has the following properties <br />
36056              * <ul style="padding:5px;padding-left:16px;">
36057          * <li>editor - This editor</li>
36058              * <li>grid - This grid</li>
36059              * <li>record - The record being edited</li>
36060              * <li>field - The field name being edited</li>
36061              * <li>value - The value being set</li>
36062              * <li>originalValue - The original value for the field, before the edit.</li>
36063              * <li>row - The grid row index</li>
36064              * <li>column - The grid column index</li>
36065              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36066              * </ul>
36067              * @param {Object} e An edit event (see above for description)
36068              */
36069             "validateedit" : true
36070         });
36071     this.on("bodyscroll", this.stopEditing,  this);
36072     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36073 };
36074
36075 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36076     /**
36077      * @cfg {Number} clicksToEdit
36078      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36079      */
36080     clicksToEdit: 2,
36081
36082     // private
36083     isEditor : true,
36084     // private
36085     trackMouseOver: false, // causes very odd FF errors
36086
36087     onCellDblClick : function(g, row, col){
36088         this.startEditing(row, col);
36089     },
36090
36091     onEditComplete : function(ed, value, startValue){
36092         this.editing = false;
36093         this.activeEditor = null;
36094         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36095         var r = ed.record;
36096         var field = this.colModel.getDataIndex(ed.col);
36097         var e = {
36098             grid: this,
36099             record: r,
36100             field: field,
36101             originalValue: startValue,
36102             value: value,
36103             row: ed.row,
36104             column: ed.col,
36105             cancel:false,
36106             editor: ed
36107         };
36108         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36109         cell.show();
36110           
36111         if(String(value) !== String(startValue)){
36112             
36113             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36114                 r.set(field, e.value);
36115                 // if we are dealing with a combo box..
36116                 // then we also set the 'name' colum to be the displayField
36117                 if (ed.field.displayField && ed.field.name) {
36118                     r.set(ed.field.name, ed.field.el.dom.value);
36119                 }
36120                 
36121                 delete e.cancel; //?? why!!!
36122                 this.fireEvent("afteredit", e);
36123             }
36124         } else {
36125             this.fireEvent("afteredit", e); // always fire it!
36126         }
36127         this.view.focusCell(ed.row, ed.col);
36128     },
36129
36130     /**
36131      * Starts editing the specified for the specified row/column
36132      * @param {Number} rowIndex
36133      * @param {Number} colIndex
36134      */
36135     startEditing : function(row, col){
36136         this.stopEditing();
36137         if(this.colModel.isCellEditable(col, row)){
36138             this.view.ensureVisible(row, col, true);
36139           
36140             var r = this.dataSource.getAt(row);
36141             var field = this.colModel.getDataIndex(col);
36142             var cell = Roo.get(this.view.getCell(row,col));
36143             var e = {
36144                 grid: this,
36145                 record: r,
36146                 field: field,
36147                 value: r.data[field],
36148                 row: row,
36149                 column: col,
36150                 cancel:false 
36151             };
36152             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36153                 this.editing = true;
36154                 var ed = this.colModel.getCellEditor(col, row);
36155                 
36156                 if (!ed) {
36157                     return;
36158                 }
36159                 if(!ed.rendered){
36160                     ed.render(ed.parentEl || document.body);
36161                 }
36162                 ed.field.reset();
36163                
36164                 cell.hide();
36165                 
36166                 (function(){ // complex but required for focus issues in safari, ie and opera
36167                     ed.row = row;
36168                     ed.col = col;
36169                     ed.record = r;
36170                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36171                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36172                     this.activeEditor = ed;
36173                     var v = r.data[field];
36174                     ed.startEdit(this.view.getCell(row, col), v);
36175                     // combo's with 'displayField and name set
36176                     if (ed.field.displayField && ed.field.name) {
36177                         ed.field.el.dom.value = r.data[ed.field.name];
36178                     }
36179                     
36180                     
36181                 }).defer(50, this);
36182             }
36183         }
36184     },
36185         
36186     /**
36187      * Stops any active editing
36188      */
36189     stopEditing : function(){
36190         if(this.activeEditor){
36191             this.activeEditor.completeEdit();
36192         }
36193         this.activeEditor = null;
36194     },
36195         
36196          /**
36197      * Called to get grid's drag proxy text, by default returns this.ddText.
36198      * @return {String}
36199      */
36200     getDragDropText : function(){
36201         var count = this.selModel.getSelectedCell() ? 1 : 0;
36202         return String.format(this.ddText, count, count == 1 ? '' : 's');
36203     }
36204         
36205 });/*
36206  * Based on:
36207  * Ext JS Library 1.1.1
36208  * Copyright(c) 2006-2007, Ext JS, LLC.
36209  *
36210  * Originally Released Under LGPL - original licence link has changed is not relivant.
36211  *
36212  * Fork - LGPL
36213  * <script type="text/javascript">
36214  */
36215
36216 // private - not really -- you end up using it !
36217 // This is a support class used internally by the Grid components
36218
36219 /**
36220  * @class Roo.grid.GridEditor
36221  * @extends Roo.Editor
36222  * Class for creating and editable grid elements.
36223  * @param {Object} config any settings (must include field)
36224  */
36225 Roo.grid.GridEditor = function(field, config){
36226     if (!config && field.field) {
36227         config = field;
36228         field = Roo.factory(config.field, Roo.form);
36229     }
36230     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36231     field.monitorTab = false;
36232 };
36233
36234 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36235     
36236     /**
36237      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36238      */
36239     
36240     alignment: "tl-tl",
36241     autoSize: "width",
36242     hideEl : false,
36243     cls: "x-small-editor x-grid-editor",
36244     shim:false,
36245     shadow:"frame"
36246 });/*
36247  * Based on:
36248  * Ext JS Library 1.1.1
36249  * Copyright(c) 2006-2007, Ext JS, LLC.
36250  *
36251  * Originally Released Under LGPL - original licence link has changed is not relivant.
36252  *
36253  * Fork - LGPL
36254  * <script type="text/javascript">
36255  */
36256   
36257
36258   
36259 Roo.grid.PropertyRecord = Roo.data.Record.create([
36260     {name:'name',type:'string'},  'value'
36261 ]);
36262
36263
36264 Roo.grid.PropertyStore = function(grid, source){
36265     this.grid = grid;
36266     this.store = new Roo.data.Store({
36267         recordType : Roo.grid.PropertyRecord
36268     });
36269     this.store.on('update', this.onUpdate,  this);
36270     if(source){
36271         this.setSource(source);
36272     }
36273     Roo.grid.PropertyStore.superclass.constructor.call(this);
36274 };
36275
36276
36277
36278 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36279     setSource : function(o){
36280         this.source = o;
36281         this.store.removeAll();
36282         var data = [];
36283         for(var k in o){
36284             if(this.isEditableValue(o[k])){
36285                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36286             }
36287         }
36288         this.store.loadRecords({records: data}, {}, true);
36289     },
36290
36291     onUpdate : function(ds, record, type){
36292         if(type == Roo.data.Record.EDIT){
36293             var v = record.data['value'];
36294             var oldValue = record.modified['value'];
36295             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36296                 this.source[record.id] = v;
36297                 record.commit();
36298                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36299             }else{
36300                 record.reject();
36301             }
36302         }
36303     },
36304
36305     getProperty : function(row){
36306        return this.store.getAt(row);
36307     },
36308
36309     isEditableValue: function(val){
36310         if(val && val instanceof Date){
36311             return true;
36312         }else if(typeof val == 'object' || typeof val == 'function'){
36313             return false;
36314         }
36315         return true;
36316     },
36317
36318     setValue : function(prop, value){
36319         this.source[prop] = value;
36320         this.store.getById(prop).set('value', value);
36321     },
36322
36323     getSource : function(){
36324         return this.source;
36325     }
36326 });
36327
36328 Roo.grid.PropertyColumnModel = function(grid, store){
36329     this.grid = grid;
36330     var g = Roo.grid;
36331     g.PropertyColumnModel.superclass.constructor.call(this, [
36332         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36333         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36334     ]);
36335     this.store = store;
36336     this.bselect = Roo.DomHelper.append(document.body, {
36337         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36338             {tag: 'option', value: 'true', html: 'true'},
36339             {tag: 'option', value: 'false', html: 'false'}
36340         ]
36341     });
36342     Roo.id(this.bselect);
36343     var f = Roo.form;
36344     this.editors = {
36345         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36346         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36347         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36348         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36349         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36350     };
36351     this.renderCellDelegate = this.renderCell.createDelegate(this);
36352     this.renderPropDelegate = this.renderProp.createDelegate(this);
36353 };
36354
36355 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36356     
36357     
36358     nameText : 'Name',
36359     valueText : 'Value',
36360     
36361     dateFormat : 'm/j/Y',
36362     
36363     
36364     renderDate : function(dateVal){
36365         return dateVal.dateFormat(this.dateFormat);
36366     },
36367
36368     renderBool : function(bVal){
36369         return bVal ? 'true' : 'false';
36370     },
36371
36372     isCellEditable : function(colIndex, rowIndex){
36373         return colIndex == 1;
36374     },
36375
36376     getRenderer : function(col){
36377         return col == 1 ?
36378             this.renderCellDelegate : this.renderPropDelegate;
36379     },
36380
36381     renderProp : function(v){
36382         return this.getPropertyName(v);
36383     },
36384
36385     renderCell : function(val){
36386         var rv = val;
36387         if(val instanceof Date){
36388             rv = this.renderDate(val);
36389         }else if(typeof val == 'boolean'){
36390             rv = this.renderBool(val);
36391         }
36392         return Roo.util.Format.htmlEncode(rv);
36393     },
36394
36395     getPropertyName : function(name){
36396         var pn = this.grid.propertyNames;
36397         return pn && pn[name] ? pn[name] : name;
36398     },
36399
36400     getCellEditor : function(colIndex, rowIndex){
36401         var p = this.store.getProperty(rowIndex);
36402         var n = p.data['name'], val = p.data['value'];
36403         
36404         if(typeof(this.grid.customEditors[n]) == 'string'){
36405             return this.editors[this.grid.customEditors[n]];
36406         }
36407         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36408             return this.grid.customEditors[n];
36409         }
36410         if(val instanceof Date){
36411             return this.editors['date'];
36412         }else if(typeof val == 'number'){
36413             return this.editors['number'];
36414         }else if(typeof val == 'boolean'){
36415             return this.editors['boolean'];
36416         }else{
36417             return this.editors['string'];
36418         }
36419     }
36420 });
36421
36422 /**
36423  * @class Roo.grid.PropertyGrid
36424  * @extends Roo.grid.EditorGrid
36425  * This class represents the  interface of a component based property grid control.
36426  * <br><br>Usage:<pre><code>
36427  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36428       
36429  });
36430  // set any options
36431  grid.render();
36432  * </code></pre>
36433   
36434  * @constructor
36435  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36436  * The container MUST have some type of size defined for the grid to fill. The container will be
36437  * automatically set to position relative if it isn't already.
36438  * @param {Object} config A config object that sets properties on this grid.
36439  */
36440 Roo.grid.PropertyGrid = function(container, config){
36441     config = config || {};
36442     var store = new Roo.grid.PropertyStore(this);
36443     this.store = store;
36444     var cm = new Roo.grid.PropertyColumnModel(this, store);
36445     store.store.sort('name', 'ASC');
36446     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36447         ds: store.store,
36448         cm: cm,
36449         enableColLock:false,
36450         enableColumnMove:false,
36451         stripeRows:false,
36452         trackMouseOver: false,
36453         clicksToEdit:1
36454     }, config));
36455     this.getGridEl().addClass('x-props-grid');
36456     this.lastEditRow = null;
36457     this.on('columnresize', this.onColumnResize, this);
36458     this.addEvents({
36459          /**
36460              * @event beforepropertychange
36461              * Fires before a property changes (return false to stop?)
36462              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36463              * @param {String} id Record Id
36464              * @param {String} newval New Value
36465          * @param {String} oldval Old Value
36466              */
36467         "beforepropertychange": true,
36468         /**
36469              * @event propertychange
36470              * Fires after a property changes
36471              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36472              * @param {String} id Record Id
36473              * @param {String} newval New Value
36474          * @param {String} oldval Old Value
36475              */
36476         "propertychange": true
36477     });
36478     this.customEditors = this.customEditors || {};
36479 };
36480 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36481     
36482      /**
36483      * @cfg {Object} customEditors map of colnames=> custom editors.
36484      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36485      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36486      * false disables editing of the field.
36487          */
36488     
36489       /**
36490      * @cfg {Object} propertyNames map of property Names to their displayed value
36491          */
36492     
36493     render : function(){
36494         Roo.grid.PropertyGrid.superclass.render.call(this);
36495         this.autoSize.defer(100, this);
36496     },
36497
36498     autoSize : function(){
36499         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36500         if(this.view){
36501             this.view.fitColumns();
36502         }
36503     },
36504
36505     onColumnResize : function(){
36506         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36507         this.autoSize();
36508     },
36509     /**
36510      * Sets the data for the Grid
36511      * accepts a Key => Value object of all the elements avaiable.
36512      * @param {Object} data  to appear in grid.
36513      */
36514     setSource : function(source){
36515         this.store.setSource(source);
36516         //this.autoSize();
36517     },
36518     /**
36519      * Gets all the data from the grid.
36520      * @return {Object} data  data stored in grid
36521      */
36522     getSource : function(){
36523         return this.store.getSource();
36524     }
36525 });/*
36526   
36527  * Licence LGPL
36528  
36529  */
36530  
36531 /**
36532  * @class Roo.grid.Calendar
36533  * @extends Roo.util.Grid
36534  * This class extends the Grid to provide a calendar widget
36535  * <br><br>Usage:<pre><code>
36536  var grid = new Roo.grid.Calendar("my-container-id", {
36537      ds: myDataStore,
36538      cm: myColModel,
36539      selModel: mySelectionModel,
36540      autoSizeColumns: true,
36541      monitorWindowResize: false,
36542      trackMouseOver: true
36543      eventstore : real data store..
36544  });
36545  // set any options
36546  grid.render();
36547   
36548   * @constructor
36549  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36550  * The container MUST have some type of size defined for the grid to fill. The container will be
36551  * automatically set to position relative if it isn't already.
36552  * @param {Object} config A config object that sets properties on this grid.
36553  */
36554 Roo.grid.Calendar = function(container, config){
36555         // initialize the container
36556         this.container = Roo.get(container);
36557         this.container.update("");
36558         this.container.setStyle("overflow", "hidden");
36559     this.container.addClass('x-grid-container');
36560
36561     this.id = this.container.id;
36562
36563     Roo.apply(this, config);
36564     // check and correct shorthanded configs
36565     
36566     var rows = [];
36567     var d =1;
36568     for (var r = 0;r < 6;r++) {
36569         
36570         rows[r]=[];
36571         for (var c =0;c < 7;c++) {
36572             rows[r][c]= '';
36573         }
36574     }
36575     if (this.eventStore) {
36576         this.eventStore= Roo.factory(this.eventStore, Roo.data);
36577         this.eventStore.on('load',this.onLoad, this);
36578         this.eventStore.on('beforeload',this.clearEvents, this);
36579          
36580     }
36581     
36582     this.dataSource = new Roo.data.Store({
36583             proxy: new Roo.data.MemoryProxy(rows),
36584             reader: new Roo.data.ArrayReader({}, [
36585                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36586     });
36587
36588     this.dataSource.load();
36589     this.ds = this.dataSource;
36590     this.ds.xmodule = this.xmodule || false;
36591     
36592     
36593     var cellRender = function(v,x,r)
36594     {
36595         return String.format(
36596             '<div class="fc-day  fc-widget-content"><div>' +
36597                 '<div class="fc-event-container"></div>' +
36598                 '<div class="fc-day-number">{0}</div>'+
36599                 
36600                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36601             '</div></div>', v);
36602     
36603     }
36604     
36605     
36606     this.colModel = new Roo.grid.ColumnModel( [
36607         {
36608             xtype: 'ColumnModel',
36609             xns: Roo.grid,
36610             dataIndex : 'weekday0',
36611             header : 'Sunday',
36612             renderer : cellRender
36613         },
36614         {
36615             xtype: 'ColumnModel',
36616             xns: Roo.grid,
36617             dataIndex : 'weekday1',
36618             header : 'Monday',
36619             renderer : cellRender
36620         },
36621         {
36622             xtype: 'ColumnModel',
36623             xns: Roo.grid,
36624             dataIndex : 'weekday2',
36625             header : 'Tuesday',
36626             renderer : cellRender
36627         },
36628         {
36629             xtype: 'ColumnModel',
36630             xns: Roo.grid,
36631             dataIndex : 'weekday3',
36632             header : 'Wednesday',
36633             renderer : cellRender
36634         },
36635         {
36636             xtype: 'ColumnModel',
36637             xns: Roo.grid,
36638             dataIndex : 'weekday4',
36639             header : 'Thursday',
36640             renderer : cellRender
36641         },
36642         {
36643             xtype: 'ColumnModel',
36644             xns: Roo.grid,
36645             dataIndex : 'weekday5',
36646             header : 'Friday',
36647             renderer : cellRender
36648         },
36649         {
36650             xtype: 'ColumnModel',
36651             xns: Roo.grid,
36652             dataIndex : 'weekday6',
36653             header : 'Saturday',
36654             renderer : cellRender
36655         }
36656     ]);
36657     this.cm = this.colModel;
36658     this.cm.xmodule = this.xmodule || false;
36659  
36660         
36661           
36662     //this.selModel = new Roo.grid.CellSelectionModel();
36663     //this.sm = this.selModel;
36664     //this.selModel.init(this);
36665     
36666     
36667     if(this.width){
36668         this.container.setWidth(this.width);
36669     }
36670
36671     if(this.height){
36672         this.container.setHeight(this.height);
36673     }
36674     /** @private */
36675         this.addEvents({
36676         // raw events
36677         /**
36678          * @event click
36679          * The raw click event for the entire grid.
36680          * @param {Roo.EventObject} e
36681          */
36682         "click" : true,
36683         /**
36684          * @event dblclick
36685          * The raw dblclick event for the entire grid.
36686          * @param {Roo.EventObject} e
36687          */
36688         "dblclick" : true,
36689         /**
36690          * @event contextmenu
36691          * The raw contextmenu event for the entire grid.
36692          * @param {Roo.EventObject} e
36693          */
36694         "contextmenu" : true,
36695         /**
36696          * @event mousedown
36697          * The raw mousedown event for the entire grid.
36698          * @param {Roo.EventObject} e
36699          */
36700         "mousedown" : true,
36701         /**
36702          * @event mouseup
36703          * The raw mouseup event for the entire grid.
36704          * @param {Roo.EventObject} e
36705          */
36706         "mouseup" : true,
36707         /**
36708          * @event mouseover
36709          * The raw mouseover event for the entire grid.
36710          * @param {Roo.EventObject} e
36711          */
36712         "mouseover" : true,
36713         /**
36714          * @event mouseout
36715          * The raw mouseout event for the entire grid.
36716          * @param {Roo.EventObject} e
36717          */
36718         "mouseout" : true,
36719         /**
36720          * @event keypress
36721          * The raw keypress event for the entire grid.
36722          * @param {Roo.EventObject} e
36723          */
36724         "keypress" : true,
36725         /**
36726          * @event keydown
36727          * The raw keydown event for the entire grid.
36728          * @param {Roo.EventObject} e
36729          */
36730         "keydown" : true,
36731
36732         // custom events
36733
36734         /**
36735          * @event cellclick
36736          * Fires when a cell is clicked
36737          * @param {Grid} this
36738          * @param {Number} rowIndex
36739          * @param {Number} columnIndex
36740          * @param {Roo.EventObject} e
36741          */
36742         "cellclick" : true,
36743         /**
36744          * @event celldblclick
36745          * Fires when a cell is double clicked
36746          * @param {Grid} this
36747          * @param {Number} rowIndex
36748          * @param {Number} columnIndex
36749          * @param {Roo.EventObject} e
36750          */
36751         "celldblclick" : true,
36752         /**
36753          * @event rowclick
36754          * Fires when a row is clicked
36755          * @param {Grid} this
36756          * @param {Number} rowIndex
36757          * @param {Roo.EventObject} e
36758          */
36759         "rowclick" : true,
36760         /**
36761          * @event rowdblclick
36762          * Fires when a row is double clicked
36763          * @param {Grid} this
36764          * @param {Number} rowIndex
36765          * @param {Roo.EventObject} e
36766          */
36767         "rowdblclick" : true,
36768         /**
36769          * @event headerclick
36770          * Fires when a header is clicked
36771          * @param {Grid} this
36772          * @param {Number} columnIndex
36773          * @param {Roo.EventObject} e
36774          */
36775         "headerclick" : true,
36776         /**
36777          * @event headerdblclick
36778          * Fires when a header cell is double clicked
36779          * @param {Grid} this
36780          * @param {Number} columnIndex
36781          * @param {Roo.EventObject} e
36782          */
36783         "headerdblclick" : true,
36784         /**
36785          * @event rowcontextmenu
36786          * Fires when a row is right clicked
36787          * @param {Grid} this
36788          * @param {Number} rowIndex
36789          * @param {Roo.EventObject} e
36790          */
36791         "rowcontextmenu" : true,
36792         /**
36793          * @event cellcontextmenu
36794          * Fires when a cell is right clicked
36795          * @param {Grid} this
36796          * @param {Number} rowIndex
36797          * @param {Number} cellIndex
36798          * @param {Roo.EventObject} e
36799          */
36800          "cellcontextmenu" : true,
36801         /**
36802          * @event headercontextmenu
36803          * Fires when a header is right clicked
36804          * @param {Grid} this
36805          * @param {Number} columnIndex
36806          * @param {Roo.EventObject} e
36807          */
36808         "headercontextmenu" : true,
36809         /**
36810          * @event bodyscroll
36811          * Fires when the body element is scrolled
36812          * @param {Number} scrollLeft
36813          * @param {Number} scrollTop
36814          */
36815         "bodyscroll" : true,
36816         /**
36817          * @event columnresize
36818          * Fires when the user resizes a column
36819          * @param {Number} columnIndex
36820          * @param {Number} newSize
36821          */
36822         "columnresize" : true,
36823         /**
36824          * @event columnmove
36825          * Fires when the user moves a column
36826          * @param {Number} oldIndex
36827          * @param {Number} newIndex
36828          */
36829         "columnmove" : true,
36830         /**
36831          * @event startdrag
36832          * Fires when row(s) start being dragged
36833          * @param {Grid} this
36834          * @param {Roo.GridDD} dd The drag drop object
36835          * @param {event} e The raw browser event
36836          */
36837         "startdrag" : true,
36838         /**
36839          * @event enddrag
36840          * Fires when a drag operation is complete
36841          * @param {Grid} this
36842          * @param {Roo.GridDD} dd The drag drop object
36843          * @param {event} e The raw browser event
36844          */
36845         "enddrag" : true,
36846         /**
36847          * @event dragdrop
36848          * Fires when dragged row(s) are dropped on a valid DD target
36849          * @param {Grid} this
36850          * @param {Roo.GridDD} dd The drag drop object
36851          * @param {String} targetId The target drag drop object
36852          * @param {event} e The raw browser event
36853          */
36854         "dragdrop" : true,
36855         /**
36856          * @event dragover
36857          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36858          * @param {Grid} this
36859          * @param {Roo.GridDD} dd The drag drop object
36860          * @param {String} targetId The target drag drop object
36861          * @param {event} e The raw browser event
36862          */
36863         "dragover" : true,
36864         /**
36865          * @event dragenter
36866          *  Fires when the dragged row(s) first cross another DD target while being dragged
36867          * @param {Grid} this
36868          * @param {Roo.GridDD} dd The drag drop object
36869          * @param {String} targetId The target drag drop object
36870          * @param {event} e The raw browser event
36871          */
36872         "dragenter" : true,
36873         /**
36874          * @event dragout
36875          * Fires when the dragged row(s) leave another DD target while being dragged
36876          * @param {Grid} this
36877          * @param {Roo.GridDD} dd The drag drop object
36878          * @param {String} targetId The target drag drop object
36879          * @param {event} e The raw browser event
36880          */
36881         "dragout" : true,
36882         /**
36883          * @event rowclass
36884          * Fires when a row is rendered, so you can change add a style to it.
36885          * @param {GridView} gridview   The grid view
36886          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36887          */
36888         'rowclass' : true,
36889
36890         /**
36891          * @event render
36892          * Fires when the grid is rendered
36893          * @param {Grid} grid
36894          */
36895         'render' : true,
36896             /**
36897              * @event select
36898              * Fires when a date is selected
36899              * @param {DatePicker} this
36900              * @param {Date} date The selected date
36901              */
36902         'select': true,
36903         /**
36904              * @event monthchange
36905              * Fires when the displayed month changes 
36906              * @param {DatePicker} this
36907              * @param {Date} date The selected month
36908              */
36909         'monthchange': true,
36910         /**
36911              * @event evententer
36912              * Fires when mouse over an event
36913              * @param {Calendar} this
36914              * @param {event} Event
36915              */
36916         'evententer': true,
36917         /**
36918              * @event eventleave
36919              * Fires when the mouse leaves an
36920              * @param {Calendar} this
36921              * @param {event}
36922              */
36923         'eventleave': true,
36924         /**
36925              * @event eventclick
36926              * Fires when the mouse click an
36927              * @param {Calendar} this
36928              * @param {event}
36929              */
36930         'eventclick': true,
36931         /**
36932              * @event eventrender
36933              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
36934              * @param {Calendar} this
36935              * @param {data} data to be modified
36936              */
36937         'eventrender': true
36938         
36939     });
36940
36941     Roo.grid.Grid.superclass.constructor.call(this);
36942     this.on('render', function() {
36943         this.view.el.addClass('x-grid-cal'); 
36944         
36945         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
36946
36947     },this);
36948     
36949     if (!Roo.grid.Calendar.style) {
36950         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
36951             
36952             
36953             '.x-grid-cal .x-grid-col' :  {
36954                 height: 'auto !important',
36955                 'vertical-align': 'top'
36956             },
36957             '.x-grid-cal  .fc-event-hori' : {
36958                 height: '14px'
36959             }
36960              
36961             
36962         }, Roo.id());
36963     }
36964
36965     
36966     
36967 };
36968 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
36969     /**
36970      * @cfg {Store} eventStore The store that loads events.
36971      */
36972     eventStore : 25,
36973
36974      
36975     activeDate : false,
36976     startDay : 0,
36977     autoWidth : true,
36978     monitorWindowResize : false,
36979
36980     
36981     resizeColumns : function() {
36982         var col = (this.view.el.getWidth() / 7) - 3;
36983         // loop through cols, and setWidth
36984         for(var i =0 ; i < 7 ; i++){
36985             this.cm.setColumnWidth(i, col);
36986         }
36987     },
36988      setDate :function(date) {
36989         
36990         Roo.log('setDate?');
36991         
36992         this.resizeColumns();
36993         var vd = this.activeDate;
36994         this.activeDate = date;
36995 //        if(vd && this.el){
36996 //            var t = date.getTime();
36997 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
36998 //                Roo.log('using add remove');
36999 //                
37000 //                this.fireEvent('monthchange', this, date);
37001 //                
37002 //                this.cells.removeClass("fc-state-highlight");
37003 //                this.cells.each(function(c){
37004 //                   if(c.dateValue == t){
37005 //                       c.addClass("fc-state-highlight");
37006 //                       setTimeout(function(){
37007 //                            try{c.dom.firstChild.focus();}catch(e){}
37008 //                       }, 50);
37009 //                       return false;
37010 //                   }
37011 //                   return true;
37012 //                });
37013 //                return;
37014 //            }
37015 //        }
37016         
37017         var days = date.getDaysInMonth();
37018         
37019         var firstOfMonth = date.getFirstDateOfMonth();
37020         var startingPos = firstOfMonth.getDay()-this.startDay;
37021         
37022         if(startingPos < this.startDay){
37023             startingPos += 7;
37024         }
37025         
37026         var pm = date.add(Date.MONTH, -1);
37027         var prevStart = pm.getDaysInMonth()-startingPos;
37028 //        
37029         
37030         
37031         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37032         
37033         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37034         //this.cells.addClassOnOver('fc-state-hover');
37035         
37036         var cells = this.cells.elements;
37037         var textEls = this.textNodes;
37038         
37039         //Roo.each(cells, function(cell){
37040         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37041         //});
37042         
37043         days += startingPos;
37044
37045         // convert everything to numbers so it's fast
37046         var day = 86400000;
37047         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37048         //Roo.log(d);
37049         //Roo.log(pm);
37050         //Roo.log(prevStart);
37051         
37052         var today = new Date().clearTime().getTime();
37053         var sel = date.clearTime().getTime();
37054         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37055         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37056         var ddMatch = this.disabledDatesRE;
37057         var ddText = this.disabledDatesText;
37058         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37059         var ddaysText = this.disabledDaysText;
37060         var format = this.format;
37061         
37062         var setCellClass = function(cal, cell){
37063             
37064             //Roo.log('set Cell Class');
37065             cell.title = "";
37066             var t = d.getTime();
37067             
37068             //Roo.log(d);
37069             
37070             
37071             cell.dateValue = t;
37072             if(t == today){
37073                 cell.className += " fc-today";
37074                 cell.className += " fc-state-highlight";
37075                 cell.title = cal.todayText;
37076             }
37077             if(t == sel){
37078                 // disable highlight in other month..
37079                 cell.className += " fc-state-highlight";
37080                 
37081             }
37082             // disabling
37083             if(t < min) {
37084                 //cell.className = " fc-state-disabled";
37085                 cell.title = cal.minText;
37086                 return;
37087             }
37088             if(t > max) {
37089                 //cell.className = " fc-state-disabled";
37090                 cell.title = cal.maxText;
37091                 return;
37092             }
37093             if(ddays){
37094                 if(ddays.indexOf(d.getDay()) != -1){
37095                     // cell.title = ddaysText;
37096                    // cell.className = " fc-state-disabled";
37097                 }
37098             }
37099             if(ddMatch && format){
37100                 var fvalue = d.dateFormat(format);
37101                 if(ddMatch.test(fvalue)){
37102                     cell.title = ddText.replace("%0", fvalue);
37103                    cell.className = " fc-state-disabled";
37104                 }
37105             }
37106             
37107             if (!cell.initialClassName) {
37108                 cell.initialClassName = cell.dom.className;
37109             }
37110             
37111             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37112         };
37113
37114         var i = 0;
37115         
37116         for(; i < startingPos; i++) {
37117             cells[i].dayName =  (++prevStart);
37118             Roo.log(textEls[i]);
37119             d.setDate(d.getDate()+1);
37120             
37121             //cells[i].className = "fc-past fc-other-month";
37122             setCellClass(this, cells[i]);
37123         }
37124         
37125         var intDay = 0;
37126         
37127         for(; i < days; i++){
37128             intDay = i - startingPos + 1;
37129             cells[i].dayName =  (intDay);
37130             d.setDate(d.getDate()+1);
37131             
37132             cells[i].className = ''; // "x-date-active";
37133             setCellClass(this, cells[i]);
37134         }
37135         var extraDays = 0;
37136         
37137         for(; i < 42; i++) {
37138             //textEls[i].innerHTML = (++extraDays);
37139             
37140             d.setDate(d.getDate()+1);
37141             cells[i].dayName = (++extraDays);
37142             cells[i].className = "fc-future fc-other-month";
37143             setCellClass(this, cells[i]);
37144         }
37145         
37146         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37147         
37148         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37149         
37150         // this will cause all the cells to mis
37151         var rows= [];
37152         var i =0;
37153         for (var r = 0;r < 6;r++) {
37154             for (var c =0;c < 7;c++) {
37155                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37156             }    
37157         }
37158         
37159         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37160         for(i=0;i<cells.length;i++) {
37161             
37162             this.cells.elements[i].dayName = cells[i].dayName ;
37163             this.cells.elements[i].className = cells[i].className;
37164             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37165             this.cells.elements[i].title = cells[i].title ;
37166             this.cells.elements[i].dateValue = cells[i].dateValue ;
37167         }
37168         
37169         
37170         
37171         
37172         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37173         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37174         
37175         ////if(totalRows != 6){
37176             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37177            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37178        // }
37179         
37180         this.fireEvent('monthchange', this, date);
37181         
37182         
37183     },
37184  /**
37185      * Returns the grid's SelectionModel.
37186      * @return {SelectionModel}
37187      */
37188     getSelectionModel : function(){
37189         if(!this.selModel){
37190             this.selModel = new Roo.grid.CellSelectionModel();
37191         }
37192         return this.selModel;
37193     },
37194
37195     load: function() {
37196         this.eventStore.load()
37197         
37198         
37199         
37200     },
37201     
37202     findCell : function(dt) {
37203         dt = dt.clearTime().getTime();
37204         var ret = false;
37205         this.cells.each(function(c){
37206             //Roo.log("check " +c.dateValue + '?=' + dt);
37207             if(c.dateValue == dt){
37208                 ret = c;
37209                 return false;
37210             }
37211             return true;
37212         });
37213         
37214         return ret;
37215     },
37216     
37217     findCells : function(rec) {
37218         var s = rec.data.start_dt.clone().clearTime().getTime();
37219        // Roo.log(s);
37220         var e= rec.data.end_dt.clone().clearTime().getTime();
37221        // Roo.log(e);
37222         var ret = [];
37223         this.cells.each(function(c){
37224              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37225             
37226             if(c.dateValue > e){
37227                 return ;
37228             }
37229             if(c.dateValue < s){
37230                 return ;
37231             }
37232             ret.push(c);
37233         });
37234         
37235         return ret;    
37236     },
37237     
37238     findBestRow: function(cells)
37239     {
37240         var ret = 0;
37241         
37242         for (var i =0 ; i < cells.length;i++) {
37243             ret  = Math.max(cells[i].rows || 0,ret);
37244         }
37245         return ret;
37246         
37247     },
37248     
37249     
37250     addItem : function(rec)
37251     {
37252         // look for vertical location slot in
37253         var cells = this.findCells(rec);
37254         
37255         rec.row = this.findBestRow(cells);
37256         
37257         // work out the location.
37258         
37259         var crow = false;
37260         var rows = [];
37261         for(var i =0; i < cells.length; i++) {
37262             if (!crow) {
37263                 crow = {
37264                     start : cells[i],
37265                     end :  cells[i]
37266                 };
37267                 continue;
37268             }
37269             if (crow.start.getY() == cells[i].getY()) {
37270                 // on same row.
37271                 crow.end = cells[i];
37272                 continue;
37273             }
37274             // different row.
37275             rows.push(crow);
37276             crow = {
37277                 start: cells[i],
37278                 end : cells[i]
37279             };
37280             
37281         }
37282         
37283         rows.push(crow);
37284         rec.els = [];
37285         rec.rows = rows;
37286         rec.cells = cells;
37287         for (var i = 0; i < cells.length;i++) {
37288             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37289             
37290         }
37291         
37292         
37293     },
37294     
37295     clearEvents: function() {
37296         
37297         if (!this.eventStore.getCount()) {
37298             return;
37299         }
37300         // reset number of rows in cells.
37301         Roo.each(this.cells.elements, function(c){
37302             c.rows = 0;
37303         });
37304         
37305         this.eventStore.each(function(e) {
37306             this.clearEvent(e);
37307         },this);
37308         
37309     },
37310     
37311     clearEvent : function(ev)
37312     {
37313         if (ev.els) {
37314             Roo.each(ev.els, function(el) {
37315                 el.un('mouseenter' ,this.onEventEnter, this);
37316                 el.un('mouseleave' ,this.onEventLeave, this);
37317                 el.remove();
37318             },this);
37319             ev.els = [];
37320         }
37321     },
37322     
37323     
37324     renderEvent : function(ev,ctr) {
37325         if (!ctr) {
37326              ctr = this.view.el.select('.fc-event-container',true).first();
37327         }
37328         
37329          
37330         this.clearEvent(ev);
37331             //code
37332        
37333         
37334         
37335         ev.els = [];
37336         var cells = ev.cells;
37337         var rows = ev.rows;
37338         this.fireEvent('eventrender', this, ev);
37339         
37340         for(var i =0; i < rows.length; i++) {
37341             
37342             cls = '';
37343             if (i == 0) {
37344                 cls += ' fc-event-start';
37345             }
37346             if ((i+1) == rows.length) {
37347                 cls += ' fc-event-end';
37348             }
37349             
37350             //Roo.log(ev.data);
37351             // how many rows should it span..
37352             var cg = this.eventTmpl.append(ctr,Roo.apply({
37353                 fccls : cls
37354                 
37355             }, ev.data) , true);
37356             
37357             
37358             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37359             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37360             cg.on('click', this.onEventClick, this, ev);
37361             
37362             ev.els.push(cg);
37363             
37364             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37365             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37366             //Roo.log(cg);
37367              
37368             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37369             cg.setWidth(ebox.right - sbox.x -2);
37370         }
37371     },
37372     
37373     renderEvents: function()
37374     {   
37375         // first make sure there is enough space..
37376         
37377         if (!this.eventTmpl) {
37378             this.eventTmpl = new Roo.Template(
37379                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37380                     '<div class="fc-event-inner">' +
37381                         '<span class="fc-event-time">{time}</span>' +
37382                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37383                     '</div>' +
37384                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37385                 '</div>'
37386             );
37387                 
37388         }
37389                
37390         
37391         
37392         this.cells.each(function(c) {
37393             //Roo.log(c.select('.fc-day-content div',true).first());
37394             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37395         });
37396         
37397         var ctr = this.view.el.select('.fc-event-container',true).first();
37398         
37399         var cls;
37400         this.eventStore.each(function(ev){
37401             
37402             this.renderEvent(ev);
37403              
37404              
37405         }, this);
37406         this.view.layout();
37407         
37408     },
37409     
37410     onEventEnter: function (e, el,event,d) {
37411         this.fireEvent('evententer', this, el, event);
37412     },
37413     
37414     onEventLeave: function (e, el,event,d) {
37415         this.fireEvent('eventleave', this, el, event);
37416     },
37417     
37418     onEventClick: function (e, el,event,d) {
37419         this.fireEvent('eventclick', this, el, event);
37420     },
37421     
37422     onMonthChange: function () {
37423         this.store.load();
37424     },
37425     
37426     onLoad: function () {
37427         
37428         //Roo.log('calendar onload');
37429 //         
37430         if(this.eventStore.getCount() > 0){
37431             
37432            
37433             
37434             this.eventStore.each(function(d){
37435                 
37436                 
37437                 // FIXME..
37438                 var add =   d.data;
37439                 if (typeof(add.end_dt) == 'undefined')  {
37440                     Roo.log("Missing End time in calendar data: ");
37441                     Roo.log(d);
37442                     return;
37443                 }
37444                 if (typeof(add.start_dt) == 'undefined')  {
37445                     Roo.log("Missing Start time in calendar data: ");
37446                     Roo.log(d);
37447                     return;
37448                 }
37449                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37450                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37451                 add.id = add.id || d.id;
37452                 add.title = add.title || '??';
37453                 
37454                 this.addItem(d);
37455                 
37456              
37457             },this);
37458         }
37459         
37460         this.renderEvents();
37461     }
37462     
37463
37464 });
37465 /*
37466  grid : {
37467                 xtype: 'Grid',
37468                 xns: Roo.grid,
37469                 listeners : {
37470                     render : function ()
37471                     {
37472                         _this.grid = this;
37473                         
37474                         if (!this.view.el.hasClass('course-timesheet')) {
37475                             this.view.el.addClass('course-timesheet');
37476                         }
37477                         if (this.tsStyle) {
37478                             this.ds.load({});
37479                             return; 
37480                         }
37481                         Roo.log('width');
37482                         Roo.log(_this.grid.view.el.getWidth());
37483                         
37484                         
37485                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37486                             '.course-timesheet .x-grid-row' : {
37487                                 height: '80px'
37488                             },
37489                             '.x-grid-row td' : {
37490                                 'vertical-align' : 0
37491                             },
37492                             '.course-edit-link' : {
37493                                 'color' : 'blue',
37494                                 'text-overflow' : 'ellipsis',
37495                                 'overflow' : 'hidden',
37496                                 'white-space' : 'nowrap',
37497                                 'cursor' : 'pointer'
37498                             },
37499                             '.sub-link' : {
37500                                 'color' : 'green'
37501                             },
37502                             '.de-act-sup-link' : {
37503                                 'color' : 'purple',
37504                                 'text-decoration' : 'line-through'
37505                             },
37506                             '.de-act-link' : {
37507                                 'color' : 'red',
37508                                 'text-decoration' : 'line-through'
37509                             },
37510                             '.course-timesheet .course-highlight' : {
37511                                 'border-top-style': 'dashed !important',
37512                                 'border-bottom-bottom': 'dashed !important'
37513                             },
37514                             '.course-timesheet .course-item' : {
37515                                 'font-family'   : 'tahoma, arial, helvetica',
37516                                 'font-size'     : '11px',
37517                                 'overflow'      : 'hidden',
37518                                 'padding-left'  : '10px',
37519                                 'padding-right' : '10px',
37520                                 'padding-top' : '10px' 
37521                             }
37522                             
37523                         }, Roo.id());
37524                                 this.ds.load({});
37525                     }
37526                 },
37527                 autoWidth : true,
37528                 monitorWindowResize : false,
37529                 cellrenderer : function(v,x,r)
37530                 {
37531                     return v;
37532                 },
37533                 sm : {
37534                     xtype: 'CellSelectionModel',
37535                     xns: Roo.grid
37536                 },
37537                 dataSource : {
37538                     xtype: 'Store',
37539                     xns: Roo.data,
37540                     listeners : {
37541                         beforeload : function (_self, options)
37542                         {
37543                             options.params = options.params || {};
37544                             options.params._month = _this.monthField.getValue();
37545                             options.params.limit = 9999;
37546                             options.params['sort'] = 'when_dt';    
37547                             options.params['dir'] = 'ASC';    
37548                             this.proxy.loadResponse = this.loadResponse;
37549                             Roo.log("load?");
37550                             //this.addColumns();
37551                         },
37552                         load : function (_self, records, options)
37553                         {
37554                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37555                                 // if you click on the translation.. you can edit it...
37556                                 var el = Roo.get(this);
37557                                 var id = el.dom.getAttribute('data-id');
37558                                 var d = el.dom.getAttribute('data-date');
37559                                 var t = el.dom.getAttribute('data-time');
37560                                 //var id = this.child('span').dom.textContent;
37561                                 
37562                                 //Roo.log(this);
37563                                 Pman.Dialog.CourseCalendar.show({
37564                                     id : id,
37565                                     when_d : d,
37566                                     when_t : t,
37567                                     productitem_active : id ? 1 : 0
37568                                 }, function() {
37569                                     _this.grid.ds.load({});
37570                                 });
37571                            
37572                            });
37573                            
37574                            _this.panel.fireEvent('resize', [ '', '' ]);
37575                         }
37576                     },
37577                     loadResponse : function(o, success, response){
37578                             // this is overridden on before load..
37579                             
37580                             Roo.log("our code?");       
37581                             //Roo.log(success);
37582                             //Roo.log(response)
37583                             delete this.activeRequest;
37584                             if(!success){
37585                                 this.fireEvent("loadexception", this, o, response);
37586                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37587                                 return;
37588                             }
37589                             var result;
37590                             try {
37591                                 result = o.reader.read(response);
37592                             }catch(e){
37593                                 Roo.log("load exception?");
37594                                 this.fireEvent("loadexception", this, o, response, e);
37595                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37596                                 return;
37597                             }
37598                             Roo.log("ready...");        
37599                             // loop through result.records;
37600                             // and set this.tdate[date] = [] << array of records..
37601                             _this.tdata  = {};
37602                             Roo.each(result.records, function(r){
37603                                 //Roo.log(r.data);
37604                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37605                                     _this.tdata[r.data.when_dt.format('j')] = [];
37606                                 }
37607                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37608                             });
37609                             
37610                             //Roo.log(_this.tdata);
37611                             
37612                             result.records = [];
37613                             result.totalRecords = 6;
37614                     
37615                             // let's generate some duumy records for the rows.
37616                             //var st = _this.dateField.getValue();
37617                             
37618                             // work out monday..
37619                             //st = st.add(Date.DAY, -1 * st.format('w'));
37620                             
37621                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37622                             
37623                             var firstOfMonth = date.getFirstDayOfMonth();
37624                             var days = date.getDaysInMonth();
37625                             var d = 1;
37626                             var firstAdded = false;
37627                             for (var i = 0; i < result.totalRecords ; i++) {
37628                                 //var d= st.add(Date.DAY, i);
37629                                 var row = {};
37630                                 var added = 0;
37631                                 for(var w = 0 ; w < 7 ; w++){
37632                                     if(!firstAdded && firstOfMonth != w){
37633                                         continue;
37634                                     }
37635                                     if(d > days){
37636                                         continue;
37637                                     }
37638                                     firstAdded = true;
37639                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
37640                                     row['weekday'+w] = String.format(
37641                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
37642                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
37643                                                     d,
37644                                                     date.format('Y-m-')+dd
37645                                                 );
37646                                     added++;
37647                                     if(typeof(_this.tdata[d]) != 'undefined'){
37648                                         Roo.each(_this.tdata[d], function(r){
37649                                             var is_sub = '';
37650                                             var deactive = '';
37651                                             var id = r.id;
37652                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
37653                                             if(r.parent_id*1>0){
37654                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
37655                                                 id = r.parent_id;
37656                                             }
37657                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
37658                                                 deactive = 'de-act-link';
37659                                             }
37660                                             
37661                                             row['weekday'+w] += String.format(
37662                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
37663                                                     id, //0
37664                                                     r.product_id_name, //1
37665                                                     r.when_dt.format('h:ia'), //2
37666                                                     is_sub, //3
37667                                                     deactive, //4
37668                                                     desc // 5
37669                                             );
37670                                         });
37671                                     }
37672                                     d++;
37673                                 }
37674                                 
37675                                 // only do this if something added..
37676                                 if(added > 0){ 
37677                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
37678                                 }
37679                                 
37680                                 
37681                                 // push it twice. (second one with an hour..
37682                                 
37683                             }
37684                             //Roo.log(result);
37685                             this.fireEvent("load", this, o, o.request.arg);
37686                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
37687                         },
37688                     sortInfo : {field: 'when_dt', direction : 'ASC' },
37689                     proxy : {
37690                         xtype: 'HttpProxy',
37691                         xns: Roo.data,
37692                         method : 'GET',
37693                         url : baseURL + '/Roo/Shop_course.php'
37694                     },
37695                     reader : {
37696                         xtype: 'JsonReader',
37697                         xns: Roo.data,
37698                         id : 'id',
37699                         fields : [
37700                             {
37701                                 'name': 'id',
37702                                 'type': 'int'
37703                             },
37704                             {
37705                                 'name': 'when_dt',
37706                                 'type': 'string'
37707                             },
37708                             {
37709                                 'name': 'end_dt',
37710                                 'type': 'string'
37711                             },
37712                             {
37713                                 'name': 'parent_id',
37714                                 'type': 'int'
37715                             },
37716                             {
37717                                 'name': 'product_id',
37718                                 'type': 'int'
37719                             },
37720                             {
37721                                 'name': 'productitem_id',
37722                                 'type': 'int'
37723                             },
37724                             {
37725                                 'name': 'guid',
37726                                 'type': 'int'
37727                             }
37728                         ]
37729                     }
37730                 },
37731                 toolbar : {
37732                     xtype: 'Toolbar',
37733                     xns: Roo,
37734                     items : [
37735                         {
37736                             xtype: 'Button',
37737                             xns: Roo.Toolbar,
37738                             listeners : {
37739                                 click : function (_self, e)
37740                                 {
37741                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37742                                     sd.setMonth(sd.getMonth()-1);
37743                                     _this.monthField.setValue(sd.format('Y-m-d'));
37744                                     _this.grid.ds.load({});
37745                                 }
37746                             },
37747                             text : "Back"
37748                         },
37749                         {
37750                             xtype: 'Separator',
37751                             xns: Roo.Toolbar
37752                         },
37753                         {
37754                             xtype: 'MonthField',
37755                             xns: Roo.form,
37756                             listeners : {
37757                                 render : function (_self)
37758                                 {
37759                                     _this.monthField = _self;
37760                                    // _this.monthField.set  today
37761                                 },
37762                                 select : function (combo, date)
37763                                 {
37764                                     _this.grid.ds.load({});
37765                                 }
37766                             },
37767                             value : (function() { return new Date(); })()
37768                         },
37769                         {
37770                             xtype: 'Separator',
37771                             xns: Roo.Toolbar
37772                         },
37773                         {
37774                             xtype: 'TextItem',
37775                             xns: Roo.Toolbar,
37776                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
37777                         },
37778                         {
37779                             xtype: 'Fill',
37780                             xns: Roo.Toolbar
37781                         },
37782                         {
37783                             xtype: 'Button',
37784                             xns: Roo.Toolbar,
37785                             listeners : {
37786                                 click : function (_self, e)
37787                                 {
37788                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37789                                     sd.setMonth(sd.getMonth()+1);
37790                                     _this.monthField.setValue(sd.format('Y-m-d'));
37791                                     _this.grid.ds.load({});
37792                                 }
37793                             },
37794                             text : "Next"
37795                         }
37796                     ]
37797                 },
37798                  
37799             }
37800         };
37801         
37802         *//*
37803  * Based on:
37804  * Ext JS Library 1.1.1
37805  * Copyright(c) 2006-2007, Ext JS, LLC.
37806  *
37807  * Originally Released Under LGPL - original licence link has changed is not relivant.
37808  *
37809  * Fork - LGPL
37810  * <script type="text/javascript">
37811  */
37812  
37813 /**
37814  * @class Roo.LoadMask
37815  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37816  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37817  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37818  * element's UpdateManager load indicator and will be destroyed after the initial load.
37819  * @constructor
37820  * Create a new LoadMask
37821  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37822  * @param {Object} config The config object
37823  */
37824 Roo.LoadMask = function(el, config){
37825     this.el = Roo.get(el);
37826     Roo.apply(this, config);
37827     if(this.store){
37828         this.store.on('beforeload', this.onBeforeLoad, this);
37829         this.store.on('load', this.onLoad, this);
37830         this.store.on('loadexception', this.onLoadException, this);
37831         this.removeMask = false;
37832     }else{
37833         var um = this.el.getUpdateManager();
37834         um.showLoadIndicator = false; // disable the default indicator
37835         um.on('beforeupdate', this.onBeforeLoad, this);
37836         um.on('update', this.onLoad, this);
37837         um.on('failure', this.onLoad, this);
37838         this.removeMask = true;
37839     }
37840 };
37841
37842 Roo.LoadMask.prototype = {
37843     /**
37844      * @cfg {Boolean} removeMask
37845      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37846      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37847      */
37848     /**
37849      * @cfg {String} msg
37850      * The text to display in a centered loading message box (defaults to 'Loading...')
37851      */
37852     msg : 'Loading...',
37853     /**
37854      * @cfg {String} msgCls
37855      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37856      */
37857     msgCls : 'x-mask-loading',
37858
37859     /**
37860      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37861      * @type Boolean
37862      */
37863     disabled: false,
37864
37865     /**
37866      * Disables the mask to prevent it from being displayed
37867      */
37868     disable : function(){
37869        this.disabled = true;
37870     },
37871
37872     /**
37873      * Enables the mask so that it can be displayed
37874      */
37875     enable : function(){
37876         this.disabled = false;
37877     },
37878     
37879     onLoadException : function()
37880     {
37881         Roo.log(arguments);
37882         
37883         if (typeof(arguments[3]) != 'undefined') {
37884             Roo.MessageBox.alert("Error loading",arguments[3]);
37885         } 
37886         /*
37887         try {
37888             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37889                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37890             }   
37891         } catch(e) {
37892             
37893         }
37894         */
37895     
37896         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37897     },
37898     // private
37899     onLoad : function()
37900     {
37901         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37902     },
37903
37904     // private
37905     onBeforeLoad : function(){
37906         if(!this.disabled){
37907             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
37908         }
37909     },
37910
37911     // private
37912     destroy : function(){
37913         if(this.store){
37914             this.store.un('beforeload', this.onBeforeLoad, this);
37915             this.store.un('load', this.onLoad, this);
37916             this.store.un('loadexception', this.onLoadException, this);
37917         }else{
37918             var um = this.el.getUpdateManager();
37919             um.un('beforeupdate', this.onBeforeLoad, this);
37920             um.un('update', this.onLoad, this);
37921             um.un('failure', this.onLoad, this);
37922         }
37923     }
37924 };/*
37925  * Based on:
37926  * Ext JS Library 1.1.1
37927  * Copyright(c) 2006-2007, Ext JS, LLC.
37928  *
37929  * Originally Released Under LGPL - original licence link has changed is not relivant.
37930  *
37931  * Fork - LGPL
37932  * <script type="text/javascript">
37933  */
37934
37935
37936 /**
37937  * @class Roo.XTemplate
37938  * @extends Roo.Template
37939  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
37940 <pre><code>
37941 var t = new Roo.XTemplate(
37942         '&lt;select name="{name}"&gt;',
37943                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
37944         '&lt;/select&gt;'
37945 );
37946  
37947 // then append, applying the master template values
37948  </code></pre>
37949  *
37950  * Supported features:
37951  *
37952  *  Tags:
37953
37954 <pre><code>
37955       {a_variable} - output encoded.
37956       {a_variable.format:("Y-m-d")} - call a method on the variable
37957       {a_variable:raw} - unencoded output
37958       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
37959       {a_variable:this.method_on_template(...)} - call a method on the template object.
37960  
37961 </code></pre>
37962  *  The tpl tag:
37963 <pre><code>
37964         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
37965         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
37966         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
37967         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
37968   
37969         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
37970         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
37971 </code></pre>
37972  *      
37973  */
37974 Roo.XTemplate = function()
37975 {
37976     Roo.XTemplate.superclass.constructor.apply(this, arguments);
37977     if (this.html) {
37978         this.compile();
37979     }
37980 };
37981
37982
37983 Roo.extend(Roo.XTemplate, Roo.Template, {
37984
37985     /**
37986      * The various sub templates
37987      */
37988     tpls : false,
37989     /**
37990      *
37991      * basic tag replacing syntax
37992      * WORD:WORD()
37993      *
37994      * // you can fake an object call by doing this
37995      *  x.t:(test,tesT) 
37996      * 
37997      */
37998     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37999
38000     /**
38001      * compile the template
38002      *
38003      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38004      *
38005      */
38006     compile: function()
38007     {
38008         var s = this.html;
38009      
38010         s = ['<tpl>', s, '</tpl>'].join('');
38011     
38012         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38013             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38014             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38015             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38016             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38017             m,
38018             id     = 0,
38019             tpls   = [];
38020     
38021         while(true == !!(m = s.match(re))){
38022             var forMatch   = m[0].match(nameRe),
38023                 ifMatch   = m[0].match(ifRe),
38024                 execMatch   = m[0].match(execRe),
38025                 namedMatch   = m[0].match(namedRe),
38026                 
38027                 exp  = null, 
38028                 fn   = null,
38029                 exec = null,
38030                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38031                 
38032             if (ifMatch) {
38033                 // if - puts fn into test..
38034                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38035                 if(exp){
38036                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38037                 }
38038             }
38039             
38040             if (execMatch) {
38041                 // exec - calls a function... returns empty if true is  returned.
38042                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38043                 if(exp){
38044                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38045                 }
38046             }
38047             
38048             
38049             if (name) {
38050                 // for = 
38051                 switch(name){
38052                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38053                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38054                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38055                 }
38056             }
38057             var uid = namedMatch ? namedMatch[1] : id;
38058             
38059             
38060             tpls.push({
38061                 id:     namedMatch ? namedMatch[1] : id,
38062                 target: name,
38063                 exec:   exec,
38064                 test:   fn,
38065                 body:   m[1] || ''
38066             });
38067             if (namedMatch) {
38068                 s = s.replace(m[0], '');
38069             } else { 
38070                 s = s.replace(m[0], '{xtpl'+ id + '}');
38071             }
38072             ++id;
38073         }
38074         this.tpls = [];
38075         for(var i = tpls.length-1; i >= 0; --i){
38076             this.compileTpl(tpls[i]);
38077             this.tpls[tpls[i].id] = tpls[i];
38078         }
38079         this.master = tpls[tpls.length-1];
38080         return this;
38081     },
38082     /**
38083      * same as applyTemplate, except it's done to one of the subTemplates
38084      * when using named templates, you can do:
38085      *
38086      * var str = pl.applySubTemplate('your-name', values);
38087      *
38088      * 
38089      * @param {Number} id of the template
38090      * @param {Object} values to apply to template
38091      * @param {Object} parent (normaly the instance of this object)
38092      */
38093     applySubTemplate : function(id, values, parent)
38094     {
38095         
38096         
38097         var t = this.tpls[id];
38098         
38099         
38100         try { 
38101             if(t.test && !t.test.call(this, values, parent)){
38102                 return '';
38103             }
38104         } catch(e) {
38105             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38106             Roo.log(e.toString());
38107             Roo.log(t.test);
38108             return ''
38109         }
38110         try { 
38111             
38112             if(t.exec && t.exec.call(this, values, parent)){
38113                 return '';
38114             }
38115         } catch(e) {
38116             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38117             Roo.log(e.toString());
38118             Roo.log(t.exec);
38119             return ''
38120         }
38121         try {
38122             var vs = t.target ? t.target.call(this, values, parent) : values;
38123             parent = t.target ? values : parent;
38124             if(t.target && vs instanceof Array){
38125                 var buf = [];
38126                 for(var i = 0, len = vs.length; i < len; i++){
38127                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38128                 }
38129                 return buf.join('');
38130             }
38131             return t.compiled.call(this, vs, parent);
38132         } catch (e) {
38133             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38134             Roo.log(e.toString());
38135             Roo.log(t.compiled);
38136             return '';
38137         }
38138     },
38139
38140     compileTpl : function(tpl)
38141     {
38142         var fm = Roo.util.Format;
38143         var useF = this.disableFormats !== true;
38144         var sep = Roo.isGecko ? "+" : ",";
38145         var undef = function(str) {
38146             Roo.log("Property not found :"  + str);
38147             return '';
38148         };
38149         
38150         var fn = function(m, name, format, args)
38151         {
38152             //Roo.log(arguments);
38153             args = args ? args.replace(/\\'/g,"'") : args;
38154             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38155             if (typeof(format) == 'undefined') {
38156                 format= 'htmlEncode';
38157             }
38158             if (format == 'raw' ) {
38159                 format = false;
38160             }
38161             
38162             if(name.substr(0, 4) == 'xtpl'){
38163                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38164             }
38165             
38166             // build an array of options to determine if value is undefined..
38167             
38168             // basically get 'xxxx.yyyy' then do
38169             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38170             //    (function () { Roo.log("Property not found"); return ''; })() :
38171             //    ......
38172             
38173             var udef_ar = [];
38174             var lookfor = '';
38175             Roo.each(name.split('.'), function(st) {
38176                 lookfor += (lookfor.length ? '.': '') + st;
38177                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38178             });
38179             
38180             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38181             
38182             
38183             if(format && useF){
38184                 
38185                 args = args ? ',' + args : "";
38186                  
38187                 if(format.substr(0, 5) != "this."){
38188                     format = "fm." + format + '(';
38189                 }else{
38190                     format = 'this.call("'+ format.substr(5) + '", ';
38191                     args = ", values";
38192                 }
38193                 
38194                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38195             }
38196              
38197             if (args.length) {
38198                 // called with xxyx.yuu:(test,test)
38199                 // change to ()
38200                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38201             }
38202             // raw.. - :raw modifier..
38203             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38204             
38205         };
38206         var body;
38207         // branched to use + in gecko and [].join() in others
38208         if(Roo.isGecko){
38209             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38210                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38211                     "';};};";
38212         }else{
38213             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38214             body.push(tpl.body.replace(/(\r\n|\n)/g,
38215                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38216             body.push("'].join('');};};");
38217             body = body.join('');
38218         }
38219         
38220         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38221        
38222         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38223         eval(body);
38224         
38225         return this;
38226     },
38227
38228     applyTemplate : function(values){
38229         return this.master.compiled.call(this, values, {});
38230         //var s = this.subs;
38231     },
38232
38233     apply : function(){
38234         return this.applyTemplate.apply(this, arguments);
38235     }
38236
38237  });
38238
38239 Roo.XTemplate.from = function(el){
38240     el = Roo.getDom(el);
38241     return new Roo.XTemplate(el.value || el.innerHTML);
38242 };