roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787
788     /**
789      * Gets the number of cached records.
790      * <p>
791      * <em>If using paging, this may not be the total size of the dataset. If the data object
792      * used by the Reader contains the dataset size, then the getTotalCount() function returns
793      * the data set size</em>
794      */
795     getCount : function(){
796         return this.data.length || 0;
797     },
798
799     /**
800      * Gets the total number of records in the dataset as returned by the server.
801      * <p>
802      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
803      * the dataset size</em>
804      */
805     getTotalCount : function(){
806         return this.totalLength || 0;
807     },
808
809     /**
810      * Returns the sort state of the Store as an object with two properties:
811      * <pre><code>
812  field {String} The name of the field by which the Records are sorted
813  direction {String} The sort order, "ASC" or "DESC"
814      * </code></pre>
815      */
816     getSortState : function(){
817         return this.sortInfo;
818     },
819
820     // private
821     applySort : function(){
822         if(this.sortInfo && !this.remoteSort){
823             var s = this.sortInfo, f = s.field;
824             var st = this.fields.get(f).sortType;
825             var fn = function(r1, r2){
826                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
827                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
828             };
829             this.data.sort(s.direction, fn);
830             if(this.snapshot && this.snapshot != this.data){
831                 this.snapshot.sort(s.direction, fn);
832             }
833         }
834     },
835
836     /**
837      * Sets the default sort column and order to be used by the next load operation.
838      * @param {String} fieldName The name of the field to sort by.
839      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
840      */
841     setDefaultSort : function(field, dir){
842         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
843     },
844
845     /**
846      * Sort the Records.
847      * If remote sorting is used, the sort is performed on the server, and the cache is
848      * reloaded. If local sorting is used, the cache is sorted internally.
849      * @param {String} fieldName The name of the field to sort by.
850      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
851      */
852     sort : function(fieldName, dir){
853         var f = this.fields.get(fieldName);
854         if(!dir){
855             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
856             
857             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
858                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
859             }else{
860                 dir = f.sortDir;
861             }
862         }
863         this.sortToggle[f.name] = dir;
864         this.sortInfo = {field: f.name, direction: dir};
865         if(!this.remoteSort){
866             this.applySort();
867             this.fireEvent("datachanged", this);
868         }else{
869             this.load(this.lastOptions);
870         }
871     },
872
873     /**
874      * Calls the specified function for each of the Records in the cache.
875      * @param {Function} fn The function to call. The Record is passed as the first parameter.
876      * Returning <em>false</em> aborts and exits the iteration.
877      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
878      */
879     each : function(fn, scope){
880         this.data.each(fn, scope);
881     },
882
883     /**
884      * Gets all records modified since the last commit.  Modified records are persisted across load operations
885      * (e.g., during paging).
886      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
887      */
888     getModifiedRecords : function(){
889         return this.modified;
890     },
891
892     // private
893     createFilterFn : function(property, value, anyMatch){
894         if(!value.exec){ // not a regex
895             value = String(value);
896             if(value.length == 0){
897                 return false;
898             }
899             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
900         }
901         return function(r){
902             return value.test(r.data[property]);
903         };
904     },
905
906     /**
907      * Sums the value of <i>property</i> for each record between start and end and returns the result.
908      * @param {String} property A field on your records
909      * @param {Number} start The record index to start at (defaults to 0)
910      * @param {Number} end The last record index to include (defaults to length - 1)
911      * @return {Number} The sum
912      */
913     sum : function(property, start, end){
914         var rs = this.data.items, v = 0;
915         start = start || 0;
916         end = (end || end === 0) ? end : rs.length-1;
917
918         for(var i = start; i <= end; i++){
919             v += (rs[i].data[property] || 0);
920         }
921         return v;
922     },
923
924     /**
925      * Filter the records by a specified property.
926      * @param {String} field A field on your records
927      * @param {String/RegExp} value Either a string that the field
928      * should start with or a RegExp to test against the field
929      * @param {Boolean} anyMatch True to match any part not just the beginning
930      */
931     filter : function(property, value, anyMatch){
932         var fn = this.createFilterFn(property, value, anyMatch);
933         return fn ? this.filterBy(fn) : this.clearFilter();
934     },
935
936     /**
937      * Filter by a function. The specified function will be called with each
938      * record in this data source. If the function returns true the record is included,
939      * otherwise it is filtered.
940      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
941      * @param {Object} scope (optional) The scope of the function (defaults to this)
942      */
943     filterBy : function(fn, scope){
944         this.snapshot = this.snapshot || this.data;
945         this.data = this.queryBy(fn, scope||this);
946         this.fireEvent("datachanged", this);
947     },
948
949     /**
950      * Query the records by a specified property.
951      * @param {String} field A field on your records
952      * @param {String/RegExp} value Either a string that the field
953      * should start with or a RegExp to test against the field
954      * @param {Boolean} anyMatch True to match any part not just the beginning
955      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
956      */
957     query : function(property, value, anyMatch){
958         var fn = this.createFilterFn(property, value, anyMatch);
959         return fn ? this.queryBy(fn) : this.data.clone();
960     },
961
962     /**
963      * Query by a function. The specified function will be called with each
964      * record in this data source. If the function returns true the record is included
965      * in the results.
966      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
967      * @param {Object} scope (optional) The scope of the function (defaults to this)
968       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
969      **/
970     queryBy : function(fn, scope){
971         var data = this.snapshot || this.data;
972         return data.filterBy(fn, scope||this);
973     },
974
975     /**
976      * Collects unique values for a particular dataIndex from this store.
977      * @param {String} dataIndex The property to collect
978      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
979      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
980      * @return {Array} An array of the unique values
981      **/
982     collect : function(dataIndex, allowNull, bypassFilter){
983         var d = (bypassFilter === true && this.snapshot) ?
984                 this.snapshot.items : this.data.items;
985         var v, sv, r = [], l = {};
986         for(var i = 0, len = d.length; i < len; i++){
987             v = d[i].data[dataIndex];
988             sv = String(v);
989             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
990                 l[sv] = true;
991                 r[r.length] = v;
992             }
993         }
994         return r;
995     },
996
997     /**
998      * Revert to a view of the Record cache with no filtering applied.
999      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1000      */
1001     clearFilter : function(suppressEvent){
1002         if(this.snapshot && this.snapshot != this.data){
1003             this.data = this.snapshot;
1004             delete this.snapshot;
1005             if(suppressEvent !== true){
1006                 this.fireEvent("datachanged", this);
1007             }
1008         }
1009     },
1010
1011     // private
1012     afterEdit : function(record){
1013         if(this.modified.indexOf(record) == -1){
1014             this.modified.push(record);
1015         }
1016         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1017     },
1018     
1019     // private
1020     afterReject : function(record){
1021         this.modified.remove(record);
1022         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1023     },
1024
1025     // private
1026     afterCommit : function(record){
1027         this.modified.remove(record);
1028         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1029     },
1030
1031     /**
1032      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1033      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1034      */
1035     commitChanges : function(){
1036         var m = this.modified.slice(0);
1037         this.modified = [];
1038         for(var i = 0, len = m.length; i < len; i++){
1039             m[i].commit();
1040         }
1041     },
1042
1043     /**
1044      * Cancel outstanding changes on all changed records.
1045      */
1046     rejectChanges : function(){
1047         var m = this.modified.slice(0);
1048         this.modified = [];
1049         for(var i = 0, len = m.length; i < len; i++){
1050             m[i].reject();
1051         }
1052     },
1053
1054     onMetaChange : function(meta, rtype, o){
1055         this.recordType = rtype;
1056         this.fields = rtype.prototype.fields;
1057         delete this.snapshot;
1058         this.sortInfo = meta.sortInfo || this.sortInfo;
1059         this.modified = [];
1060         this.fireEvent('metachange', this, this.reader.meta);
1061     },
1062     
1063     moveIndex : function(data, type)
1064     {
1065         var index = this.indexOf(data);
1066         
1067         var newIndex = index + type;
1068         
1069         this.remove(data);
1070         
1071         this.insert(newIndex, data);
1072         
1073     }
1074 });/*
1075  * Based on:
1076  * Ext JS Library 1.1.1
1077  * Copyright(c) 2006-2007, Ext JS, LLC.
1078  *
1079  * Originally Released Under LGPL - original licence link has changed is not relivant.
1080  *
1081  * Fork - LGPL
1082  * <script type="text/javascript">
1083  */
1084
1085 /**
1086  * @class Roo.data.SimpleStore
1087  * @extends Roo.data.Store
1088  * Small helper class to make creating Stores from Array data easier.
1089  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1090  * @cfg {Array} fields An array of field definition objects, or field name strings.
1091  * @cfg {Array} data The multi-dimensional array of data
1092  * @constructor
1093  * @param {Object} config
1094  */
1095 Roo.data.SimpleStore = function(config){
1096     Roo.data.SimpleStore.superclass.constructor.call(this, {
1097         isLocal : true,
1098         reader: new Roo.data.ArrayReader({
1099                 id: config.id
1100             },
1101             Roo.data.Record.create(config.fields)
1102         ),
1103         proxy : new Roo.data.MemoryProxy(config.data)
1104     });
1105     this.load();
1106 };
1107 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1108  * Based on:
1109  * Ext JS Library 1.1.1
1110  * Copyright(c) 2006-2007, Ext JS, LLC.
1111  *
1112  * Originally Released Under LGPL - original licence link has changed is not relivant.
1113  *
1114  * Fork - LGPL
1115  * <script type="text/javascript">
1116  */
1117
1118 /**
1119 /**
1120  * @extends Roo.data.Store
1121  * @class Roo.data.JsonStore
1122  * Small helper class to make creating Stores for JSON data easier. <br/>
1123 <pre><code>
1124 var store = new Roo.data.JsonStore({
1125     url: 'get-images.php',
1126     root: 'images',
1127     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1128 });
1129 </code></pre>
1130  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1131  * JsonReader and HttpProxy (unless inline data is provided).</b>
1132  * @cfg {Array} fields An array of field definition objects, or field name strings.
1133  * @constructor
1134  * @param {Object} config
1135  */
1136 Roo.data.JsonStore = function(c){
1137     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1138         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1139         reader: new Roo.data.JsonReader(c, c.fields)
1140     }));
1141 };
1142 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1143  * Based on:
1144  * Ext JS Library 1.1.1
1145  * Copyright(c) 2006-2007, Ext JS, LLC.
1146  *
1147  * Originally Released Under LGPL - original licence link has changed is not relivant.
1148  *
1149  * Fork - LGPL
1150  * <script type="text/javascript">
1151  */
1152
1153  
1154 Roo.data.Field = function(config){
1155     if(typeof config == "string"){
1156         config = {name: config};
1157     }
1158     Roo.apply(this, config);
1159     
1160     if(!this.type){
1161         this.type = "auto";
1162     }
1163     
1164     var st = Roo.data.SortTypes;
1165     // named sortTypes are supported, here we look them up
1166     if(typeof this.sortType == "string"){
1167         this.sortType = st[this.sortType];
1168     }
1169     
1170     // set default sortType for strings and dates
1171     if(!this.sortType){
1172         switch(this.type){
1173             case "string":
1174                 this.sortType = st.asUCString;
1175                 break;
1176             case "date":
1177                 this.sortType = st.asDate;
1178                 break;
1179             default:
1180                 this.sortType = st.none;
1181         }
1182     }
1183
1184     // define once
1185     var stripRe = /[\$,%]/g;
1186
1187     // prebuilt conversion function for this field, instead of
1188     // switching every time we're reading a value
1189     if(!this.convert){
1190         var cv, dateFormat = this.dateFormat;
1191         switch(this.type){
1192             case "":
1193             case "auto":
1194             case undefined:
1195                 cv = function(v){ return v; };
1196                 break;
1197             case "string":
1198                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1199                 break;
1200             case "int":
1201                 cv = function(v){
1202                     return v !== undefined && v !== null && v !== '' ?
1203                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1204                     };
1205                 break;
1206             case "float":
1207                 cv = function(v){
1208                     return v !== undefined && v !== null && v !== '' ?
1209                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1210                     };
1211                 break;
1212             case "bool":
1213             case "boolean":
1214                 cv = function(v){ return v === true || v === "true" || v == 1; };
1215                 break;
1216             case "date":
1217                 cv = function(v){
1218                     if(!v){
1219                         return '';
1220                     }
1221                     if(v instanceof Date){
1222                         return v;
1223                     }
1224                     if(dateFormat){
1225                         if(dateFormat == "timestamp"){
1226                             return new Date(v*1000);
1227                         }
1228                         return Date.parseDate(v, dateFormat);
1229                     }
1230                     var parsed = Date.parse(v);
1231                     return parsed ? new Date(parsed) : null;
1232                 };
1233              break;
1234             
1235         }
1236         this.convert = cv;
1237     }
1238 };
1239
1240 Roo.data.Field.prototype = {
1241     dateFormat: null,
1242     defaultValue: "",
1243     mapping: null,
1244     sortType : null,
1245     sortDir : "ASC"
1246 };/*
1247  * Based on:
1248  * Ext JS Library 1.1.1
1249  * Copyright(c) 2006-2007, Ext JS, LLC.
1250  *
1251  * Originally Released Under LGPL - original licence link has changed is not relivant.
1252  *
1253  * Fork - LGPL
1254  * <script type="text/javascript">
1255  */
1256  
1257 // Base class for reading structured data from a data source.  This class is intended to be
1258 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1259
1260 /**
1261  * @class Roo.data.DataReader
1262  * Base class for reading structured data from a data source.  This class is intended to be
1263  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1264  */
1265
1266 Roo.data.DataReader = function(meta, recordType){
1267     
1268     this.meta = meta;
1269     
1270     this.recordType = recordType instanceof Array ? 
1271         Roo.data.Record.create(recordType) : recordType;
1272 };
1273
1274 Roo.data.DataReader.prototype = {
1275      /**
1276      * Create an empty record
1277      * @param {Object} data (optional) - overlay some values
1278      * @return {Roo.data.Record} record created.
1279      */
1280     newRow :  function(d) {
1281         var da =  {};
1282         this.recordType.prototype.fields.each(function(c) {
1283             switch( c.type) {
1284                 case 'int' : da[c.name] = 0; break;
1285                 case 'date' : da[c.name] = new Date(); break;
1286                 case 'float' : da[c.name] = 0.0; break;
1287                 case 'boolean' : da[c.name] = false; break;
1288                 default : da[c.name] = ""; break;
1289             }
1290             
1291         });
1292         return new this.recordType(Roo.apply(da, d));
1293     }
1294     
1295 };/*
1296  * Based on:
1297  * Ext JS Library 1.1.1
1298  * Copyright(c) 2006-2007, Ext JS, LLC.
1299  *
1300  * Originally Released Under LGPL - original licence link has changed is not relivant.
1301  *
1302  * Fork - LGPL
1303  * <script type="text/javascript">
1304  */
1305
1306 /**
1307  * @class Roo.data.DataProxy
1308  * @extends Roo.data.Observable
1309  * This class is an abstract base class for implementations which provide retrieval of
1310  * unformatted data objects.<br>
1311  * <p>
1312  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1313  * (of the appropriate type which knows how to parse the data object) to provide a block of
1314  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1315  * <p>
1316  * Custom implementations must implement the load method as described in
1317  * {@link Roo.data.HttpProxy#load}.
1318  */
1319 Roo.data.DataProxy = function(){
1320     this.addEvents({
1321         /**
1322          * @event beforeload
1323          * Fires before a network request is made to retrieve a data object.
1324          * @param {Object} This DataProxy object.
1325          * @param {Object} params The params parameter to the load function.
1326          */
1327         beforeload : true,
1328         /**
1329          * @event load
1330          * Fires before the load method's callback is called.
1331          * @param {Object} This DataProxy object.
1332          * @param {Object} o The data object.
1333          * @param {Object} arg The callback argument object passed to the load function.
1334          */
1335         load : true,
1336         /**
1337          * @event loadexception
1338          * Fires if an Exception occurs during data retrieval.
1339          * @param {Object} This DataProxy object.
1340          * @param {Object} o The data object.
1341          * @param {Object} arg The callback argument object passed to the load function.
1342          * @param {Object} e The Exception.
1343          */
1344         loadexception : true
1345     });
1346     Roo.data.DataProxy.superclass.constructor.call(this);
1347 };
1348
1349 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1350
1351     /**
1352      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1353      */
1354 /*
1355  * Based on:
1356  * Ext JS Library 1.1.1
1357  * Copyright(c) 2006-2007, Ext JS, LLC.
1358  *
1359  * Originally Released Under LGPL - original licence link has changed is not relivant.
1360  *
1361  * Fork - LGPL
1362  * <script type="text/javascript">
1363  */
1364 /**
1365  * @class Roo.data.MemoryProxy
1366  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1367  * to the Reader when its load method is called.
1368  * @constructor
1369  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1370  */
1371 Roo.data.MemoryProxy = function(data){
1372     if (data.data) {
1373         data = data.data;
1374     }
1375     Roo.data.MemoryProxy.superclass.constructor.call(this);
1376     this.data = data;
1377 };
1378
1379 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1380     
1381     /**
1382      * Load data from the requested source (in this case an in-memory
1383      * data object passed to the constructor), read the data object into
1384      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1385      * process that block using the passed callback.
1386      * @param {Object} params This parameter is not used by the MemoryProxy class.
1387      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1388      * object into a block of Roo.data.Records.
1389      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1390      * The function must be passed <ul>
1391      * <li>The Record block object</li>
1392      * <li>The "arg" argument from the load function</li>
1393      * <li>A boolean success indicator</li>
1394      * </ul>
1395      * @param {Object} scope The scope in which to call the callback
1396      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1397      */
1398     load : function(params, reader, callback, scope, arg){
1399         params = params || {};
1400         var result;
1401         try {
1402             result = reader.readRecords(params.data ? params.data :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  
2142  * @constructor
2143  * Create a new JsonReader
2144  * @param {Object} meta Metadata configuration options.
2145  * @param {Object|Array} recordType Either an Array of field definition objects
2146  * 
2147  * @cfg {Array} fields Array of field definition objects
2148  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2149  * as specified to {@link Roo.data.Record#create},
2150  * or an {@link Roo.data.Record} object
2151  *
2152  * 
2153  * created using {@link Roo.data.Record#create}.
2154  */
2155 Roo.data.ArrayReader = function(meta, recordType){
2156     
2157      
2158     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2159 };
2160
2161 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2162     /**
2163      * Create a data block containing Roo.data.Records from an XML document.
2164      * @param {Object} o An Array of row objects which represents the dataset.
2165      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2166      * a cache of Roo.data.Records.
2167      */
2168     readRecords : function(o)
2169     {
2170         var sid = this.meta ? this.meta.id : null;
2171         var recordType = this.recordType, fields = recordType.prototype.fields;
2172         var records = [];
2173         var root = o;
2174         for(var i = 0; i < root.length; i++){
2175                 var n = root[i];
2176             var values = {};
2177             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2178             for(var j = 0, jlen = fields.length; j < jlen; j++){
2179                 var f = fields.items[j];
2180                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2181                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2182                 v = f.convert(v);
2183                 values[f.name] = v;
2184             }
2185             var record = new recordType(values, id);
2186             record.json = n;
2187             records[records.length] = record;
2188         }
2189         return {
2190             records : records,
2191             totalRecords : records.length
2192         };
2193     }
2194 });/*
2195  * Based on:
2196  * Ext JS Library 1.1.1
2197  * Copyright(c) 2006-2007, Ext JS, LLC.
2198  *
2199  * Originally Released Under LGPL - original licence link has changed is not relivant.
2200  *
2201  * Fork - LGPL
2202  * <script type="text/javascript">
2203  */
2204
2205
2206 /**
2207  * @class Roo.data.Tree
2208  * @extends Roo.util.Observable
2209  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2210  * in the tree have most standard DOM functionality.
2211  * @constructor
2212  * @param {Node} root (optional) The root node
2213  */
2214 Roo.data.Tree = function(root){
2215    this.nodeHash = {};
2216    /**
2217     * The root node for this tree
2218     * @type Node
2219     */
2220    this.root = null;
2221    if(root){
2222        this.setRootNode(root);
2223    }
2224    this.addEvents({
2225        /**
2226         * @event append
2227         * Fires when a new child node is appended to a node in this tree.
2228         * @param {Tree} tree The owner tree
2229         * @param {Node} parent The parent node
2230         * @param {Node} node The newly appended node
2231         * @param {Number} index The index of the newly appended node
2232         */
2233        "append" : true,
2234        /**
2235         * @event remove
2236         * Fires when a child node is removed from a node in this tree.
2237         * @param {Tree} tree The owner tree
2238         * @param {Node} parent The parent node
2239         * @param {Node} node The child node removed
2240         */
2241        "remove" : true,
2242        /**
2243         * @event move
2244         * Fires when a node is moved to a new location in the tree
2245         * @param {Tree} tree The owner tree
2246         * @param {Node} node The node moved
2247         * @param {Node} oldParent The old parent of this node
2248         * @param {Node} newParent The new parent of this node
2249         * @param {Number} index The index it was moved to
2250         */
2251        "move" : true,
2252        /**
2253         * @event insert
2254         * Fires when a new child node is inserted in a node in this tree.
2255         * @param {Tree} tree The owner tree
2256         * @param {Node} parent The parent node
2257         * @param {Node} node The child node inserted
2258         * @param {Node} refNode The child node the node was inserted before
2259         */
2260        "insert" : true,
2261        /**
2262         * @event beforeappend
2263         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2264         * @param {Tree} tree The owner tree
2265         * @param {Node} parent The parent node
2266         * @param {Node} node The child node to be appended
2267         */
2268        "beforeappend" : true,
2269        /**
2270         * @event beforeremove
2271         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2272         * @param {Tree} tree The owner tree
2273         * @param {Node} parent The parent node
2274         * @param {Node} node The child node to be removed
2275         */
2276        "beforeremove" : true,
2277        /**
2278         * @event beforemove
2279         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2280         * @param {Tree} tree The owner tree
2281         * @param {Node} node The node being moved
2282         * @param {Node} oldParent The parent of the node
2283         * @param {Node} newParent The new parent the node is moving to
2284         * @param {Number} index The index it is being moved to
2285         */
2286        "beforemove" : true,
2287        /**
2288         * @event beforeinsert
2289         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2290         * @param {Tree} tree The owner tree
2291         * @param {Node} parent The parent node
2292         * @param {Node} node The child node to be inserted
2293         * @param {Node} refNode The child node the node is being inserted before
2294         */
2295        "beforeinsert" : true
2296    });
2297
2298     Roo.data.Tree.superclass.constructor.call(this);
2299 };
2300
2301 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2302     pathSeparator: "/",
2303
2304     proxyNodeEvent : function(){
2305         return this.fireEvent.apply(this, arguments);
2306     },
2307
2308     /**
2309      * Returns the root node for this tree.
2310      * @return {Node}
2311      */
2312     getRootNode : function(){
2313         return this.root;
2314     },
2315
2316     /**
2317      * Sets the root node for this tree.
2318      * @param {Node} node
2319      * @return {Node}
2320      */
2321     setRootNode : function(node){
2322         this.root = node;
2323         node.ownerTree = this;
2324         node.isRoot = true;
2325         this.registerNode(node);
2326         return node;
2327     },
2328
2329     /**
2330      * Gets a node in this tree by its id.
2331      * @param {String} id
2332      * @return {Node}
2333      */
2334     getNodeById : function(id){
2335         return this.nodeHash[id];
2336     },
2337
2338     registerNode : function(node){
2339         this.nodeHash[node.id] = node;
2340     },
2341
2342     unregisterNode : function(node){
2343         delete this.nodeHash[node.id];
2344     },
2345
2346     toString : function(){
2347         return "[Tree"+(this.id?" "+this.id:"")+"]";
2348     }
2349 });
2350
2351 /**
2352  * @class Roo.data.Node
2353  * @extends Roo.util.Observable
2354  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2355  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2356  * @constructor
2357  * @param {Object} attributes The attributes/config for the node
2358  */
2359 Roo.data.Node = function(attributes){
2360     /**
2361      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2362      * @type {Object}
2363      */
2364     this.attributes = attributes || {};
2365     this.leaf = this.attributes.leaf;
2366     /**
2367      * The node id. @type String
2368      */
2369     this.id = this.attributes.id;
2370     if(!this.id){
2371         this.id = Roo.id(null, "ynode-");
2372         this.attributes.id = this.id;
2373     }
2374      
2375     
2376     /**
2377      * All child nodes of this node. @type Array
2378      */
2379     this.childNodes = [];
2380     if(!this.childNodes.indexOf){ // indexOf is a must
2381         this.childNodes.indexOf = function(o){
2382             for(var i = 0, len = this.length; i < len; i++){
2383                 if(this[i] == o) {
2384                     return i;
2385                 }
2386             }
2387             return -1;
2388         };
2389     }
2390     /**
2391      * The parent node for this node. @type Node
2392      */
2393     this.parentNode = null;
2394     /**
2395      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2396      */
2397     this.firstChild = null;
2398     /**
2399      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2400      */
2401     this.lastChild = null;
2402     /**
2403      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2404      */
2405     this.previousSibling = null;
2406     /**
2407      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2408      */
2409     this.nextSibling = null;
2410
2411     this.addEvents({
2412        /**
2413         * @event append
2414         * Fires when a new child node is appended
2415         * @param {Tree} tree The owner tree
2416         * @param {Node} this This node
2417         * @param {Node} node The newly appended node
2418         * @param {Number} index The index of the newly appended node
2419         */
2420        "append" : true,
2421        /**
2422         * @event remove
2423         * Fires when a child node is removed
2424         * @param {Tree} tree The owner tree
2425         * @param {Node} this This node
2426         * @param {Node} node The removed node
2427         */
2428        "remove" : true,
2429        /**
2430         * @event move
2431         * Fires when this node is moved to a new location in the tree
2432         * @param {Tree} tree The owner tree
2433         * @param {Node} this This node
2434         * @param {Node} oldParent The old parent of this node
2435         * @param {Node} newParent The new parent of this node
2436         * @param {Number} index The index it was moved to
2437         */
2438        "move" : true,
2439        /**
2440         * @event insert
2441         * Fires when a new child node is inserted.
2442         * @param {Tree} tree The owner tree
2443         * @param {Node} this This node
2444         * @param {Node} node The child node inserted
2445         * @param {Node} refNode The child node the node was inserted before
2446         */
2447        "insert" : true,
2448        /**
2449         * @event beforeappend
2450         * Fires before a new child is appended, return false to cancel the append.
2451         * @param {Tree} tree The owner tree
2452         * @param {Node} this This node
2453         * @param {Node} node The child node to be appended
2454         */
2455        "beforeappend" : true,
2456        /**
2457         * @event beforeremove
2458         * Fires before a child is removed, return false to cancel the remove.
2459         * @param {Tree} tree The owner tree
2460         * @param {Node} this This node
2461         * @param {Node} node The child node to be removed
2462         */
2463        "beforeremove" : true,
2464        /**
2465         * @event beforemove
2466         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2467         * @param {Tree} tree The owner tree
2468         * @param {Node} this This node
2469         * @param {Node} oldParent The parent of this node
2470         * @param {Node} newParent The new parent this node is moving to
2471         * @param {Number} index The index it is being moved to
2472         */
2473        "beforemove" : true,
2474        /**
2475         * @event beforeinsert
2476         * Fires before a new child is inserted, return false to cancel the insert.
2477         * @param {Tree} tree The owner tree
2478         * @param {Node} this This node
2479         * @param {Node} node The child node to be inserted
2480         * @param {Node} refNode The child node the node is being inserted before
2481         */
2482        "beforeinsert" : true
2483    });
2484     this.listeners = this.attributes.listeners;
2485     Roo.data.Node.superclass.constructor.call(this);
2486 };
2487
2488 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2489     fireEvent : function(evtName){
2490         // first do standard event for this node
2491         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2492             return false;
2493         }
2494         // then bubble it up to the tree if the event wasn't cancelled
2495         var ot = this.getOwnerTree();
2496         if(ot){
2497             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2498                 return false;
2499             }
2500         }
2501         return true;
2502     },
2503
2504     /**
2505      * Returns true if this node is a leaf
2506      * @return {Boolean}
2507      */
2508     isLeaf : function(){
2509         return this.leaf === true;
2510     },
2511
2512     // private
2513     setFirstChild : function(node){
2514         this.firstChild = node;
2515     },
2516
2517     //private
2518     setLastChild : function(node){
2519         this.lastChild = node;
2520     },
2521
2522
2523     /**
2524      * Returns true if this node is the last child of its parent
2525      * @return {Boolean}
2526      */
2527     isLast : function(){
2528        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2529     },
2530
2531     /**
2532      * Returns true if this node is the first child of its parent
2533      * @return {Boolean}
2534      */
2535     isFirst : function(){
2536        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2537     },
2538
2539     hasChildNodes : function(){
2540         return !this.isLeaf() && this.childNodes.length > 0;
2541     },
2542
2543     /**
2544      * Insert node(s) as the last child node of this node.
2545      * @param {Node/Array} node The node or Array of nodes to append
2546      * @return {Node} The appended node if single append, or null if an array was passed
2547      */
2548     appendChild : function(node){
2549         var multi = false;
2550         if(node instanceof Array){
2551             multi = node;
2552         }else if(arguments.length > 1){
2553             multi = arguments;
2554         }
2555         
2556         // if passed an array or multiple args do them one by one
2557         if(multi){
2558             for(var i = 0, len = multi.length; i < len; i++) {
2559                 this.appendChild(multi[i]);
2560             }
2561         }else{
2562             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2563                 return false;
2564             }
2565             var index = this.childNodes.length;
2566             var oldParent = node.parentNode;
2567             // it's a move, make sure we move it cleanly
2568             if(oldParent){
2569                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2570                     return false;
2571                 }
2572                 oldParent.removeChild(node);
2573             }
2574             
2575             index = this.childNodes.length;
2576             if(index == 0){
2577                 this.setFirstChild(node);
2578             }
2579             this.childNodes.push(node);
2580             node.parentNode = this;
2581             var ps = this.childNodes[index-1];
2582             if(ps){
2583                 node.previousSibling = ps;
2584                 ps.nextSibling = node;
2585             }else{
2586                 node.previousSibling = null;
2587             }
2588             node.nextSibling = null;
2589             this.setLastChild(node);
2590             node.setOwnerTree(this.getOwnerTree());
2591             this.fireEvent("append", this.ownerTree, this, node, index);
2592             if(this.ownerTree) {
2593                 this.ownerTree.fireEvent("appendnode", this, node, index);
2594             }
2595             if(oldParent){
2596                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2597             }
2598             return node;
2599         }
2600     },
2601
2602     /**
2603      * Removes a child node from this node.
2604      * @param {Node} node The node to remove
2605      * @return {Node} The removed node
2606      */
2607     removeChild : function(node){
2608         var index = this.childNodes.indexOf(node);
2609         if(index == -1){
2610             return false;
2611         }
2612         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2613             return false;
2614         }
2615
2616         // remove it from childNodes collection
2617         this.childNodes.splice(index, 1);
2618
2619         // update siblings
2620         if(node.previousSibling){
2621             node.previousSibling.nextSibling = node.nextSibling;
2622         }
2623         if(node.nextSibling){
2624             node.nextSibling.previousSibling = node.previousSibling;
2625         }
2626
2627         // update child refs
2628         if(this.firstChild == node){
2629             this.setFirstChild(node.nextSibling);
2630         }
2631         if(this.lastChild == node){
2632             this.setLastChild(node.previousSibling);
2633         }
2634
2635         node.setOwnerTree(null);
2636         // clear any references from the node
2637         node.parentNode = null;
2638         node.previousSibling = null;
2639         node.nextSibling = null;
2640         this.fireEvent("remove", this.ownerTree, this, node);
2641         return node;
2642     },
2643
2644     /**
2645      * Inserts the first node before the second node in this nodes childNodes collection.
2646      * @param {Node} node The node to insert
2647      * @param {Node} refNode The node to insert before (if null the node is appended)
2648      * @return {Node} The inserted node
2649      */
2650     insertBefore : function(node, refNode){
2651         if(!refNode){ // like standard Dom, refNode can be null for append
2652             return this.appendChild(node);
2653         }
2654         // nothing to do
2655         if(node == refNode){
2656             return false;
2657         }
2658
2659         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2660             return false;
2661         }
2662         var index = this.childNodes.indexOf(refNode);
2663         var oldParent = node.parentNode;
2664         var refIndex = index;
2665
2666         // when moving internally, indexes will change after remove
2667         if(oldParent == this && this.childNodes.indexOf(node) < index){
2668             refIndex--;
2669         }
2670
2671         // it's a move, make sure we move it cleanly
2672         if(oldParent){
2673             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2674                 return false;
2675             }
2676             oldParent.removeChild(node);
2677         }
2678         if(refIndex == 0){
2679             this.setFirstChild(node);
2680         }
2681         this.childNodes.splice(refIndex, 0, node);
2682         node.parentNode = this;
2683         var ps = this.childNodes[refIndex-1];
2684         if(ps){
2685             node.previousSibling = ps;
2686             ps.nextSibling = node;
2687         }else{
2688             node.previousSibling = null;
2689         }
2690         node.nextSibling = refNode;
2691         refNode.previousSibling = node;
2692         node.setOwnerTree(this.getOwnerTree());
2693         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2694         if(oldParent){
2695             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2696         }
2697         return node;
2698     },
2699
2700     /**
2701      * Returns the child node at the specified index.
2702      * @param {Number} index
2703      * @return {Node}
2704      */
2705     item : function(index){
2706         return this.childNodes[index];
2707     },
2708
2709     /**
2710      * Replaces one child node in this node with another.
2711      * @param {Node} newChild The replacement node
2712      * @param {Node} oldChild The node to replace
2713      * @return {Node} The replaced node
2714      */
2715     replaceChild : function(newChild, oldChild){
2716         this.insertBefore(newChild, oldChild);
2717         this.removeChild(oldChild);
2718         return oldChild;
2719     },
2720
2721     /**
2722      * Returns the index of a child node
2723      * @param {Node} node
2724      * @return {Number} The index of the node or -1 if it was not found
2725      */
2726     indexOf : function(child){
2727         return this.childNodes.indexOf(child);
2728     },
2729
2730     /**
2731      * Returns the tree this node is in.
2732      * @return {Tree}
2733      */
2734     getOwnerTree : function(){
2735         // if it doesn't have one, look for one
2736         if(!this.ownerTree){
2737             var p = this;
2738             while(p){
2739                 if(p.ownerTree){
2740                     this.ownerTree = p.ownerTree;
2741                     break;
2742                 }
2743                 p = p.parentNode;
2744             }
2745         }
2746         return this.ownerTree;
2747     },
2748
2749     /**
2750      * Returns depth of this node (the root node has a depth of 0)
2751      * @return {Number}
2752      */
2753     getDepth : function(){
2754         var depth = 0;
2755         var p = this;
2756         while(p.parentNode){
2757             ++depth;
2758             p = p.parentNode;
2759         }
2760         return depth;
2761     },
2762
2763     // private
2764     setOwnerTree : function(tree){
2765         // if it's move, we need to update everyone
2766         if(tree != this.ownerTree){
2767             if(this.ownerTree){
2768                 this.ownerTree.unregisterNode(this);
2769             }
2770             this.ownerTree = tree;
2771             var cs = this.childNodes;
2772             for(var i = 0, len = cs.length; i < len; i++) {
2773                 cs[i].setOwnerTree(tree);
2774             }
2775             if(tree){
2776                 tree.registerNode(this);
2777             }
2778         }
2779     },
2780
2781     /**
2782      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2783      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2784      * @return {String} The path
2785      */
2786     getPath : function(attr){
2787         attr = attr || "id";
2788         var p = this.parentNode;
2789         var b = [this.attributes[attr]];
2790         while(p){
2791             b.unshift(p.attributes[attr]);
2792             p = p.parentNode;
2793         }
2794         var sep = this.getOwnerTree().pathSeparator;
2795         return sep + b.join(sep);
2796     },
2797
2798     /**
2799      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2800      * function call will be the scope provided or the current node. The arguments to the function
2801      * will be the args provided or the current node. If the function returns false at any point,
2802      * the bubble is stopped.
2803      * @param {Function} fn The function to call
2804      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2805      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2806      */
2807     bubble : function(fn, scope, args){
2808         var p = this;
2809         while(p){
2810             if(fn.call(scope || p, args || p) === false){
2811                 break;
2812             }
2813             p = p.parentNode;
2814         }
2815     },
2816
2817     /**
2818      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2819      * function call will be the scope provided or the current node. The arguments to the function
2820      * will be the args provided or the current node. If the function returns false at any point,
2821      * the cascade is stopped on that branch.
2822      * @param {Function} fn The function to call
2823      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2824      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2825      */
2826     cascade : function(fn, scope, args){
2827         if(fn.call(scope || this, args || this) !== false){
2828             var cs = this.childNodes;
2829             for(var i = 0, len = cs.length; i < len; i++) {
2830                 cs[i].cascade(fn, scope, args);
2831             }
2832         }
2833     },
2834
2835     /**
2836      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2837      * function call will be the scope provided or the current node. The arguments to the function
2838      * will be the args provided or the current node. If the function returns false at any point,
2839      * the iteration stops.
2840      * @param {Function} fn The function to call
2841      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2842      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2843      */
2844     eachChild : function(fn, scope, args){
2845         var cs = this.childNodes;
2846         for(var i = 0, len = cs.length; i < len; i++) {
2847                 if(fn.call(scope || this, args || cs[i]) === false){
2848                     break;
2849                 }
2850         }
2851     },
2852
2853     /**
2854      * Finds the first child that has the attribute with the specified value.
2855      * @param {String} attribute The attribute name
2856      * @param {Mixed} value The value to search for
2857      * @return {Node} The found child or null if none was found
2858      */
2859     findChild : function(attribute, value){
2860         var cs = this.childNodes;
2861         for(var i = 0, len = cs.length; i < len; i++) {
2862                 if(cs[i].attributes[attribute] == value){
2863                     return cs[i];
2864                 }
2865         }
2866         return null;
2867     },
2868
2869     /**
2870      * Finds the first child by a custom function. The child matches if the function passed
2871      * returns true.
2872      * @param {Function} fn
2873      * @param {Object} scope (optional)
2874      * @return {Node} The found child or null if none was found
2875      */
2876     findChildBy : function(fn, scope){
2877         var cs = this.childNodes;
2878         for(var i = 0, len = cs.length; i < len; i++) {
2879                 if(fn.call(scope||cs[i], cs[i]) === true){
2880                     return cs[i];
2881                 }
2882         }
2883         return null;
2884     },
2885
2886     /**
2887      * Sorts this nodes children using the supplied sort function
2888      * @param {Function} fn
2889      * @param {Object} scope (optional)
2890      */
2891     sort : function(fn, scope){
2892         var cs = this.childNodes;
2893         var len = cs.length;
2894         if(len > 0){
2895             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2896             cs.sort(sortFn);
2897             for(var i = 0; i < len; i++){
2898                 var n = cs[i];
2899                 n.previousSibling = cs[i-1];
2900                 n.nextSibling = cs[i+1];
2901                 if(i == 0){
2902                     this.setFirstChild(n);
2903                 }
2904                 if(i == len-1){
2905                     this.setLastChild(n);
2906                 }
2907             }
2908         }
2909     },
2910
2911     /**
2912      * Returns true if this node is an ancestor (at any point) of the passed node.
2913      * @param {Node} node
2914      * @return {Boolean}
2915      */
2916     contains : function(node){
2917         return node.isAncestor(this);
2918     },
2919
2920     /**
2921      * Returns true if the passed node is an ancestor (at any point) of this node.
2922      * @param {Node} node
2923      * @return {Boolean}
2924      */
2925     isAncestor : function(node){
2926         var p = this.parentNode;
2927         while(p){
2928             if(p == node){
2929                 return true;
2930             }
2931             p = p.parentNode;
2932         }
2933         return false;
2934     },
2935
2936     toString : function(){
2937         return "[Node"+(this.id?" "+this.id:"")+"]";
2938     }
2939 });/*
2940  * Based on:
2941  * Ext JS Library 1.1.1
2942  * Copyright(c) 2006-2007, Ext JS, LLC.
2943  *
2944  * Originally Released Under LGPL - original licence link has changed is not relivant.
2945  *
2946  * Fork - LGPL
2947  * <script type="text/javascript">
2948  */
2949  (function(){ 
2950 /**
2951  * @class Roo.Layer
2952  * @extends Roo.Element
2953  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2954  * automatic maintaining of shadow/shim positions.
2955  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2956  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2957  * you can pass a string with a CSS class name. False turns off the shadow.
2958  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2959  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2960  * @cfg {String} cls CSS class to add to the element
2961  * @cfg {Number} zindex Starting z-index (defaults to 11000)
2962  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2963  * @constructor
2964  * @param {Object} config An object with config options.
2965  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2966  */
2967
2968 Roo.Layer = function(config, existingEl){
2969     config = config || {};
2970     var dh = Roo.DomHelper;
2971     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2972     if(existingEl){
2973         this.dom = Roo.getDom(existingEl);
2974     }
2975     if(!this.dom){
2976         var o = config.dh || {tag: "div", cls: "x-layer"};
2977         this.dom = dh.append(pel, o);
2978     }
2979     if(config.cls){
2980         this.addClass(config.cls);
2981     }
2982     this.constrain = config.constrain !== false;
2983     this.visibilityMode = Roo.Element.VISIBILITY;
2984     if(config.id){
2985         this.id = this.dom.id = config.id;
2986     }else{
2987         this.id = Roo.id(this.dom);
2988     }
2989     this.zindex = config.zindex || this.getZIndex();
2990     this.position("absolute", this.zindex);
2991     if(config.shadow){
2992         this.shadowOffset = config.shadowOffset || 4;
2993         this.shadow = new Roo.Shadow({
2994             offset : this.shadowOffset,
2995             mode : config.shadow
2996         });
2997     }else{
2998         this.shadowOffset = 0;
2999     }
3000     this.useShim = config.shim !== false && Roo.useShims;
3001     this.useDisplay = config.useDisplay;
3002     this.hide();
3003 };
3004
3005 var supr = Roo.Element.prototype;
3006
3007 // shims are shared among layer to keep from having 100 iframes
3008 var shims = [];
3009
3010 Roo.extend(Roo.Layer, Roo.Element, {
3011
3012     getZIndex : function(){
3013         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3014     },
3015
3016     getShim : function(){
3017         if(!this.useShim){
3018             return null;
3019         }
3020         if(this.shim){
3021             return this.shim;
3022         }
3023         var shim = shims.shift();
3024         if(!shim){
3025             shim = this.createShim();
3026             shim.enableDisplayMode('block');
3027             shim.dom.style.display = 'none';
3028             shim.dom.style.visibility = 'visible';
3029         }
3030         var pn = this.dom.parentNode;
3031         if(shim.dom.parentNode != pn){
3032             pn.insertBefore(shim.dom, this.dom);
3033         }
3034         shim.setStyle('z-index', this.getZIndex()-2);
3035         this.shim = shim;
3036         return shim;
3037     },
3038
3039     hideShim : function(){
3040         if(this.shim){
3041             this.shim.setDisplayed(false);
3042             shims.push(this.shim);
3043             delete this.shim;
3044         }
3045     },
3046
3047     disableShadow : function(){
3048         if(this.shadow){
3049             this.shadowDisabled = true;
3050             this.shadow.hide();
3051             this.lastShadowOffset = this.shadowOffset;
3052             this.shadowOffset = 0;
3053         }
3054     },
3055
3056     enableShadow : function(show){
3057         if(this.shadow){
3058             this.shadowDisabled = false;
3059             this.shadowOffset = this.lastShadowOffset;
3060             delete this.lastShadowOffset;
3061             if(show){
3062                 this.sync(true);
3063             }
3064         }
3065     },
3066
3067     // private
3068     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3069     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3070     sync : function(doShow){
3071         var sw = this.shadow;
3072         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3073             var sh = this.getShim();
3074
3075             var w = this.getWidth(),
3076                 h = this.getHeight();
3077
3078             var l = this.getLeft(true),
3079                 t = this.getTop(true);
3080
3081             if(sw && !this.shadowDisabled){
3082                 if(doShow && !sw.isVisible()){
3083                     sw.show(this);
3084                 }else{
3085                     sw.realign(l, t, w, h);
3086                 }
3087                 if(sh){
3088                     if(doShow){
3089                        sh.show();
3090                     }
3091                     // fit the shim behind the shadow, so it is shimmed too
3092                     var a = sw.adjusts, s = sh.dom.style;
3093                     s.left = (Math.min(l, l+a.l))+"px";
3094                     s.top = (Math.min(t, t+a.t))+"px";
3095                     s.width = (w+a.w)+"px";
3096                     s.height = (h+a.h)+"px";
3097                 }
3098             }else if(sh){
3099                 if(doShow){
3100                    sh.show();
3101                 }
3102                 sh.setSize(w, h);
3103                 sh.setLeftTop(l, t);
3104             }
3105             
3106         }
3107     },
3108
3109     // private
3110     destroy : function(){
3111         this.hideShim();
3112         if(this.shadow){
3113             this.shadow.hide();
3114         }
3115         this.removeAllListeners();
3116         var pn = this.dom.parentNode;
3117         if(pn){
3118             pn.removeChild(this.dom);
3119         }
3120         Roo.Element.uncache(this.id);
3121     },
3122
3123     remove : function(){
3124         this.destroy();
3125     },
3126
3127     // private
3128     beginUpdate : function(){
3129         this.updating = true;
3130     },
3131
3132     // private
3133     endUpdate : function(){
3134         this.updating = false;
3135         this.sync(true);
3136     },
3137
3138     // private
3139     hideUnders : function(negOffset){
3140         if(this.shadow){
3141             this.shadow.hide();
3142         }
3143         this.hideShim();
3144     },
3145
3146     // private
3147     constrainXY : function(){
3148         if(this.constrain){
3149             var vw = Roo.lib.Dom.getViewWidth(),
3150                 vh = Roo.lib.Dom.getViewHeight();
3151             var s = Roo.get(document).getScroll();
3152
3153             var xy = this.getXY();
3154             var x = xy[0], y = xy[1];   
3155             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3156             // only move it if it needs it
3157             var moved = false;
3158             // first validate right/bottom
3159             if((x + w) > vw+s.left){
3160                 x = vw - w - this.shadowOffset;
3161                 moved = true;
3162             }
3163             if((y + h) > vh+s.top){
3164                 y = vh - h - this.shadowOffset;
3165                 moved = true;
3166             }
3167             // then make sure top/left isn't negative
3168             if(x < s.left){
3169                 x = s.left;
3170                 moved = true;
3171             }
3172             if(y < s.top){
3173                 y = s.top;
3174                 moved = true;
3175             }
3176             if(moved){
3177                 if(this.avoidY){
3178                     var ay = this.avoidY;
3179                     if(y <= ay && (y+h) >= ay){
3180                         y = ay-h-5;   
3181                     }
3182                 }
3183                 xy = [x, y];
3184                 this.storeXY(xy);
3185                 supr.setXY.call(this, xy);
3186                 this.sync();
3187             }
3188         }
3189     },
3190
3191     isVisible : function(){
3192         return this.visible;    
3193     },
3194
3195     // private
3196     showAction : function(){
3197         this.visible = true; // track visibility to prevent getStyle calls
3198         if(this.useDisplay === true){
3199             this.setDisplayed("");
3200         }else if(this.lastXY){
3201             supr.setXY.call(this, this.lastXY);
3202         }else if(this.lastLT){
3203             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3204         }
3205     },
3206
3207     // private
3208     hideAction : function(){
3209         this.visible = false;
3210         if(this.useDisplay === true){
3211             this.setDisplayed(false);
3212         }else{
3213             this.setLeftTop(-10000,-10000);
3214         }
3215     },
3216
3217     // overridden Element method
3218     setVisible : function(v, a, d, c, e){
3219         if(v){
3220             this.showAction();
3221         }
3222         if(a && v){
3223             var cb = function(){
3224                 this.sync(true);
3225                 if(c){
3226                     c();
3227                 }
3228             }.createDelegate(this);
3229             supr.setVisible.call(this, true, true, d, cb, e);
3230         }else{
3231             if(!v){
3232                 this.hideUnders(true);
3233             }
3234             var cb = c;
3235             if(a){
3236                 cb = function(){
3237                     this.hideAction();
3238                     if(c){
3239                         c();
3240                     }
3241                 }.createDelegate(this);
3242             }
3243             supr.setVisible.call(this, v, a, d, cb, e);
3244             if(v){
3245                 this.sync(true);
3246             }else if(!a){
3247                 this.hideAction();
3248             }
3249         }
3250     },
3251
3252     storeXY : function(xy){
3253         delete this.lastLT;
3254         this.lastXY = xy;
3255     },
3256
3257     storeLeftTop : function(left, top){
3258         delete this.lastXY;
3259         this.lastLT = [left, top];
3260     },
3261
3262     // private
3263     beforeFx : function(){
3264         this.beforeAction();
3265         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3266     },
3267
3268     // private
3269     afterFx : function(){
3270         Roo.Layer.superclass.afterFx.apply(this, arguments);
3271         this.sync(this.isVisible());
3272     },
3273
3274     // private
3275     beforeAction : function(){
3276         if(!this.updating && this.shadow){
3277             this.shadow.hide();
3278         }
3279     },
3280
3281     // overridden Element method
3282     setLeft : function(left){
3283         this.storeLeftTop(left, this.getTop(true));
3284         supr.setLeft.apply(this, arguments);
3285         this.sync();
3286     },
3287
3288     setTop : function(top){
3289         this.storeLeftTop(this.getLeft(true), top);
3290         supr.setTop.apply(this, arguments);
3291         this.sync();
3292     },
3293
3294     setLeftTop : function(left, top){
3295         this.storeLeftTop(left, top);
3296         supr.setLeftTop.apply(this, arguments);
3297         this.sync();
3298     },
3299
3300     setXY : function(xy, a, d, c, e){
3301         this.fixDisplay();
3302         this.beforeAction();
3303         this.storeXY(xy);
3304         var cb = this.createCB(c);
3305         supr.setXY.call(this, xy, a, d, cb, e);
3306         if(!a){
3307             cb();
3308         }
3309     },
3310
3311     // private
3312     createCB : function(c){
3313         var el = this;
3314         return function(){
3315             el.constrainXY();
3316             el.sync(true);
3317             if(c){
3318                 c();
3319             }
3320         };
3321     },
3322
3323     // overridden Element method
3324     setX : function(x, a, d, c, e){
3325         this.setXY([x, this.getY()], a, d, c, e);
3326     },
3327
3328     // overridden Element method
3329     setY : function(y, a, d, c, e){
3330         this.setXY([this.getX(), y], a, d, c, e);
3331     },
3332
3333     // overridden Element method
3334     setSize : function(w, h, a, d, c, e){
3335         this.beforeAction();
3336         var cb = this.createCB(c);
3337         supr.setSize.call(this, w, h, a, d, cb, e);
3338         if(!a){
3339             cb();
3340         }
3341     },
3342
3343     // overridden Element method
3344     setWidth : function(w, a, d, c, e){
3345         this.beforeAction();
3346         var cb = this.createCB(c);
3347         supr.setWidth.call(this, w, a, d, cb, e);
3348         if(!a){
3349             cb();
3350         }
3351     },
3352
3353     // overridden Element method
3354     setHeight : function(h, a, d, c, e){
3355         this.beforeAction();
3356         var cb = this.createCB(c);
3357         supr.setHeight.call(this, h, a, d, cb, e);
3358         if(!a){
3359             cb();
3360         }
3361     },
3362
3363     // overridden Element method
3364     setBounds : function(x, y, w, h, a, d, c, e){
3365         this.beforeAction();
3366         var cb = this.createCB(c);
3367         if(!a){
3368             this.storeXY([x, y]);
3369             supr.setXY.call(this, [x, y]);
3370             supr.setSize.call(this, w, h, a, d, cb, e);
3371             cb();
3372         }else{
3373             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3374         }
3375         return this;
3376     },
3377     
3378     /**
3379      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3380      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3381      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3382      * @param {Number} zindex The new z-index to set
3383      * @return {this} The Layer
3384      */
3385     setZIndex : function(zindex){
3386         this.zindex = zindex;
3387         this.setStyle("z-index", zindex + 2);
3388         if(this.shadow){
3389             this.shadow.setZIndex(zindex + 1);
3390         }
3391         if(this.shim){
3392             this.shim.setStyle("z-index", zindex);
3393         }
3394     }
3395 });
3396 })();/*
3397  * Based on:
3398  * Ext JS Library 1.1.1
3399  * Copyright(c) 2006-2007, Ext JS, LLC.
3400  *
3401  * Originally Released Under LGPL - original licence link has changed is not relivant.
3402  *
3403  * Fork - LGPL
3404  * <script type="text/javascript">
3405  */
3406
3407
3408 /**
3409  * @class Roo.Shadow
3410  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3411  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3412  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3413  * @constructor
3414  * Create a new Shadow
3415  * @param {Object} config The config object
3416  */
3417 Roo.Shadow = function(config){
3418     Roo.apply(this, config);
3419     if(typeof this.mode != "string"){
3420         this.mode = this.defaultMode;
3421     }
3422     var o = this.offset, a = {h: 0};
3423     var rad = Math.floor(this.offset/2);
3424     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3425         case "drop":
3426             a.w = 0;
3427             a.l = a.t = o;
3428             a.t -= 1;
3429             if(Roo.isIE){
3430                 a.l -= this.offset + rad;
3431                 a.t -= this.offset + rad;
3432                 a.w -= rad;
3433                 a.h -= rad;
3434                 a.t += 1;
3435             }
3436         break;
3437         case "sides":
3438             a.w = (o*2);
3439             a.l = -o;
3440             a.t = o-1;
3441             if(Roo.isIE){
3442                 a.l -= (this.offset - rad);
3443                 a.t -= this.offset + rad;
3444                 a.l += 1;
3445                 a.w -= (this.offset - rad)*2;
3446                 a.w -= rad + 1;
3447                 a.h -= 1;
3448             }
3449         break;
3450         case "frame":
3451             a.w = a.h = (o*2);
3452             a.l = a.t = -o;
3453             a.t += 1;
3454             a.h -= 2;
3455             if(Roo.isIE){
3456                 a.l -= (this.offset - rad);
3457                 a.t -= (this.offset - rad);
3458                 a.l += 1;
3459                 a.w -= (this.offset + rad + 1);
3460                 a.h -= (this.offset + rad);
3461                 a.h += 1;
3462             }
3463         break;
3464     };
3465
3466     this.adjusts = a;
3467 };
3468
3469 Roo.Shadow.prototype = {
3470     /**
3471      * @cfg {String} mode
3472      * The shadow display mode.  Supports the following options:<br />
3473      * sides: Shadow displays on both sides and bottom only<br />
3474      * frame: Shadow displays equally on all four sides<br />
3475      * drop: Traditional bottom-right drop shadow (default)
3476      */
3477     /**
3478      * @cfg {String} offset
3479      * The number of pixels to offset the shadow from the element (defaults to 4)
3480      */
3481     offset: 4,
3482
3483     // private
3484     defaultMode: "drop",
3485
3486     /**
3487      * Displays the shadow under the target element
3488      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3489      */
3490     show : function(target){
3491         target = Roo.get(target);
3492         if(!this.el){
3493             this.el = Roo.Shadow.Pool.pull();
3494             if(this.el.dom.nextSibling != target.dom){
3495                 this.el.insertBefore(target);
3496             }
3497         }
3498         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3499         if(Roo.isIE){
3500             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3501         }
3502         this.realign(
3503             target.getLeft(true),
3504             target.getTop(true),
3505             target.getWidth(),
3506             target.getHeight()
3507         );
3508         this.el.dom.style.display = "block";
3509     },
3510
3511     /**
3512      * Returns true if the shadow is visible, else false
3513      */
3514     isVisible : function(){
3515         return this.el ? true : false;  
3516     },
3517
3518     /**
3519      * Direct alignment when values are already available. Show must be called at least once before
3520      * calling this method to ensure it is initialized.
3521      * @param {Number} left The target element left position
3522      * @param {Number} top The target element top position
3523      * @param {Number} width The target element width
3524      * @param {Number} height The target element height
3525      */
3526     realign : function(l, t, w, h){
3527         if(!this.el){
3528             return;
3529         }
3530         var a = this.adjusts, d = this.el.dom, s = d.style;
3531         var iea = 0;
3532         s.left = (l+a.l)+"px";
3533         s.top = (t+a.t)+"px";
3534         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3535  
3536         if(s.width != sws || s.height != shs){
3537             s.width = sws;
3538             s.height = shs;
3539             if(!Roo.isIE){
3540                 var cn = d.childNodes;
3541                 var sww = Math.max(0, (sw-12))+"px";
3542                 cn[0].childNodes[1].style.width = sww;
3543                 cn[1].childNodes[1].style.width = sww;
3544                 cn[2].childNodes[1].style.width = sww;
3545                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3546             }
3547         }
3548     },
3549
3550     /**
3551      * Hides this shadow
3552      */
3553     hide : function(){
3554         if(this.el){
3555             this.el.dom.style.display = "none";
3556             Roo.Shadow.Pool.push(this.el);
3557             delete this.el;
3558         }
3559     },
3560
3561     /**
3562      * Adjust the z-index of this shadow
3563      * @param {Number} zindex The new z-index
3564      */
3565     setZIndex : function(z){
3566         this.zIndex = z;
3567         if(this.el){
3568             this.el.setStyle("z-index", z);
3569         }
3570     }
3571 };
3572
3573 // Private utility class that manages the internal Shadow cache
3574 Roo.Shadow.Pool = function(){
3575     var p = [];
3576     var markup = Roo.isIE ?
3577                  '<div class="x-ie-shadow"></div>' :
3578                  '<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>';
3579     return {
3580         pull : function(){
3581             var sh = p.shift();
3582             if(!sh){
3583                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3584                 sh.autoBoxAdjust = false;
3585             }
3586             return sh;
3587         },
3588
3589         push : function(sh){
3590             p.push(sh);
3591         }
3592     };
3593 }();/*
3594  * Based on:
3595  * Ext JS Library 1.1.1
3596  * Copyright(c) 2006-2007, Ext JS, LLC.
3597  *
3598  * Originally Released Under LGPL - original licence link has changed is not relivant.
3599  *
3600  * Fork - LGPL
3601  * <script type="text/javascript">
3602  */
3603
3604
3605 /**
3606  * @class Roo.SplitBar
3607  * @extends Roo.util.Observable
3608  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3609  * <br><br>
3610  * Usage:
3611  * <pre><code>
3612 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3613                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3614 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3615 split.minSize = 100;
3616 split.maxSize = 600;
3617 split.animate = true;
3618 split.on('moved', splitterMoved);
3619 </code></pre>
3620  * @constructor
3621  * Create a new SplitBar
3622  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3623  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3624  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3625  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3626                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3627                         position of the SplitBar).
3628  */
3629 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3630     
3631     /** @private */
3632     this.el = Roo.get(dragElement, true);
3633     this.el.dom.unselectable = "on";
3634     /** @private */
3635     this.resizingEl = Roo.get(resizingElement, true);
3636
3637     /**
3638      * @private
3639      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3640      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3641      * @type Number
3642      */
3643     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3644     
3645     /**
3646      * The minimum size of the resizing element. (Defaults to 0)
3647      * @type Number
3648      */
3649     this.minSize = 0;
3650     
3651     /**
3652      * The maximum size of the resizing element. (Defaults to 2000)
3653      * @type Number
3654      */
3655     this.maxSize = 2000;
3656     
3657     /**
3658      * Whether to animate the transition to the new size
3659      * @type Boolean
3660      */
3661     this.animate = false;
3662     
3663     /**
3664      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3665      * @type Boolean
3666      */
3667     this.useShim = false;
3668     
3669     /** @private */
3670     this.shim = null;
3671     
3672     if(!existingProxy){
3673         /** @private */
3674         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3675     }else{
3676         this.proxy = Roo.get(existingProxy).dom;
3677     }
3678     /** @private */
3679     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3680     
3681     /** @private */
3682     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3683     
3684     /** @private */
3685     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3686     
3687     /** @private */
3688     this.dragSpecs = {};
3689     
3690     /**
3691      * @private The adapter to use to positon and resize elements
3692      */
3693     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3694     this.adapter.init(this);
3695     
3696     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3697         /** @private */
3698         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3699         this.el.addClass("x-splitbar-h");
3700     }else{
3701         /** @private */
3702         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3703         this.el.addClass("x-splitbar-v");
3704     }
3705     
3706     this.addEvents({
3707         /**
3708          * @event resize
3709          * Fires when the splitter is moved (alias for {@link #event-moved})
3710          * @param {Roo.SplitBar} this
3711          * @param {Number} newSize the new width or height
3712          */
3713         "resize" : true,
3714         /**
3715          * @event moved
3716          * Fires when the splitter is moved
3717          * @param {Roo.SplitBar} this
3718          * @param {Number} newSize the new width or height
3719          */
3720         "moved" : true,
3721         /**
3722          * @event beforeresize
3723          * Fires before the splitter is dragged
3724          * @param {Roo.SplitBar} this
3725          */
3726         "beforeresize" : true,
3727
3728         "beforeapply" : true
3729     });
3730
3731     Roo.util.Observable.call(this);
3732 };
3733
3734 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3735     onStartProxyDrag : function(x, y){
3736         this.fireEvent("beforeresize", this);
3737         if(!this.overlay){
3738             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3739             o.unselectable();
3740             o.enableDisplayMode("block");
3741             // all splitbars share the same overlay
3742             Roo.SplitBar.prototype.overlay = o;
3743         }
3744         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3745         this.overlay.show();
3746         Roo.get(this.proxy).setDisplayed("block");
3747         var size = this.adapter.getElementSize(this);
3748         this.activeMinSize = this.getMinimumSize();;
3749         this.activeMaxSize = this.getMaximumSize();;
3750         var c1 = size - this.activeMinSize;
3751         var c2 = Math.max(this.activeMaxSize - size, 0);
3752         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3753             this.dd.resetConstraints();
3754             this.dd.setXConstraint(
3755                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3756                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3757             );
3758             this.dd.setYConstraint(0, 0);
3759         }else{
3760             this.dd.resetConstraints();
3761             this.dd.setXConstraint(0, 0);
3762             this.dd.setYConstraint(
3763                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3764                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3765             );
3766          }
3767         this.dragSpecs.startSize = size;
3768         this.dragSpecs.startPoint = [x, y];
3769         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3770     },
3771     
3772     /** 
3773      * @private Called after the drag operation by the DDProxy
3774      */
3775     onEndProxyDrag : function(e){
3776         Roo.get(this.proxy).setDisplayed(false);
3777         var endPoint = Roo.lib.Event.getXY(e);
3778         if(this.overlay){
3779             this.overlay.hide();
3780         }
3781         var newSize;
3782         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3783             newSize = this.dragSpecs.startSize + 
3784                 (this.placement == Roo.SplitBar.LEFT ?
3785                     endPoint[0] - this.dragSpecs.startPoint[0] :
3786                     this.dragSpecs.startPoint[0] - endPoint[0]
3787                 );
3788         }else{
3789             newSize = this.dragSpecs.startSize + 
3790                 (this.placement == Roo.SplitBar.TOP ?
3791                     endPoint[1] - this.dragSpecs.startPoint[1] :
3792                     this.dragSpecs.startPoint[1] - endPoint[1]
3793                 );
3794         }
3795         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3796         if(newSize != this.dragSpecs.startSize){
3797             if(this.fireEvent('beforeapply', this, newSize) !== false){
3798                 this.adapter.setElementSize(this, newSize);
3799                 this.fireEvent("moved", this, newSize);
3800                 this.fireEvent("resize", this, newSize);
3801             }
3802         }
3803     },
3804     
3805     /**
3806      * Get the adapter this SplitBar uses
3807      * @return The adapter object
3808      */
3809     getAdapter : function(){
3810         return this.adapter;
3811     },
3812     
3813     /**
3814      * Set the adapter this SplitBar uses
3815      * @param {Object} adapter A SplitBar adapter object
3816      */
3817     setAdapter : function(adapter){
3818         this.adapter = adapter;
3819         this.adapter.init(this);
3820     },
3821     
3822     /**
3823      * Gets the minimum size for the resizing element
3824      * @return {Number} The minimum size
3825      */
3826     getMinimumSize : function(){
3827         return this.minSize;
3828     },
3829     
3830     /**
3831      * Sets the minimum size for the resizing element
3832      * @param {Number} minSize The minimum size
3833      */
3834     setMinimumSize : function(minSize){
3835         this.minSize = minSize;
3836     },
3837     
3838     /**
3839      * Gets the maximum size for the resizing element
3840      * @return {Number} The maximum size
3841      */
3842     getMaximumSize : function(){
3843         return this.maxSize;
3844     },
3845     
3846     /**
3847      * Sets the maximum size for the resizing element
3848      * @param {Number} maxSize The maximum size
3849      */
3850     setMaximumSize : function(maxSize){
3851         this.maxSize = maxSize;
3852     },
3853     
3854     /**
3855      * Sets the initialize size for the resizing element
3856      * @param {Number} size The initial size
3857      */
3858     setCurrentSize : function(size){
3859         var oldAnimate = this.animate;
3860         this.animate = false;
3861         this.adapter.setElementSize(this, size);
3862         this.animate = oldAnimate;
3863     },
3864     
3865     /**
3866      * Destroy this splitbar. 
3867      * @param {Boolean} removeEl True to remove the element
3868      */
3869     destroy : function(removeEl){
3870         if(this.shim){
3871             this.shim.remove();
3872         }
3873         this.dd.unreg();
3874         this.proxy.parentNode.removeChild(this.proxy);
3875         if(removeEl){
3876             this.el.remove();
3877         }
3878     }
3879 });
3880
3881 /**
3882  * @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.
3883  */
3884 Roo.SplitBar.createProxy = function(dir){
3885     var proxy = new Roo.Element(document.createElement("div"));
3886     proxy.unselectable();
3887     var cls = 'x-splitbar-proxy';
3888     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3889     document.body.appendChild(proxy.dom);
3890     return proxy.dom;
3891 };
3892
3893 /** 
3894  * @class Roo.SplitBar.BasicLayoutAdapter
3895  * Default Adapter. It assumes the splitter and resizing element are not positioned
3896  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3897  */
3898 Roo.SplitBar.BasicLayoutAdapter = function(){
3899 };
3900
3901 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3902     // do nothing for now
3903     init : function(s){
3904     
3905     },
3906     /**
3907      * Called before drag operations to get the current size of the resizing element. 
3908      * @param {Roo.SplitBar} s The SplitBar using this adapter
3909      */
3910      getElementSize : function(s){
3911         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3912             return s.resizingEl.getWidth();
3913         }else{
3914             return s.resizingEl.getHeight();
3915         }
3916     },
3917     
3918     /**
3919      * Called after drag operations to set the size of the resizing element.
3920      * @param {Roo.SplitBar} s The SplitBar using this adapter
3921      * @param {Number} newSize The new size to set
3922      * @param {Function} onComplete A function to be invoked when resizing is complete
3923      */
3924     setElementSize : function(s, newSize, onComplete){
3925         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3926             if(!s.animate){
3927                 s.resizingEl.setWidth(newSize);
3928                 if(onComplete){
3929                     onComplete(s, newSize);
3930                 }
3931             }else{
3932                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3933             }
3934         }else{
3935             
3936             if(!s.animate){
3937                 s.resizingEl.setHeight(newSize);
3938                 if(onComplete){
3939                     onComplete(s, newSize);
3940                 }
3941             }else{
3942                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3943             }
3944         }
3945     }
3946 };
3947
3948 /** 
3949  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3950  * @extends Roo.SplitBar.BasicLayoutAdapter
3951  * Adapter that  moves the splitter element to align with the resized sizing element. 
3952  * Used with an absolute positioned SplitBar.
3953  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3954  * document.body, make sure you assign an id to the body element.
3955  */
3956 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3957     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3958     this.container = Roo.get(container);
3959 };
3960
3961 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3962     init : function(s){
3963         this.basic.init(s);
3964     },
3965     
3966     getElementSize : function(s){
3967         return this.basic.getElementSize(s);
3968     },
3969     
3970     setElementSize : function(s, newSize, onComplete){
3971         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3972     },
3973     
3974     moveSplitter : function(s){
3975         var yes = Roo.SplitBar;
3976         switch(s.placement){
3977             case yes.LEFT:
3978                 s.el.setX(s.resizingEl.getRight());
3979                 break;
3980             case yes.RIGHT:
3981                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3982                 break;
3983             case yes.TOP:
3984                 s.el.setY(s.resizingEl.getBottom());
3985                 break;
3986             case yes.BOTTOM:
3987                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3988                 break;
3989         }
3990     }
3991 };
3992
3993 /**
3994  * Orientation constant - Create a vertical SplitBar
3995  * @static
3996  * @type Number
3997  */
3998 Roo.SplitBar.VERTICAL = 1;
3999
4000 /**
4001  * Orientation constant - Create a horizontal SplitBar
4002  * @static
4003  * @type Number
4004  */
4005 Roo.SplitBar.HORIZONTAL = 2;
4006
4007 /**
4008  * Placement constant - The resizing element is to the left of the splitter element
4009  * @static
4010  * @type Number
4011  */
4012 Roo.SplitBar.LEFT = 1;
4013
4014 /**
4015  * Placement constant - The resizing element is to the right of the splitter element
4016  * @static
4017  * @type Number
4018  */
4019 Roo.SplitBar.RIGHT = 2;
4020
4021 /**
4022  * Placement constant - The resizing element is positioned above the splitter element
4023  * @static
4024  * @type Number
4025  */
4026 Roo.SplitBar.TOP = 3;
4027
4028 /**
4029  * Placement constant - The resizing element is positioned under splitter element
4030  * @static
4031  * @type Number
4032  */
4033 Roo.SplitBar.BOTTOM = 4;
4034 /*
4035  * Based on:
4036  * Ext JS Library 1.1.1
4037  * Copyright(c) 2006-2007, Ext JS, LLC.
4038  *
4039  * Originally Released Under LGPL - original licence link has changed is not relivant.
4040  *
4041  * Fork - LGPL
4042  * <script type="text/javascript">
4043  */
4044
4045 /**
4046  * @class Roo.View
4047  * @extends Roo.util.Observable
4048  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4049  * This class also supports single and multi selection modes. <br>
4050  * Create a data model bound view:
4051  <pre><code>
4052  var store = new Roo.data.Store(...);
4053
4054  var view = new Roo.View({
4055     el : "my-element",
4056     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4057  
4058     singleSelect: true,
4059     selectedClass: "ydataview-selected",
4060     store: store
4061  });
4062
4063  // listen for node click?
4064  view.on("click", function(vw, index, node, e){
4065  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4066  });
4067
4068  // load XML data
4069  dataModel.load("foobar.xml");
4070  </code></pre>
4071  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4072  * <br><br>
4073  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4074  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4075  * 
4076  * Note: old style constructor is still suported (container, template, config)
4077  * 
4078  * @constructor
4079  * Create a new View
4080  * @param {Object} config The config object
4081  * 
4082  */
4083 Roo.View = function(config, depreciated_tpl, depreciated_config){
4084     
4085     this.parent = false;
4086     
4087     if (typeof(depreciated_tpl) == 'undefined') {
4088         // new way.. - universal constructor.
4089         Roo.apply(this, config);
4090         this.el  = Roo.get(this.el);
4091     } else {
4092         // old format..
4093         this.el  = Roo.get(config);
4094         this.tpl = depreciated_tpl;
4095         Roo.apply(this, depreciated_config);
4096     }
4097     this.wrapEl  = this.el.wrap().wrap();
4098     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4099     
4100     
4101     if(typeof(this.tpl) == "string"){
4102         this.tpl = new Roo.Template(this.tpl);
4103     } else {
4104         // support xtype ctors..
4105         this.tpl = new Roo.factory(this.tpl, Roo);
4106     }
4107     
4108     
4109     this.tpl.compile();
4110     
4111     /** @private */
4112     this.addEvents({
4113         /**
4114          * @event beforeclick
4115          * Fires before a click is processed. Returns false to cancel the default action.
4116          * @param {Roo.View} this
4117          * @param {Number} index The index of the target node
4118          * @param {HTMLElement} node The target node
4119          * @param {Roo.EventObject} e The raw event object
4120          */
4121             "beforeclick" : true,
4122         /**
4123          * @event click
4124          * Fires when a template node is clicked.
4125          * @param {Roo.View} this
4126          * @param {Number} index The index of the target node
4127          * @param {HTMLElement} node The target node
4128          * @param {Roo.EventObject} e The raw event object
4129          */
4130             "click" : true,
4131         /**
4132          * @event dblclick
4133          * Fires when a template node is double clicked.
4134          * @param {Roo.View} this
4135          * @param {Number} index The index of the target node
4136          * @param {HTMLElement} node The target node
4137          * @param {Roo.EventObject} e The raw event object
4138          */
4139             "dblclick" : true,
4140         /**
4141          * @event contextmenu
4142          * Fires when a template node is right clicked.
4143          * @param {Roo.View} this
4144          * @param {Number} index The index of the target node
4145          * @param {HTMLElement} node The target node
4146          * @param {Roo.EventObject} e The raw event object
4147          */
4148             "contextmenu" : true,
4149         /**
4150          * @event selectionchange
4151          * Fires when the selected nodes change.
4152          * @param {Roo.View} this
4153          * @param {Array} selections Array of the selected nodes
4154          */
4155             "selectionchange" : true,
4156     
4157         /**
4158          * @event beforeselect
4159          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4160          * @param {Roo.View} this
4161          * @param {HTMLElement} node The node to be selected
4162          * @param {Array} selections Array of currently selected nodes
4163          */
4164             "beforeselect" : true,
4165         /**
4166          * @event preparedata
4167          * Fires on every row to render, to allow you to change the data.
4168          * @param {Roo.View} this
4169          * @param {Object} data to be rendered (change this)
4170          */
4171           "preparedata" : true
4172           
4173           
4174         });
4175
4176
4177
4178     this.el.on({
4179         "click": this.onClick,
4180         "dblclick": this.onDblClick,
4181         "contextmenu": this.onContextMenu,
4182         scope:this
4183     });
4184
4185     this.selections = [];
4186     this.nodes = [];
4187     this.cmp = new Roo.CompositeElementLite([]);
4188     if(this.store){
4189         this.store = Roo.factory(this.store, Roo.data);
4190         this.setStore(this.store, true);
4191     }
4192     
4193     if ( this.footer && this.footer.xtype) {
4194            
4195          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4196         
4197         this.footer.dataSource = this.store;
4198         this.footer.container = fctr;
4199         this.footer = Roo.factory(this.footer, Roo);
4200         fctr.insertFirst(this.el);
4201         
4202         // this is a bit insane - as the paging toolbar seems to detach the el..
4203 //        dom.parentNode.parentNode.parentNode
4204          // they get detached?
4205     }
4206     
4207     
4208     Roo.View.superclass.constructor.call(this);
4209     
4210     
4211 };
4212
4213 Roo.extend(Roo.View, Roo.util.Observable, {
4214     
4215      /**
4216      * @cfg {Roo.data.Store} store Data store to load data from.
4217      */
4218     store : false,
4219     
4220     /**
4221      * @cfg {String|Roo.Element} el The container element.
4222      */
4223     el : '',
4224     
4225     /**
4226      * @cfg {String|Roo.Template} tpl The template used by this View 
4227      */
4228     tpl : false,
4229     /**
4230      * @cfg {String} dataName the named area of the template to use as the data area
4231      *                          Works with domtemplates roo-name="name"
4232      */
4233     dataName: false,
4234     /**
4235      * @cfg {String} selectedClass The css class to add to selected nodes
4236      */
4237     selectedClass : "x-view-selected",
4238      /**
4239      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4240      */
4241     emptyText : "",
4242     
4243     /**
4244      * @cfg {String} text to display on mask (default Loading)
4245      */
4246     mask : false,
4247     /**
4248      * @cfg {Boolean} multiSelect Allow multiple selection
4249      */
4250     multiSelect : false,
4251     /**
4252      * @cfg {Boolean} singleSelect Allow single selection
4253      */
4254     singleSelect:  false,
4255     
4256     /**
4257      * @cfg {Boolean} toggleSelect - selecting 
4258      */
4259     toggleSelect : false,
4260     
4261     /**
4262      * @cfg {Boolean} tickable - selecting 
4263      */
4264     tickable : false,
4265     
4266     /**
4267      * Returns the element this view is bound to.
4268      * @return {Roo.Element}
4269      */
4270     getEl : function(){
4271         return this.wrapEl;
4272     },
4273     
4274     
4275
4276     /**
4277      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4278      */
4279     refresh : function(){
4280         //Roo.log('refresh');
4281         var t = this.tpl;
4282         
4283         // if we are using something like 'domtemplate', then
4284         // the what gets used is:
4285         // t.applySubtemplate(NAME, data, wrapping data..)
4286         // the outer template then get' applied with
4287         //     the store 'extra data'
4288         // and the body get's added to the
4289         //      roo-name="data" node?
4290         //      <span class='roo-tpl-{name}'></span> ?????
4291         
4292         
4293         
4294         this.clearSelections();
4295         this.el.update("");
4296         var html = [];
4297         var records = this.store.getRange();
4298         if(records.length < 1) {
4299             
4300             // is this valid??  = should it render a template??
4301             
4302             this.el.update(this.emptyText);
4303             return;
4304         }
4305         var el = this.el;
4306         if (this.dataName) {
4307             this.el.update(t.apply(this.store.meta)); //????
4308             el = this.el.child('.roo-tpl-' + this.dataName);
4309         }
4310         
4311         for(var i = 0, len = records.length; i < len; i++){
4312             var data = this.prepareData(records[i].data, i, records[i]);
4313             this.fireEvent("preparedata", this, data, i, records[i]);
4314             
4315             var d = Roo.apply({}, data);
4316             
4317             if(this.tickable){
4318                 Roo.apply(d, {'roo-id' : Roo.id()});
4319                 
4320                 var _this = this;
4321             
4322                 Roo.each(this.parent.item, function(item){
4323                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4324                         return;
4325                     }
4326                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4327                 });
4328             }
4329             
4330             html[html.length] = Roo.util.Format.trim(
4331                 this.dataName ?
4332                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4333                     t.apply(d)
4334             );
4335         }
4336         
4337         
4338         
4339         el.update(html.join(""));
4340         this.nodes = el.dom.childNodes;
4341         this.updateIndexes(0);
4342     },
4343     
4344
4345     /**
4346      * Function to override to reformat the data that is sent to
4347      * the template for each node.
4348      * DEPRICATED - use the preparedata event handler.
4349      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4350      * a JSON object for an UpdateManager bound view).
4351      */
4352     prepareData : function(data, index, record)
4353     {
4354         this.fireEvent("preparedata", this, data, index, record);
4355         return data;
4356     },
4357
4358     onUpdate : function(ds, record){
4359         // Roo.log('on update');   
4360         this.clearSelections();
4361         var index = this.store.indexOf(record);
4362         var n = this.nodes[index];
4363         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4364         n.parentNode.removeChild(n);
4365         this.updateIndexes(index, index);
4366     },
4367
4368     
4369     
4370 // --------- FIXME     
4371     onAdd : function(ds, records, index)
4372     {
4373         //Roo.log(['on Add', ds, records, index] );        
4374         this.clearSelections();
4375         if(this.nodes.length == 0){
4376             this.refresh();
4377             return;
4378         }
4379         var n = this.nodes[index];
4380         for(var i = 0, len = records.length; i < len; i++){
4381             var d = this.prepareData(records[i].data, i, records[i]);
4382             if(n){
4383                 this.tpl.insertBefore(n, d);
4384             }else{
4385                 
4386                 this.tpl.append(this.el, d);
4387             }
4388         }
4389         this.updateIndexes(index);
4390     },
4391
4392     onRemove : function(ds, record, index){
4393        // Roo.log('onRemove');
4394         this.clearSelections();
4395         var el = this.dataName  ?
4396             this.el.child('.roo-tpl-' + this.dataName) :
4397             this.el; 
4398         
4399         el.dom.removeChild(this.nodes[index]);
4400         this.updateIndexes(index);
4401     },
4402
4403     /**
4404      * Refresh an individual node.
4405      * @param {Number} index
4406      */
4407     refreshNode : function(index){
4408         this.onUpdate(this.store, this.store.getAt(index));
4409     },
4410
4411     updateIndexes : function(startIndex, endIndex){
4412         var ns = this.nodes;
4413         startIndex = startIndex || 0;
4414         endIndex = endIndex || ns.length - 1;
4415         for(var i = startIndex; i <= endIndex; i++){
4416             ns[i].nodeIndex = i;
4417         }
4418     },
4419
4420     /**
4421      * Changes the data store this view uses and refresh the view.
4422      * @param {Store} store
4423      */
4424     setStore : function(store, initial){
4425         if(!initial && this.store){
4426             this.store.un("datachanged", this.refresh);
4427             this.store.un("add", this.onAdd);
4428             this.store.un("remove", this.onRemove);
4429             this.store.un("update", this.onUpdate);
4430             this.store.un("clear", this.refresh);
4431             this.store.un("beforeload", this.onBeforeLoad);
4432             this.store.un("load", this.onLoad);
4433             this.store.un("loadexception", this.onLoad);
4434         }
4435         if(store){
4436           
4437             store.on("datachanged", this.refresh, this);
4438             store.on("add", this.onAdd, this);
4439             store.on("remove", this.onRemove, this);
4440             store.on("update", this.onUpdate, this);
4441             store.on("clear", this.refresh, this);
4442             store.on("beforeload", this.onBeforeLoad, this);
4443             store.on("load", this.onLoad, this);
4444             store.on("loadexception", this.onLoad, this);
4445         }
4446         
4447         if(store){
4448             this.refresh();
4449         }
4450     },
4451     /**
4452      * onbeforeLoad - masks the loading area.
4453      *
4454      */
4455     onBeforeLoad : function(store,opts)
4456     {
4457          //Roo.log('onBeforeLoad');   
4458         if (!opts.add) {
4459             this.el.update("");
4460         }
4461         this.el.mask(this.mask ? this.mask : "Loading" ); 
4462     },
4463     onLoad : function ()
4464     {
4465         this.el.unmask();
4466     },
4467     
4468
4469     /**
4470      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4471      * @param {HTMLElement} node
4472      * @return {HTMLElement} The template node
4473      */
4474     findItemFromChild : function(node){
4475         var el = this.dataName  ?
4476             this.el.child('.roo-tpl-' + this.dataName,true) :
4477             this.el.dom; 
4478         
4479         if(!node || node.parentNode == el){
4480                     return node;
4481             }
4482             var p = node.parentNode;
4483             while(p && p != el){
4484             if(p.parentNode == el){
4485                 return p;
4486             }
4487             p = p.parentNode;
4488         }
4489             return null;
4490     },
4491
4492     /** @ignore */
4493     onClick : function(e){
4494         var item = this.findItemFromChild(e.getTarget());
4495         if(item){
4496             var index = this.indexOf(item);
4497             if(this.onItemClick(item, index, e) !== false){
4498                 this.fireEvent("click", this, index, item, e);
4499             }
4500         }else{
4501             this.clearSelections();
4502         }
4503     },
4504
4505     /** @ignore */
4506     onContextMenu : function(e){
4507         var item = this.findItemFromChild(e.getTarget());
4508         if(item){
4509             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4510         }
4511     },
4512
4513     /** @ignore */
4514     onDblClick : function(e){
4515         var item = this.findItemFromChild(e.getTarget());
4516         if(item){
4517             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4518         }
4519     },
4520
4521     onItemClick : function(item, index, e)
4522     {
4523         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4524             return false;
4525         }
4526         if (this.toggleSelect) {
4527             var m = this.isSelected(item) ? 'unselect' : 'select';
4528             //Roo.log(m);
4529             var _t = this;
4530             _t[m](item, true, false);
4531             return true;
4532         }
4533         if(this.multiSelect || this.singleSelect){
4534             if(this.multiSelect && e.shiftKey && this.lastSelection){
4535                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4536             }else{
4537                 this.select(item, this.multiSelect && e.ctrlKey);
4538                 this.lastSelection = item;
4539             }
4540             
4541             if(!this.tickable){
4542                 e.preventDefault();
4543             }
4544             
4545         }
4546         return true;
4547     },
4548
4549     /**
4550      * Get the number of selected nodes.
4551      * @return {Number}
4552      */
4553     getSelectionCount : function(){
4554         return this.selections.length;
4555     },
4556
4557     /**
4558      * Get the currently selected nodes.
4559      * @return {Array} An array of HTMLElements
4560      */
4561     getSelectedNodes : function(){
4562         return this.selections;
4563     },
4564
4565     /**
4566      * Get the indexes of the selected nodes.
4567      * @return {Array}
4568      */
4569     getSelectedIndexes : function(){
4570         var indexes = [], s = this.selections;
4571         for(var i = 0, len = s.length; i < len; i++){
4572             indexes.push(s[i].nodeIndex);
4573         }
4574         return indexes;
4575     },
4576
4577     /**
4578      * Clear all selections
4579      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4580      */
4581     clearSelections : function(suppressEvent){
4582         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4583             this.cmp.elements = this.selections;
4584             this.cmp.removeClass(this.selectedClass);
4585             this.selections = [];
4586             if(!suppressEvent){
4587                 this.fireEvent("selectionchange", this, this.selections);
4588             }
4589         }
4590     },
4591
4592     /**
4593      * Returns true if the passed node is selected
4594      * @param {HTMLElement/Number} node The node or node index
4595      * @return {Boolean}
4596      */
4597     isSelected : function(node){
4598         var s = this.selections;
4599         if(s.length < 1){
4600             return false;
4601         }
4602         node = this.getNode(node);
4603         return s.indexOf(node) !== -1;
4604     },
4605
4606     /**
4607      * Selects nodes.
4608      * @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
4609      * @param {Boolean} keepExisting (optional) true to keep existing selections
4610      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4611      */
4612     select : function(nodeInfo, keepExisting, suppressEvent){
4613         if(nodeInfo instanceof Array){
4614             if(!keepExisting){
4615                 this.clearSelections(true);
4616             }
4617             for(var i = 0, len = nodeInfo.length; i < len; i++){
4618                 this.select(nodeInfo[i], true, true);
4619             }
4620             return;
4621         } 
4622         var node = this.getNode(nodeInfo);
4623         if(!node || this.isSelected(node)){
4624             return; // already selected.
4625         }
4626         if(!keepExisting){
4627             this.clearSelections(true);
4628         }
4629         
4630         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4631             Roo.fly(node).addClass(this.selectedClass);
4632             this.selections.push(node);
4633             if(!suppressEvent){
4634                 this.fireEvent("selectionchange", this, this.selections);
4635             }
4636         }
4637         
4638         
4639     },
4640       /**
4641      * Unselects nodes.
4642      * @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
4643      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4644      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4645      */
4646     unselect : function(nodeInfo, keepExisting, suppressEvent)
4647     {
4648         if(nodeInfo instanceof Array){
4649             Roo.each(this.selections, function(s) {
4650                 this.unselect(s, nodeInfo);
4651             }, this);
4652             return;
4653         }
4654         var node = this.getNode(nodeInfo);
4655         if(!node || !this.isSelected(node)){
4656             //Roo.log("not selected");
4657             return; // not selected.
4658         }
4659         // fireevent???
4660         var ns = [];
4661         Roo.each(this.selections, function(s) {
4662             if (s == node ) {
4663                 Roo.fly(node).removeClass(this.selectedClass);
4664
4665                 return;
4666             }
4667             ns.push(s);
4668         },this);
4669         
4670         this.selections= ns;
4671         this.fireEvent("selectionchange", this, this.selections);
4672     },
4673
4674     /**
4675      * Gets a template node.
4676      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4677      * @return {HTMLElement} The node or null if it wasn't found
4678      */
4679     getNode : function(nodeInfo){
4680         if(typeof nodeInfo == "string"){
4681             return document.getElementById(nodeInfo);
4682         }else if(typeof nodeInfo == "number"){
4683             return this.nodes[nodeInfo];
4684         }
4685         return nodeInfo;
4686     },
4687
4688     /**
4689      * Gets a range template nodes.
4690      * @param {Number} startIndex
4691      * @param {Number} endIndex
4692      * @return {Array} An array of nodes
4693      */
4694     getNodes : function(start, end){
4695         var ns = this.nodes;
4696         start = start || 0;
4697         end = typeof end == "undefined" ? ns.length - 1 : end;
4698         var nodes = [];
4699         if(start <= end){
4700             for(var i = start; i <= end; i++){
4701                 nodes.push(ns[i]);
4702             }
4703         } else{
4704             for(var i = start; i >= end; i--){
4705                 nodes.push(ns[i]);
4706             }
4707         }
4708         return nodes;
4709     },
4710
4711     /**
4712      * Finds the index of the passed node
4713      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4714      * @return {Number} The index of the node or -1
4715      */
4716     indexOf : function(node){
4717         node = this.getNode(node);
4718         if(typeof node.nodeIndex == "number"){
4719             return node.nodeIndex;
4720         }
4721         var ns = this.nodes;
4722         for(var i = 0, len = ns.length; i < len; i++){
4723             if(ns[i] == node){
4724                 return i;
4725             }
4726         }
4727         return -1;
4728     }
4729 });
4730 /*
4731  * Based on:
4732  * Ext JS Library 1.1.1
4733  * Copyright(c) 2006-2007, Ext JS, LLC.
4734  *
4735  * Originally Released Under LGPL - original licence link has changed is not relivant.
4736  *
4737  * Fork - LGPL
4738  * <script type="text/javascript">
4739  */
4740
4741 /**
4742  * @class Roo.JsonView
4743  * @extends Roo.View
4744  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4745 <pre><code>
4746 var view = new Roo.JsonView({
4747     container: "my-element",
4748     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4749     multiSelect: true, 
4750     jsonRoot: "data" 
4751 });
4752
4753 // listen for node click?
4754 view.on("click", function(vw, index, node, e){
4755     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4756 });
4757
4758 // direct load of JSON data
4759 view.load("foobar.php");
4760
4761 // Example from my blog list
4762 var tpl = new Roo.Template(
4763     '&lt;div class="entry"&gt;' +
4764     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4765     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4766     "&lt;/div&gt;&lt;hr /&gt;"
4767 );
4768
4769 var moreView = new Roo.JsonView({
4770     container :  "entry-list", 
4771     template : tpl,
4772     jsonRoot: "posts"
4773 });
4774 moreView.on("beforerender", this.sortEntries, this);
4775 moreView.load({
4776     url: "/blog/get-posts.php",
4777     params: "allposts=true",
4778     text: "Loading Blog Entries..."
4779 });
4780 </code></pre>
4781
4782 * Note: old code is supported with arguments : (container, template, config)
4783
4784
4785  * @constructor
4786  * Create a new JsonView
4787  * 
4788  * @param {Object} config The config object
4789  * 
4790  */
4791 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4792     
4793     
4794     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4795
4796     var um = this.el.getUpdateManager();
4797     um.setRenderer(this);
4798     um.on("update", this.onLoad, this);
4799     um.on("failure", this.onLoadException, this);
4800
4801     /**
4802      * @event beforerender
4803      * Fires before rendering of the downloaded JSON data.
4804      * @param {Roo.JsonView} this
4805      * @param {Object} data The JSON data loaded
4806      */
4807     /**
4808      * @event load
4809      * Fires when data is loaded.
4810      * @param {Roo.JsonView} this
4811      * @param {Object} data The JSON data loaded
4812      * @param {Object} response The raw Connect response object
4813      */
4814     /**
4815      * @event loadexception
4816      * Fires when loading fails.
4817      * @param {Roo.JsonView} this
4818      * @param {Object} response The raw Connect response object
4819      */
4820     this.addEvents({
4821         'beforerender' : true,
4822         'load' : true,
4823         'loadexception' : true
4824     });
4825 };
4826 Roo.extend(Roo.JsonView, Roo.View, {
4827     /**
4828      * @type {String} The root property in the loaded JSON object that contains the data
4829      */
4830     jsonRoot : "",
4831
4832     /**
4833      * Refreshes the view.
4834      */
4835     refresh : function(){
4836         this.clearSelections();
4837         this.el.update("");
4838         var html = [];
4839         var o = this.jsonData;
4840         if(o && o.length > 0){
4841             for(var i = 0, len = o.length; i < len; i++){
4842                 var data = this.prepareData(o[i], i, o);
4843                 html[html.length] = this.tpl.apply(data);
4844             }
4845         }else{
4846             html.push(this.emptyText);
4847         }
4848         this.el.update(html.join(""));
4849         this.nodes = this.el.dom.childNodes;
4850         this.updateIndexes(0);
4851     },
4852
4853     /**
4854      * 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.
4855      * @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:
4856      <pre><code>
4857      view.load({
4858          url: "your-url.php",
4859          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4860          callback: yourFunction,
4861          scope: yourObject, //(optional scope)
4862          discardUrl: false,
4863          nocache: false,
4864          text: "Loading...",
4865          timeout: 30,
4866          scripts: false
4867      });
4868      </code></pre>
4869      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4870      * 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.
4871      * @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}
4872      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4873      * @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.
4874      */
4875     load : function(){
4876         var um = this.el.getUpdateManager();
4877         um.update.apply(um, arguments);
4878     },
4879
4880     // note - render is a standard framework call...
4881     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4882     render : function(el, response){
4883         
4884         this.clearSelections();
4885         this.el.update("");
4886         var o;
4887         try{
4888             if (response != '') {
4889                 o = Roo.util.JSON.decode(response.responseText);
4890                 if(this.jsonRoot){
4891                     
4892                     o = o[this.jsonRoot];
4893                 }
4894             }
4895         } catch(e){
4896         }
4897         /**
4898          * The current JSON data or null
4899          */
4900         this.jsonData = o;
4901         this.beforeRender();
4902         this.refresh();
4903     },
4904
4905 /**
4906  * Get the number of records in the current JSON dataset
4907  * @return {Number}
4908  */
4909     getCount : function(){
4910         return this.jsonData ? this.jsonData.length : 0;
4911     },
4912
4913 /**
4914  * Returns the JSON object for the specified node(s)
4915  * @param {HTMLElement/Array} node The node or an array of nodes
4916  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4917  * you get the JSON object for the node
4918  */
4919     getNodeData : function(node){
4920         if(node instanceof Array){
4921             var data = [];
4922             for(var i = 0, len = node.length; i < len; i++){
4923                 data.push(this.getNodeData(node[i]));
4924             }
4925             return data;
4926         }
4927         return this.jsonData[this.indexOf(node)] || null;
4928     },
4929
4930     beforeRender : function(){
4931         this.snapshot = this.jsonData;
4932         if(this.sortInfo){
4933             this.sort.apply(this, this.sortInfo);
4934         }
4935         this.fireEvent("beforerender", this, this.jsonData);
4936     },
4937
4938     onLoad : function(el, o){
4939         this.fireEvent("load", this, this.jsonData, o);
4940     },
4941
4942     onLoadException : function(el, o){
4943         this.fireEvent("loadexception", this, o);
4944     },
4945
4946 /**
4947  * Filter the data by a specific property.
4948  * @param {String} property A property on your JSON objects
4949  * @param {String/RegExp} value Either string that the property values
4950  * should start with, or a RegExp to test against the property
4951  */
4952     filter : function(property, value){
4953         if(this.jsonData){
4954             var data = [];
4955             var ss = this.snapshot;
4956             if(typeof value == "string"){
4957                 var vlen = value.length;
4958                 if(vlen == 0){
4959                     this.clearFilter();
4960                     return;
4961                 }
4962                 value = value.toLowerCase();
4963                 for(var i = 0, len = ss.length; i < len; i++){
4964                     var o = ss[i];
4965                     if(o[property].substr(0, vlen).toLowerCase() == value){
4966                         data.push(o);
4967                     }
4968                 }
4969             } else if(value.exec){ // regex?
4970                 for(var i = 0, len = ss.length; i < len; i++){
4971                     var o = ss[i];
4972                     if(value.test(o[property])){
4973                         data.push(o);
4974                     }
4975                 }
4976             } else{
4977                 return;
4978             }
4979             this.jsonData = data;
4980             this.refresh();
4981         }
4982     },
4983
4984 /**
4985  * Filter by a function. The passed function will be called with each
4986  * object in the current dataset. If the function returns true the value is kept,
4987  * otherwise it is filtered.
4988  * @param {Function} fn
4989  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4990  */
4991     filterBy : function(fn, scope){
4992         if(this.jsonData){
4993             var data = [];
4994             var ss = this.snapshot;
4995             for(var i = 0, len = ss.length; i < len; i++){
4996                 var o = ss[i];
4997                 if(fn.call(scope || this, o)){
4998                     data.push(o);
4999                 }
5000             }
5001             this.jsonData = data;
5002             this.refresh();
5003         }
5004     },
5005
5006 /**
5007  * Clears the current filter.
5008  */
5009     clearFilter : function(){
5010         if(this.snapshot && this.jsonData != this.snapshot){
5011             this.jsonData = this.snapshot;
5012             this.refresh();
5013         }
5014     },
5015
5016
5017 /**
5018  * Sorts the data for this view and refreshes it.
5019  * @param {String} property A property on your JSON objects to sort on
5020  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5021  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5022  */
5023     sort : function(property, dir, sortType){
5024         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5025         if(this.jsonData){
5026             var p = property;
5027             var dsc = dir && dir.toLowerCase() == "desc";
5028             var f = function(o1, o2){
5029                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5030                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5031                 ;
5032                 if(v1 < v2){
5033                     return dsc ? +1 : -1;
5034                 } else if(v1 > v2){
5035                     return dsc ? -1 : +1;
5036                 } else{
5037                     return 0;
5038                 }
5039             };
5040             this.jsonData.sort(f);
5041             this.refresh();
5042             if(this.jsonData != this.snapshot){
5043                 this.snapshot.sort(f);
5044             }
5045         }
5046     }
5047 });/*
5048  * Based on:
5049  * Ext JS Library 1.1.1
5050  * Copyright(c) 2006-2007, Ext JS, LLC.
5051  *
5052  * Originally Released Under LGPL - original licence link has changed is not relivant.
5053  *
5054  * Fork - LGPL
5055  * <script type="text/javascript">
5056  */
5057  
5058
5059 /**
5060  * @class Roo.ColorPalette
5061  * @extends Roo.Component
5062  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5063  * Here's an example of typical usage:
5064  * <pre><code>
5065 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5066 cp.render('my-div');
5067
5068 cp.on('select', function(palette, selColor){
5069     // do something with selColor
5070 });
5071 </code></pre>
5072  * @constructor
5073  * Create a new ColorPalette
5074  * @param {Object} config The config object
5075  */
5076 Roo.ColorPalette = function(config){
5077     Roo.ColorPalette.superclass.constructor.call(this, config);
5078     this.addEvents({
5079         /**
5080              * @event select
5081              * Fires when a color is selected
5082              * @param {ColorPalette} this
5083              * @param {String} color The 6-digit color hex code (without the # symbol)
5084              */
5085         select: true
5086     });
5087
5088     if(this.handler){
5089         this.on("select", this.handler, this.scope, true);
5090     }
5091 };
5092 Roo.extend(Roo.ColorPalette, Roo.Component, {
5093     /**
5094      * @cfg {String} itemCls
5095      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5096      */
5097     itemCls : "x-color-palette",
5098     /**
5099      * @cfg {String} value
5100      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5101      * the hex codes are case-sensitive.
5102      */
5103     value : null,
5104     clickEvent:'click',
5105     // private
5106     ctype: "Roo.ColorPalette",
5107
5108     /**
5109      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5110      */
5111     allowReselect : false,
5112
5113     /**
5114      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5115      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5116      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5117      * of colors with the width setting until the box is symmetrical.</p>
5118      * <p>You can override individual colors if needed:</p>
5119      * <pre><code>
5120 var cp = new Roo.ColorPalette();
5121 cp.colors[0] = "FF0000";  // change the first box to red
5122 </code></pre>
5123
5124 Or you can provide a custom array of your own for complete control:
5125 <pre><code>
5126 var cp = new Roo.ColorPalette();
5127 cp.colors = ["000000", "993300", "333300"];
5128 </code></pre>
5129      * @type Array
5130      */
5131     colors : [
5132         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5133         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5134         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5135         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5136         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5137     ],
5138
5139     // private
5140     onRender : function(container, position){
5141         var t = new Roo.MasterTemplate(
5142             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5143         );
5144         var c = this.colors;
5145         for(var i = 0, len = c.length; i < len; i++){
5146             t.add([c[i]]);
5147         }
5148         var el = document.createElement("div");
5149         el.className = this.itemCls;
5150         t.overwrite(el);
5151         container.dom.insertBefore(el, position);
5152         this.el = Roo.get(el);
5153         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5154         if(this.clickEvent != 'click'){
5155             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5156         }
5157     },
5158
5159     // private
5160     afterRender : function(){
5161         Roo.ColorPalette.superclass.afterRender.call(this);
5162         if(this.value){
5163             var s = this.value;
5164             this.value = null;
5165             this.select(s);
5166         }
5167     },
5168
5169     // private
5170     handleClick : function(e, t){
5171         e.preventDefault();
5172         if(!this.disabled){
5173             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5174             this.select(c.toUpperCase());
5175         }
5176     },
5177
5178     /**
5179      * Selects the specified color in the palette (fires the select event)
5180      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5181      */
5182     select : function(color){
5183         color = color.replace("#", "");
5184         if(color != this.value || this.allowReselect){
5185             var el = this.el;
5186             if(this.value){
5187                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5188             }
5189             el.child("a.color-"+color).addClass("x-color-palette-sel");
5190             this.value = color;
5191             this.fireEvent("select", this, color);
5192         }
5193     }
5194 });/*
5195  * Based on:
5196  * Ext JS Library 1.1.1
5197  * Copyright(c) 2006-2007, Ext JS, LLC.
5198  *
5199  * Originally Released Under LGPL - original licence link has changed is not relivant.
5200  *
5201  * Fork - LGPL
5202  * <script type="text/javascript">
5203  */
5204  
5205 /**
5206  * @class Roo.DatePicker
5207  * @extends Roo.Component
5208  * Simple date picker class.
5209  * @constructor
5210  * Create a new DatePicker
5211  * @param {Object} config The config object
5212  */
5213 Roo.DatePicker = function(config){
5214     Roo.DatePicker.superclass.constructor.call(this, config);
5215
5216     this.value = config && config.value ?
5217                  config.value.clearTime() : new Date().clearTime();
5218
5219     this.addEvents({
5220         /**
5221              * @event select
5222              * Fires when a date is selected
5223              * @param {DatePicker} this
5224              * @param {Date} date The selected date
5225              */
5226         'select': true,
5227         /**
5228              * @event monthchange
5229              * Fires when the displayed month changes 
5230              * @param {DatePicker} this
5231              * @param {Date} date The selected month
5232              */
5233         'monthchange': true
5234     });
5235
5236     if(this.handler){
5237         this.on("select", this.handler,  this.scope || this);
5238     }
5239     // build the disabledDatesRE
5240     if(!this.disabledDatesRE && this.disabledDates){
5241         var dd = this.disabledDates;
5242         var re = "(?:";
5243         for(var i = 0; i < dd.length; i++){
5244             re += dd[i];
5245             if(i != dd.length-1) {
5246                 re += "|";
5247             }
5248         }
5249         this.disabledDatesRE = new RegExp(re + ")");
5250     }
5251 };
5252
5253 Roo.extend(Roo.DatePicker, Roo.Component, {
5254     /**
5255      * @cfg {String} todayText
5256      * The text to display on the button that selects the current date (defaults to "Today")
5257      */
5258     todayText : "Today",
5259     /**
5260      * @cfg {String} okText
5261      * The text to display on the ok button
5262      */
5263     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5264     /**
5265      * @cfg {String} cancelText
5266      * The text to display on the cancel button
5267      */
5268     cancelText : "Cancel",
5269     /**
5270      * @cfg {String} todayTip
5271      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5272      */
5273     todayTip : "{0} (Spacebar)",
5274     /**
5275      * @cfg {Date} minDate
5276      * Minimum allowable date (JavaScript date object, defaults to null)
5277      */
5278     minDate : null,
5279     /**
5280      * @cfg {Date} maxDate
5281      * Maximum allowable date (JavaScript date object, defaults to null)
5282      */
5283     maxDate : null,
5284     /**
5285      * @cfg {String} minText
5286      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5287      */
5288     minText : "This date is before the minimum date",
5289     /**
5290      * @cfg {String} maxText
5291      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5292      */
5293     maxText : "This date is after the maximum date",
5294     /**
5295      * @cfg {String} format
5296      * The default date format string which can be overriden for localization support.  The format must be
5297      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5298      */
5299     format : "m/d/y",
5300     /**
5301      * @cfg {Array} disabledDays
5302      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5303      */
5304     disabledDays : null,
5305     /**
5306      * @cfg {String} disabledDaysText
5307      * The tooltip to display when the date falls on a disabled day (defaults to "")
5308      */
5309     disabledDaysText : "",
5310     /**
5311      * @cfg {RegExp} disabledDatesRE
5312      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5313      */
5314     disabledDatesRE : null,
5315     /**
5316      * @cfg {String} disabledDatesText
5317      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5318      */
5319     disabledDatesText : "",
5320     /**
5321      * @cfg {Boolean} constrainToViewport
5322      * True to constrain the date picker to the viewport (defaults to true)
5323      */
5324     constrainToViewport : true,
5325     /**
5326      * @cfg {Array} monthNames
5327      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5328      */
5329     monthNames : Date.monthNames,
5330     /**
5331      * @cfg {Array} dayNames
5332      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5333      */
5334     dayNames : Date.dayNames,
5335     /**
5336      * @cfg {String} nextText
5337      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5338      */
5339     nextText: 'Next Month (Control+Right)',
5340     /**
5341      * @cfg {String} prevText
5342      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5343      */
5344     prevText: 'Previous Month (Control+Left)',
5345     /**
5346      * @cfg {String} monthYearText
5347      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5348      */
5349     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5350     /**
5351      * @cfg {Number} startDay
5352      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5353      */
5354     startDay : 0,
5355     /**
5356      * @cfg {Bool} showClear
5357      * Show a clear button (usefull for date form elements that can be blank.)
5358      */
5359     
5360     showClear: false,
5361     
5362     /**
5363      * Sets the value of the date field
5364      * @param {Date} value The date to set
5365      */
5366     setValue : function(value){
5367         var old = this.value;
5368         
5369         if (typeof(value) == 'string') {
5370          
5371             value = Date.parseDate(value, this.format);
5372         }
5373         if (!value) {
5374             value = new Date();
5375         }
5376         
5377         this.value = value.clearTime(true);
5378         if(this.el){
5379             this.update(this.value);
5380         }
5381     },
5382
5383     /**
5384      * Gets the current selected value of the date field
5385      * @return {Date} The selected date
5386      */
5387     getValue : function(){
5388         return this.value;
5389     },
5390
5391     // private
5392     focus : function(){
5393         if(this.el){
5394             this.update(this.activeDate);
5395         }
5396     },
5397
5398     // privateval
5399     onRender : function(container, position){
5400         
5401         var m = [
5402              '<table cellspacing="0">',
5403                 '<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>',
5404                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5405         var dn = this.dayNames;
5406         for(var i = 0; i < 7; i++){
5407             var d = this.startDay+i;
5408             if(d > 6){
5409                 d = d-7;
5410             }
5411             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5412         }
5413         m[m.length] = "</tr></thead><tbody><tr>";
5414         for(var i = 0; i < 42; i++) {
5415             if(i % 7 == 0 && i != 0){
5416                 m[m.length] = "</tr><tr>";
5417             }
5418             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5419         }
5420         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5421             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5422
5423         var el = document.createElement("div");
5424         el.className = "x-date-picker";
5425         el.innerHTML = m.join("");
5426
5427         container.dom.insertBefore(el, position);
5428
5429         this.el = Roo.get(el);
5430         this.eventEl = Roo.get(el.firstChild);
5431
5432         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5433             handler: this.showPrevMonth,
5434             scope: this,
5435             preventDefault:true,
5436             stopDefault:true
5437         });
5438
5439         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5440             handler: this.showNextMonth,
5441             scope: this,
5442             preventDefault:true,
5443             stopDefault:true
5444         });
5445
5446         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5447
5448         this.monthPicker = this.el.down('div.x-date-mp');
5449         this.monthPicker.enableDisplayMode('block');
5450         
5451         var kn = new Roo.KeyNav(this.eventEl, {
5452             "left" : function(e){
5453                 e.ctrlKey ?
5454                     this.showPrevMonth() :
5455                     this.update(this.activeDate.add("d", -1));
5456             },
5457
5458             "right" : function(e){
5459                 e.ctrlKey ?
5460                     this.showNextMonth() :
5461                     this.update(this.activeDate.add("d", 1));
5462             },
5463
5464             "up" : function(e){
5465                 e.ctrlKey ?
5466                     this.showNextYear() :
5467                     this.update(this.activeDate.add("d", -7));
5468             },
5469
5470             "down" : function(e){
5471                 e.ctrlKey ?
5472                     this.showPrevYear() :
5473                     this.update(this.activeDate.add("d", 7));
5474             },
5475
5476             "pageUp" : function(e){
5477                 this.showNextMonth();
5478             },
5479
5480             "pageDown" : function(e){
5481                 this.showPrevMonth();
5482             },
5483
5484             "enter" : function(e){
5485                 e.stopPropagation();
5486                 return true;
5487             },
5488
5489             scope : this
5490         });
5491
5492         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5493
5494         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5495
5496         this.el.unselectable();
5497         
5498         this.cells = this.el.select("table.x-date-inner tbody td");
5499         this.textNodes = this.el.query("table.x-date-inner tbody span");
5500
5501         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5502             text: "&#160;",
5503             tooltip: this.monthYearText
5504         });
5505
5506         this.mbtn.on('click', this.showMonthPicker, this);
5507         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5508
5509
5510         var today = (new Date()).dateFormat(this.format);
5511         
5512         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5513         if (this.showClear) {
5514             baseTb.add( new Roo.Toolbar.Fill());
5515         }
5516         baseTb.add({
5517             text: String.format(this.todayText, today),
5518             tooltip: String.format(this.todayTip, today),
5519             handler: this.selectToday,
5520             scope: this
5521         });
5522         
5523         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5524             
5525         //});
5526         if (this.showClear) {
5527             
5528             baseTb.add( new Roo.Toolbar.Fill());
5529             baseTb.add({
5530                 text: '&#160;',
5531                 cls: 'x-btn-icon x-btn-clear',
5532                 handler: function() {
5533                     //this.value = '';
5534                     this.fireEvent("select", this, '');
5535                 },
5536                 scope: this
5537             });
5538         }
5539         
5540         
5541         if(Roo.isIE){
5542             this.el.repaint();
5543         }
5544         this.update(this.value);
5545     },
5546
5547     createMonthPicker : function(){
5548         if(!this.monthPicker.dom.firstChild){
5549             var buf = ['<table border="0" cellspacing="0">'];
5550             for(var i = 0; i < 6; i++){
5551                 buf.push(
5552                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5553                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5554                     i == 0 ?
5555                     '<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>' :
5556                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5557                 );
5558             }
5559             buf.push(
5560                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5561                     this.okText,
5562                     '</button><button type="button" class="x-date-mp-cancel">',
5563                     this.cancelText,
5564                     '</button></td></tr>',
5565                 '</table>'
5566             );
5567             this.monthPicker.update(buf.join(''));
5568             this.monthPicker.on('click', this.onMonthClick, this);
5569             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5570
5571             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5572             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5573
5574             this.mpMonths.each(function(m, a, i){
5575                 i += 1;
5576                 if((i%2) == 0){
5577                     m.dom.xmonth = 5 + Math.round(i * .5);
5578                 }else{
5579                     m.dom.xmonth = Math.round((i-1) * .5);
5580                 }
5581             });
5582         }
5583     },
5584
5585     showMonthPicker : function(){
5586         this.createMonthPicker();
5587         var size = this.el.getSize();
5588         this.monthPicker.setSize(size);
5589         this.monthPicker.child('table').setSize(size);
5590
5591         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5592         this.updateMPMonth(this.mpSelMonth);
5593         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5594         this.updateMPYear(this.mpSelYear);
5595
5596         this.monthPicker.slideIn('t', {duration:.2});
5597     },
5598
5599     updateMPYear : function(y){
5600         this.mpyear = y;
5601         var ys = this.mpYears.elements;
5602         for(var i = 1; i <= 10; i++){
5603             var td = ys[i-1], y2;
5604             if((i%2) == 0){
5605                 y2 = y + Math.round(i * .5);
5606                 td.firstChild.innerHTML = y2;
5607                 td.xyear = y2;
5608             }else{
5609                 y2 = y - (5-Math.round(i * .5));
5610                 td.firstChild.innerHTML = y2;
5611                 td.xyear = y2;
5612             }
5613             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5614         }
5615     },
5616
5617     updateMPMonth : function(sm){
5618         this.mpMonths.each(function(m, a, i){
5619             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5620         });
5621     },
5622
5623     selectMPMonth: function(m){
5624         
5625     },
5626
5627     onMonthClick : function(e, t){
5628         e.stopEvent();
5629         var el = new Roo.Element(t), pn;
5630         if(el.is('button.x-date-mp-cancel')){
5631             this.hideMonthPicker();
5632         }
5633         else if(el.is('button.x-date-mp-ok')){
5634             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5635             this.hideMonthPicker();
5636         }
5637         else if(pn = el.up('td.x-date-mp-month', 2)){
5638             this.mpMonths.removeClass('x-date-mp-sel');
5639             pn.addClass('x-date-mp-sel');
5640             this.mpSelMonth = pn.dom.xmonth;
5641         }
5642         else if(pn = el.up('td.x-date-mp-year', 2)){
5643             this.mpYears.removeClass('x-date-mp-sel');
5644             pn.addClass('x-date-mp-sel');
5645             this.mpSelYear = pn.dom.xyear;
5646         }
5647         else if(el.is('a.x-date-mp-prev')){
5648             this.updateMPYear(this.mpyear-10);
5649         }
5650         else if(el.is('a.x-date-mp-next')){
5651             this.updateMPYear(this.mpyear+10);
5652         }
5653     },
5654
5655     onMonthDblClick : function(e, t){
5656         e.stopEvent();
5657         var el = new Roo.Element(t), pn;
5658         if(pn = el.up('td.x-date-mp-month', 2)){
5659             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5660             this.hideMonthPicker();
5661         }
5662         else if(pn = el.up('td.x-date-mp-year', 2)){
5663             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5664             this.hideMonthPicker();
5665         }
5666     },
5667
5668     hideMonthPicker : function(disableAnim){
5669         if(this.monthPicker){
5670             if(disableAnim === true){
5671                 this.monthPicker.hide();
5672             }else{
5673                 this.monthPicker.slideOut('t', {duration:.2});
5674             }
5675         }
5676     },
5677
5678     // private
5679     showPrevMonth : function(e){
5680         this.update(this.activeDate.add("mo", -1));
5681     },
5682
5683     // private
5684     showNextMonth : function(e){
5685         this.update(this.activeDate.add("mo", 1));
5686     },
5687
5688     // private
5689     showPrevYear : function(){
5690         this.update(this.activeDate.add("y", -1));
5691     },
5692
5693     // private
5694     showNextYear : function(){
5695         this.update(this.activeDate.add("y", 1));
5696     },
5697
5698     // private
5699     handleMouseWheel : function(e){
5700         var delta = e.getWheelDelta();
5701         if(delta > 0){
5702             this.showPrevMonth();
5703             e.stopEvent();
5704         } else if(delta < 0){
5705             this.showNextMonth();
5706             e.stopEvent();
5707         }
5708     },
5709
5710     // private
5711     handleDateClick : function(e, t){
5712         e.stopEvent();
5713         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5714             this.setValue(new Date(t.dateValue));
5715             this.fireEvent("select", this, this.value);
5716         }
5717     },
5718
5719     // private
5720     selectToday : function(){
5721         this.setValue(new Date().clearTime());
5722         this.fireEvent("select", this, this.value);
5723     },
5724
5725     // private
5726     update : function(date)
5727     {
5728         var vd = this.activeDate;
5729         this.activeDate = date;
5730         if(vd && this.el){
5731             var t = date.getTime();
5732             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5733                 this.cells.removeClass("x-date-selected");
5734                 this.cells.each(function(c){
5735                    if(c.dom.firstChild.dateValue == t){
5736                        c.addClass("x-date-selected");
5737                        setTimeout(function(){
5738                             try{c.dom.firstChild.focus();}catch(e){}
5739                        }, 50);
5740                        return false;
5741                    }
5742                 });
5743                 return;
5744             }
5745         }
5746         
5747         var days = date.getDaysInMonth();
5748         var firstOfMonth = date.getFirstDateOfMonth();
5749         var startingPos = firstOfMonth.getDay()-this.startDay;
5750
5751         if(startingPos <= this.startDay){
5752             startingPos += 7;
5753         }
5754
5755         var pm = date.add("mo", -1);
5756         var prevStart = pm.getDaysInMonth()-startingPos;
5757
5758         var cells = this.cells.elements;
5759         var textEls = this.textNodes;
5760         days += startingPos;
5761
5762         // convert everything to numbers so it's fast
5763         var day = 86400000;
5764         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5765         var today = new Date().clearTime().getTime();
5766         var sel = date.clearTime().getTime();
5767         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5768         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5769         var ddMatch = this.disabledDatesRE;
5770         var ddText = this.disabledDatesText;
5771         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5772         var ddaysText = this.disabledDaysText;
5773         var format = this.format;
5774
5775         var setCellClass = function(cal, cell){
5776             cell.title = "";
5777             var t = d.getTime();
5778             cell.firstChild.dateValue = t;
5779             if(t == today){
5780                 cell.className += " x-date-today";
5781                 cell.title = cal.todayText;
5782             }
5783             if(t == sel){
5784                 cell.className += " x-date-selected";
5785                 setTimeout(function(){
5786                     try{cell.firstChild.focus();}catch(e){}
5787                 }, 50);
5788             }
5789             // disabling
5790             if(t < min) {
5791                 cell.className = " x-date-disabled";
5792                 cell.title = cal.minText;
5793                 return;
5794             }
5795             if(t > max) {
5796                 cell.className = " x-date-disabled";
5797                 cell.title = cal.maxText;
5798                 return;
5799             }
5800             if(ddays){
5801                 if(ddays.indexOf(d.getDay()) != -1){
5802                     cell.title = ddaysText;
5803                     cell.className = " x-date-disabled";
5804                 }
5805             }
5806             if(ddMatch && format){
5807                 var fvalue = d.dateFormat(format);
5808                 if(ddMatch.test(fvalue)){
5809                     cell.title = ddText.replace("%0", fvalue);
5810                     cell.className = " x-date-disabled";
5811                 }
5812             }
5813         };
5814
5815         var i = 0;
5816         for(; i < startingPos; i++) {
5817             textEls[i].innerHTML = (++prevStart);
5818             d.setDate(d.getDate()+1);
5819             cells[i].className = "x-date-prevday";
5820             setCellClass(this, cells[i]);
5821         }
5822         for(; i < days; i++){
5823             intDay = i - startingPos + 1;
5824             textEls[i].innerHTML = (intDay);
5825             d.setDate(d.getDate()+1);
5826             cells[i].className = "x-date-active";
5827             setCellClass(this, cells[i]);
5828         }
5829         var extraDays = 0;
5830         for(; i < 42; i++) {
5831              textEls[i].innerHTML = (++extraDays);
5832              d.setDate(d.getDate()+1);
5833              cells[i].className = "x-date-nextday";
5834              setCellClass(this, cells[i]);
5835         }
5836
5837         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5838         this.fireEvent('monthchange', this, date);
5839         
5840         if(!this.internalRender){
5841             var main = this.el.dom.firstChild;
5842             var w = main.offsetWidth;
5843             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5844             Roo.fly(main).setWidth(w);
5845             this.internalRender = true;
5846             // opera does not respect the auto grow header center column
5847             // then, after it gets a width opera refuses to recalculate
5848             // without a second pass
5849             if(Roo.isOpera && !this.secondPass){
5850                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5851                 this.secondPass = true;
5852                 this.update.defer(10, this, [date]);
5853             }
5854         }
5855         
5856         
5857     }
5858 });        /*
5859  * Based on:
5860  * Ext JS Library 1.1.1
5861  * Copyright(c) 2006-2007, Ext JS, LLC.
5862  *
5863  * Originally Released Under LGPL - original licence link has changed is not relivant.
5864  *
5865  * Fork - LGPL
5866  * <script type="text/javascript">
5867  */
5868 /**
5869  * @class Roo.TabPanel
5870  * @extends Roo.util.Observable
5871  * A lightweight tab container.
5872  * <br><br>
5873  * Usage:
5874  * <pre><code>
5875 // basic tabs 1, built from existing content
5876 var tabs = new Roo.TabPanel("tabs1");
5877 tabs.addTab("script", "View Script");
5878 tabs.addTab("markup", "View Markup");
5879 tabs.activate("script");
5880
5881 // more advanced tabs, built from javascript
5882 var jtabs = new Roo.TabPanel("jtabs");
5883 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5884
5885 // set up the UpdateManager
5886 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5887 var updater = tab2.getUpdateManager();
5888 updater.setDefaultUrl("ajax1.htm");
5889 tab2.on('activate', updater.refresh, updater, true);
5890
5891 // Use setUrl for Ajax loading
5892 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5893 tab3.setUrl("ajax2.htm", null, true);
5894
5895 // Disabled tab
5896 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5897 tab4.disable();
5898
5899 jtabs.activate("jtabs-1");
5900  * </code></pre>
5901  * @constructor
5902  * Create a new TabPanel.
5903  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5904  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5905  */
5906 Roo.TabPanel = function(container, config){
5907     /**
5908     * The container element for this TabPanel.
5909     * @type Roo.Element
5910     */
5911     this.el = Roo.get(container, true);
5912     if(config){
5913         if(typeof config == "boolean"){
5914             this.tabPosition = config ? "bottom" : "top";
5915         }else{
5916             Roo.apply(this, config);
5917         }
5918     }
5919     if(this.tabPosition == "bottom"){
5920         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5921         this.el.addClass("x-tabs-bottom");
5922     }
5923     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5924     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5925     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5926     if(Roo.isIE){
5927         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5928     }
5929     if(this.tabPosition != "bottom"){
5930         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5931          * @type Roo.Element
5932          */
5933         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5934         this.el.addClass("x-tabs-top");
5935     }
5936     this.items = [];
5937
5938     this.bodyEl.setStyle("position", "relative");
5939
5940     this.active = null;
5941     this.activateDelegate = this.activate.createDelegate(this);
5942
5943     this.addEvents({
5944         /**
5945          * @event tabchange
5946          * Fires when the active tab changes
5947          * @param {Roo.TabPanel} this
5948          * @param {Roo.TabPanelItem} activePanel The new active tab
5949          */
5950         "tabchange": true,
5951         /**
5952          * @event beforetabchange
5953          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5954          * @param {Roo.TabPanel} this
5955          * @param {Object} e Set cancel to true on this object to cancel the tab change
5956          * @param {Roo.TabPanelItem} tab The tab being changed to
5957          */
5958         "beforetabchange" : true
5959     });
5960
5961     Roo.EventManager.onWindowResize(this.onResize, this);
5962     this.cpad = this.el.getPadding("lr");
5963     this.hiddenCount = 0;
5964
5965
5966     // toolbar on the tabbar support...
5967     if (this.toolbar) {
5968         var tcfg = this.toolbar;
5969         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5970         this.toolbar = new Roo.Toolbar(tcfg);
5971         if (Roo.isSafari) {
5972             var tbl = tcfg.container.child('table', true);
5973             tbl.setAttribute('width', '100%');
5974         }
5975         
5976     }
5977    
5978
5979
5980     Roo.TabPanel.superclass.constructor.call(this);
5981 };
5982
5983 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5984     /*
5985      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5986      */
5987     tabPosition : "top",
5988     /*
5989      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5990      */
5991     currentTabWidth : 0,
5992     /*
5993      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5994      */
5995     minTabWidth : 40,
5996     /*
5997      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5998      */
5999     maxTabWidth : 250,
6000     /*
6001      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
6002      */
6003     preferredTabWidth : 175,
6004     /*
6005      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6006      */
6007     resizeTabs : false,
6008     /*
6009      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6010      */
6011     monitorResize : true,
6012     /*
6013      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6014      */
6015     toolbar : false,
6016
6017     /**
6018      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6019      * @param {String} id The id of the div to use <b>or create</b>
6020      * @param {String} text The text for the tab
6021      * @param {String} content (optional) Content to put in the TabPanelItem body
6022      * @param {Boolean} closable (optional) True to create a close icon on the tab
6023      * @return {Roo.TabPanelItem} The created TabPanelItem
6024      */
6025     addTab : function(id, text, content, closable){
6026         var item = new Roo.TabPanelItem(this, id, text, closable);
6027         this.addTabItem(item);
6028         if(content){
6029             item.setContent(content);
6030         }
6031         return item;
6032     },
6033
6034     /**
6035      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6036      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6037      * @return {Roo.TabPanelItem}
6038      */
6039     getTab : function(id){
6040         return this.items[id];
6041     },
6042
6043     /**
6044      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6045      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6046      */
6047     hideTab : function(id){
6048         var t = this.items[id];
6049         if(!t.isHidden()){
6050            t.setHidden(true);
6051            this.hiddenCount++;
6052            this.autoSizeTabs();
6053         }
6054     },
6055
6056     /**
6057      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6058      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6059      */
6060     unhideTab : function(id){
6061         var t = this.items[id];
6062         if(t.isHidden()){
6063            t.setHidden(false);
6064            this.hiddenCount--;
6065            this.autoSizeTabs();
6066         }
6067     },
6068
6069     /**
6070      * Adds an existing {@link Roo.TabPanelItem}.
6071      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6072      */
6073     addTabItem : function(item){
6074         this.items[item.id] = item;
6075         this.items.push(item);
6076         if(this.resizeTabs){
6077            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6078            this.autoSizeTabs();
6079         }else{
6080             item.autoSize();
6081         }
6082     },
6083
6084     /**
6085      * Removes a {@link Roo.TabPanelItem}.
6086      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6087      */
6088     removeTab : function(id){
6089         var items = this.items;
6090         var tab = items[id];
6091         if(!tab) { return; }
6092         var index = items.indexOf(tab);
6093         if(this.active == tab && items.length > 1){
6094             var newTab = this.getNextAvailable(index);
6095             if(newTab) {
6096                 newTab.activate();
6097             }
6098         }
6099         this.stripEl.dom.removeChild(tab.pnode.dom);
6100         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6101             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6102         }
6103         items.splice(index, 1);
6104         delete this.items[tab.id];
6105         tab.fireEvent("close", tab);
6106         tab.purgeListeners();
6107         this.autoSizeTabs();
6108     },
6109
6110     getNextAvailable : function(start){
6111         var items = this.items;
6112         var index = start;
6113         // look for a next tab that will slide over to
6114         // replace the one being removed
6115         while(index < items.length){
6116             var item = items[++index];
6117             if(item && !item.isHidden()){
6118                 return item;
6119             }
6120         }
6121         // if one isn't found select the previous tab (on the left)
6122         index = start;
6123         while(index >= 0){
6124             var item = items[--index];
6125             if(item && !item.isHidden()){
6126                 return item;
6127             }
6128         }
6129         return null;
6130     },
6131
6132     /**
6133      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6134      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6135      */
6136     disableTab : function(id){
6137         var tab = this.items[id];
6138         if(tab && this.active != tab){
6139             tab.disable();
6140         }
6141     },
6142
6143     /**
6144      * Enables a {@link Roo.TabPanelItem} that is disabled.
6145      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6146      */
6147     enableTab : function(id){
6148         var tab = this.items[id];
6149         tab.enable();
6150     },
6151
6152     /**
6153      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6154      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6155      * @return {Roo.TabPanelItem} The TabPanelItem.
6156      */
6157     activate : function(id){
6158         var tab = this.items[id];
6159         if(!tab){
6160             return null;
6161         }
6162         if(tab == this.active || tab.disabled){
6163             return tab;
6164         }
6165         var e = {};
6166         this.fireEvent("beforetabchange", this, e, tab);
6167         if(e.cancel !== true && !tab.disabled){
6168             if(this.active){
6169                 this.active.hide();
6170             }
6171             this.active = this.items[id];
6172             this.active.show();
6173             this.fireEvent("tabchange", this, this.active);
6174         }
6175         return tab;
6176     },
6177
6178     /**
6179      * Gets the active {@link Roo.TabPanelItem}.
6180      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6181      */
6182     getActiveTab : function(){
6183         return this.active;
6184     },
6185
6186     /**
6187      * Updates the tab body element to fit the height of the container element
6188      * for overflow scrolling
6189      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6190      */
6191     syncHeight : function(targetHeight){
6192         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6193         var bm = this.bodyEl.getMargins();
6194         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6195         this.bodyEl.setHeight(newHeight);
6196         return newHeight;
6197     },
6198
6199     onResize : function(){
6200         if(this.monitorResize){
6201             this.autoSizeTabs();
6202         }
6203     },
6204
6205     /**
6206      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6207      */
6208     beginUpdate : function(){
6209         this.updating = true;
6210     },
6211
6212     /**
6213      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6214      */
6215     endUpdate : function(){
6216         this.updating = false;
6217         this.autoSizeTabs();
6218     },
6219
6220     /**
6221      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6222      */
6223     autoSizeTabs : function(){
6224         var count = this.items.length;
6225         var vcount = count - this.hiddenCount;
6226         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6227             return;
6228         }
6229         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6230         var availWidth = Math.floor(w / vcount);
6231         var b = this.stripBody;
6232         if(b.getWidth() > w){
6233             var tabs = this.items;
6234             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6235             if(availWidth < this.minTabWidth){
6236                 /*if(!this.sleft){    // incomplete scrolling code
6237                     this.createScrollButtons();
6238                 }
6239                 this.showScroll();
6240                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6241             }
6242         }else{
6243             if(this.currentTabWidth < this.preferredTabWidth){
6244                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6245             }
6246         }
6247     },
6248
6249     /**
6250      * Returns the number of tabs in this TabPanel.
6251      * @return {Number}
6252      */
6253      getCount : function(){
6254          return this.items.length;
6255      },
6256
6257     /**
6258      * Resizes all the tabs to the passed width
6259      * @param {Number} The new width
6260      */
6261     setTabWidth : function(width){
6262         this.currentTabWidth = width;
6263         for(var i = 0, len = this.items.length; i < len; i++) {
6264                 if(!this.items[i].isHidden()) {
6265                 this.items[i].setWidth(width);
6266             }
6267         }
6268     },
6269
6270     /**
6271      * Destroys this TabPanel
6272      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6273      */
6274     destroy : function(removeEl){
6275         Roo.EventManager.removeResizeListener(this.onResize, this);
6276         for(var i = 0, len = this.items.length; i < len; i++){
6277             this.items[i].purgeListeners();
6278         }
6279         if(removeEl === true){
6280             this.el.update("");
6281             this.el.remove();
6282         }
6283     }
6284 });
6285
6286 /**
6287  * @class Roo.TabPanelItem
6288  * @extends Roo.util.Observable
6289  * Represents an individual item (tab plus body) in a TabPanel.
6290  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6291  * @param {String} id The id of this TabPanelItem
6292  * @param {String} text The text for the tab of this TabPanelItem
6293  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6294  */
6295 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6296     /**
6297      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6298      * @type Roo.TabPanel
6299      */
6300     this.tabPanel = tabPanel;
6301     /**
6302      * The id for this TabPanelItem
6303      * @type String
6304      */
6305     this.id = id;
6306     /** @private */
6307     this.disabled = false;
6308     /** @private */
6309     this.text = text;
6310     /** @private */
6311     this.loaded = false;
6312     this.closable = closable;
6313
6314     /**
6315      * The body element for this TabPanelItem.
6316      * @type Roo.Element
6317      */
6318     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6319     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6320     this.bodyEl.setStyle("display", "block");
6321     this.bodyEl.setStyle("zoom", "1");
6322     this.hideAction();
6323
6324     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6325     /** @private */
6326     this.el = Roo.get(els.el, true);
6327     this.inner = Roo.get(els.inner, true);
6328     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6329     this.pnode = Roo.get(els.el.parentNode, true);
6330     this.el.on("mousedown", this.onTabMouseDown, this);
6331     this.el.on("click", this.onTabClick, this);
6332     /** @private */
6333     if(closable){
6334         var c = Roo.get(els.close, true);
6335         c.dom.title = this.closeText;
6336         c.addClassOnOver("close-over");
6337         c.on("click", this.closeClick, this);
6338      }
6339
6340     this.addEvents({
6341          /**
6342          * @event activate
6343          * Fires when this tab becomes the active tab.
6344          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6345          * @param {Roo.TabPanelItem} this
6346          */
6347         "activate": true,
6348         /**
6349          * @event beforeclose
6350          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6351          * @param {Roo.TabPanelItem} this
6352          * @param {Object} e Set cancel to true on this object to cancel the close.
6353          */
6354         "beforeclose": true,
6355         /**
6356          * @event close
6357          * Fires when this tab is closed.
6358          * @param {Roo.TabPanelItem} this
6359          */
6360          "close": true,
6361         /**
6362          * @event deactivate
6363          * Fires when this tab is no longer the active tab.
6364          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6365          * @param {Roo.TabPanelItem} this
6366          */
6367          "deactivate" : true
6368     });
6369     this.hidden = false;
6370
6371     Roo.TabPanelItem.superclass.constructor.call(this);
6372 };
6373
6374 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6375     purgeListeners : function(){
6376        Roo.util.Observable.prototype.purgeListeners.call(this);
6377        this.el.removeAllListeners();
6378     },
6379     /**
6380      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6381      */
6382     show : function(){
6383         this.pnode.addClass("on");
6384         this.showAction();
6385         if(Roo.isOpera){
6386             this.tabPanel.stripWrap.repaint();
6387         }
6388         this.fireEvent("activate", this.tabPanel, this);
6389     },
6390
6391     /**
6392      * Returns true if this tab is the active tab.
6393      * @return {Boolean}
6394      */
6395     isActive : function(){
6396         return this.tabPanel.getActiveTab() == this;
6397     },
6398
6399     /**
6400      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6401      */
6402     hide : function(){
6403         this.pnode.removeClass("on");
6404         this.hideAction();
6405         this.fireEvent("deactivate", this.tabPanel, this);
6406     },
6407
6408     hideAction : function(){
6409         this.bodyEl.hide();
6410         this.bodyEl.setStyle("position", "absolute");
6411         this.bodyEl.setLeft("-20000px");
6412         this.bodyEl.setTop("-20000px");
6413     },
6414
6415     showAction : function(){
6416         this.bodyEl.setStyle("position", "relative");
6417         this.bodyEl.setTop("");
6418         this.bodyEl.setLeft("");
6419         this.bodyEl.show();
6420     },
6421
6422     /**
6423      * Set the tooltip for the tab.
6424      * @param {String} tooltip The tab's tooltip
6425      */
6426     setTooltip : function(text){
6427         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6428             this.textEl.dom.qtip = text;
6429             this.textEl.dom.removeAttribute('title');
6430         }else{
6431             this.textEl.dom.title = text;
6432         }
6433     },
6434
6435     onTabClick : function(e){
6436         e.preventDefault();
6437         this.tabPanel.activate(this.id);
6438     },
6439
6440     onTabMouseDown : function(e){
6441         e.preventDefault();
6442         this.tabPanel.activate(this.id);
6443     },
6444
6445     getWidth : function(){
6446         return this.inner.getWidth();
6447     },
6448
6449     setWidth : function(width){
6450         var iwidth = width - this.pnode.getPadding("lr");
6451         this.inner.setWidth(iwidth);
6452         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6453         this.pnode.setWidth(width);
6454     },
6455
6456     /**
6457      * Show or hide the tab
6458      * @param {Boolean} hidden True to hide or false to show.
6459      */
6460     setHidden : function(hidden){
6461         this.hidden = hidden;
6462         this.pnode.setStyle("display", hidden ? "none" : "");
6463     },
6464
6465     /**
6466      * Returns true if this tab is "hidden"
6467      * @return {Boolean}
6468      */
6469     isHidden : function(){
6470         return this.hidden;
6471     },
6472
6473     /**
6474      * Returns the text for this tab
6475      * @return {String}
6476      */
6477     getText : function(){
6478         return this.text;
6479     },
6480
6481     autoSize : function(){
6482         //this.el.beginMeasure();
6483         this.textEl.setWidth(1);
6484         /*
6485          *  #2804 [new] Tabs in Roojs
6486          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6487          */
6488         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6489         //this.el.endMeasure();
6490     },
6491
6492     /**
6493      * Sets the text for the tab (Note: this also sets the tooltip text)
6494      * @param {String} text The tab's text and tooltip
6495      */
6496     setText : function(text){
6497         this.text = text;
6498         this.textEl.update(text);
6499         this.setTooltip(text);
6500         if(!this.tabPanel.resizeTabs){
6501             this.autoSize();
6502         }
6503     },
6504     /**
6505      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6506      */
6507     activate : function(){
6508         this.tabPanel.activate(this.id);
6509     },
6510
6511     /**
6512      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6513      */
6514     disable : function(){
6515         if(this.tabPanel.active != this){
6516             this.disabled = true;
6517             this.pnode.addClass("disabled");
6518         }
6519     },
6520
6521     /**
6522      * Enables this TabPanelItem if it was previously disabled.
6523      */
6524     enable : function(){
6525         this.disabled = false;
6526         this.pnode.removeClass("disabled");
6527     },
6528
6529     /**
6530      * Sets the content for this TabPanelItem.
6531      * @param {String} content The content
6532      * @param {Boolean} loadScripts true to look for and load scripts
6533      */
6534     setContent : function(content, loadScripts){
6535         this.bodyEl.update(content, loadScripts);
6536     },
6537
6538     /**
6539      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6540      * @return {Roo.UpdateManager} The UpdateManager
6541      */
6542     getUpdateManager : function(){
6543         return this.bodyEl.getUpdateManager();
6544     },
6545
6546     /**
6547      * Set a URL to be used to load the content for this TabPanelItem.
6548      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6549      * @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)
6550      * @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)
6551      * @return {Roo.UpdateManager} The UpdateManager
6552      */
6553     setUrl : function(url, params, loadOnce){
6554         if(this.refreshDelegate){
6555             this.un('activate', this.refreshDelegate);
6556         }
6557         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6558         this.on("activate", this.refreshDelegate);
6559         return this.bodyEl.getUpdateManager();
6560     },
6561
6562     /** @private */
6563     _handleRefresh : function(url, params, loadOnce){
6564         if(!loadOnce || !this.loaded){
6565             var updater = this.bodyEl.getUpdateManager();
6566             updater.update(url, params, this._setLoaded.createDelegate(this));
6567         }
6568     },
6569
6570     /**
6571      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6572      *   Will fail silently if the setUrl method has not been called.
6573      *   This does not activate the panel, just updates its content.
6574      */
6575     refresh : function(){
6576         if(this.refreshDelegate){
6577            this.loaded = false;
6578            this.refreshDelegate();
6579         }
6580     },
6581
6582     /** @private */
6583     _setLoaded : function(){
6584         this.loaded = true;
6585     },
6586
6587     /** @private */
6588     closeClick : function(e){
6589         var o = {};
6590         e.stopEvent();
6591         this.fireEvent("beforeclose", this, o);
6592         if(o.cancel !== true){
6593             this.tabPanel.removeTab(this.id);
6594         }
6595     },
6596     /**
6597      * The text displayed in the tooltip for the close icon.
6598      * @type String
6599      */
6600     closeText : "Close this tab"
6601 });
6602
6603 /** @private */
6604 Roo.TabPanel.prototype.createStrip = function(container){
6605     var strip = document.createElement("div");
6606     strip.className = "x-tabs-wrap";
6607     container.appendChild(strip);
6608     return strip;
6609 };
6610 /** @private */
6611 Roo.TabPanel.prototype.createStripList = function(strip){
6612     // div wrapper for retard IE
6613     // returns the "tr" element.
6614     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6615         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6616         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6617     return strip.firstChild.firstChild.firstChild.firstChild;
6618 };
6619 /** @private */
6620 Roo.TabPanel.prototype.createBody = function(container){
6621     var body = document.createElement("div");
6622     Roo.id(body, "tab-body");
6623     Roo.fly(body).addClass("x-tabs-body");
6624     container.appendChild(body);
6625     return body;
6626 };
6627 /** @private */
6628 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6629     var body = Roo.getDom(id);
6630     if(!body){
6631         body = document.createElement("div");
6632         body.id = id;
6633     }
6634     Roo.fly(body).addClass("x-tabs-item-body");
6635     bodyEl.insertBefore(body, bodyEl.firstChild);
6636     return body;
6637 };
6638 /** @private */
6639 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6640     var td = document.createElement("td");
6641     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6642     //stripEl.appendChild(td);
6643     if(closable){
6644         td.className = "x-tabs-closable";
6645         if(!this.closeTpl){
6646             this.closeTpl = new Roo.Template(
6647                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6648                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6649                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6650             );
6651         }
6652         var el = this.closeTpl.overwrite(td, {"text": text});
6653         var close = el.getElementsByTagName("div")[0];
6654         var inner = el.getElementsByTagName("em")[0];
6655         return {"el": el, "close": close, "inner": inner};
6656     } else {
6657         if(!this.tabTpl){
6658             this.tabTpl = new Roo.Template(
6659                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6660                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6661             );
6662         }
6663         var el = this.tabTpl.overwrite(td, {"text": text});
6664         var inner = el.getElementsByTagName("em")[0];
6665         return {"el": el, "inner": inner};
6666     }
6667 };/*
6668  * Based on:
6669  * Ext JS Library 1.1.1
6670  * Copyright(c) 2006-2007, Ext JS, LLC.
6671  *
6672  * Originally Released Under LGPL - original licence link has changed is not relivant.
6673  *
6674  * Fork - LGPL
6675  * <script type="text/javascript">
6676  */
6677
6678 /**
6679  * @class Roo.Button
6680  * @extends Roo.util.Observable
6681  * Simple Button class
6682  * @cfg {String} text The button text
6683  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6684  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6685  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6686  * @cfg {Object} scope The scope of the handler
6687  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6688  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6689  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6690  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6691  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6692  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6693    applies if enableToggle = true)
6694  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6695  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6696   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6697  * @constructor
6698  * Create a new button
6699  * @param {Object} config The config object
6700  */
6701 Roo.Button = function(renderTo, config)
6702 {
6703     if (!config) {
6704         config = renderTo;
6705         renderTo = config.renderTo || false;
6706     }
6707     
6708     Roo.apply(this, config);
6709     this.addEvents({
6710         /**
6711              * @event click
6712              * Fires when this button is clicked
6713              * @param {Button} this
6714              * @param {EventObject} e The click event
6715              */
6716             "click" : true,
6717         /**
6718              * @event toggle
6719              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6720              * @param {Button} this
6721              * @param {Boolean} pressed
6722              */
6723             "toggle" : true,
6724         /**
6725              * @event mouseover
6726              * Fires when the mouse hovers over the button
6727              * @param {Button} this
6728              * @param {Event} e The event object
6729              */
6730         'mouseover' : true,
6731         /**
6732              * @event mouseout
6733              * Fires when the mouse exits the button
6734              * @param {Button} this
6735              * @param {Event} e The event object
6736              */
6737         'mouseout': true,
6738          /**
6739              * @event render
6740              * Fires when the button is rendered
6741              * @param {Button} this
6742              */
6743         'render': true
6744     });
6745     if(this.menu){
6746         this.menu = Roo.menu.MenuMgr.get(this.menu);
6747     }
6748     // register listeners first!!  - so render can be captured..
6749     Roo.util.Observable.call(this);
6750     if(renderTo){
6751         this.render(renderTo);
6752     }
6753     
6754   
6755 };
6756
6757 Roo.extend(Roo.Button, Roo.util.Observable, {
6758     /**
6759      * 
6760      */
6761     
6762     /**
6763      * Read-only. True if this button is hidden
6764      * @type Boolean
6765      */
6766     hidden : false,
6767     /**
6768      * Read-only. True if this button is disabled
6769      * @type Boolean
6770      */
6771     disabled : false,
6772     /**
6773      * Read-only. True if this button is pressed (only if enableToggle = true)
6774      * @type Boolean
6775      */
6776     pressed : false,
6777
6778     /**
6779      * @cfg {Number} tabIndex 
6780      * The DOM tabIndex for this button (defaults to undefined)
6781      */
6782     tabIndex : undefined,
6783
6784     /**
6785      * @cfg {Boolean} enableToggle
6786      * True to enable pressed/not pressed toggling (defaults to false)
6787      */
6788     enableToggle: false,
6789     /**
6790      * @cfg {Mixed} menu
6791      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6792      */
6793     menu : undefined,
6794     /**
6795      * @cfg {String} menuAlign
6796      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6797      */
6798     menuAlign : "tl-bl?",
6799
6800     /**
6801      * @cfg {String} iconCls
6802      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6803      */
6804     iconCls : undefined,
6805     /**
6806      * @cfg {String} type
6807      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6808      */
6809     type : 'button',
6810
6811     // private
6812     menuClassTarget: 'tr',
6813
6814     /**
6815      * @cfg {String} clickEvent
6816      * The type of event to map to the button's event handler (defaults to 'click')
6817      */
6818     clickEvent : 'click',
6819
6820     /**
6821      * @cfg {Boolean} handleMouseEvents
6822      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6823      */
6824     handleMouseEvents : true,
6825
6826     /**
6827      * @cfg {String} tooltipType
6828      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6829      */
6830     tooltipType : 'qtip',
6831
6832     /**
6833      * @cfg {String} cls
6834      * A CSS class to apply to the button's main element.
6835      */
6836     
6837     /**
6838      * @cfg {Roo.Template} template (Optional)
6839      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6840      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6841      * require code modifications if required elements (e.g. a button) aren't present.
6842      */
6843
6844     // private
6845     render : function(renderTo){
6846         var btn;
6847         if(this.hideParent){
6848             this.parentEl = Roo.get(renderTo);
6849         }
6850         if(!this.dhconfig){
6851             if(!this.template){
6852                 if(!Roo.Button.buttonTemplate){
6853                     // hideous table template
6854                     Roo.Button.buttonTemplate = new Roo.Template(
6855                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6856                         '<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>',
6857                         "</tr></tbody></table>");
6858                 }
6859                 this.template = Roo.Button.buttonTemplate;
6860             }
6861             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6862             var btnEl = btn.child("button:first");
6863             btnEl.on('focus', this.onFocus, this);
6864             btnEl.on('blur', this.onBlur, this);
6865             if(this.cls){
6866                 btn.addClass(this.cls);
6867             }
6868             if(this.icon){
6869                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6870             }
6871             if(this.iconCls){
6872                 btnEl.addClass(this.iconCls);
6873                 if(!this.cls){
6874                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6875                 }
6876             }
6877             if(this.tabIndex !== undefined){
6878                 btnEl.dom.tabIndex = this.tabIndex;
6879             }
6880             if(this.tooltip){
6881                 if(typeof this.tooltip == 'object'){
6882                     Roo.QuickTips.tips(Roo.apply({
6883                           target: btnEl.id
6884                     }, this.tooltip));
6885                 } else {
6886                     btnEl.dom[this.tooltipType] = this.tooltip;
6887                 }
6888             }
6889         }else{
6890             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6891         }
6892         this.el = btn;
6893         if(this.id){
6894             this.el.dom.id = this.el.id = this.id;
6895         }
6896         if(this.menu){
6897             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6898             this.menu.on("show", this.onMenuShow, this);
6899             this.menu.on("hide", this.onMenuHide, this);
6900         }
6901         btn.addClass("x-btn");
6902         if(Roo.isIE && !Roo.isIE7){
6903             this.autoWidth.defer(1, this);
6904         }else{
6905             this.autoWidth();
6906         }
6907         if(this.handleMouseEvents){
6908             btn.on("mouseover", this.onMouseOver, this);
6909             btn.on("mouseout", this.onMouseOut, this);
6910             btn.on("mousedown", this.onMouseDown, this);
6911         }
6912         btn.on(this.clickEvent, this.onClick, this);
6913         //btn.on("mouseup", this.onMouseUp, this);
6914         if(this.hidden){
6915             this.hide();
6916         }
6917         if(this.disabled){
6918             this.disable();
6919         }
6920         Roo.ButtonToggleMgr.register(this);
6921         if(this.pressed){
6922             this.el.addClass("x-btn-pressed");
6923         }
6924         if(this.repeat){
6925             var repeater = new Roo.util.ClickRepeater(btn,
6926                 typeof this.repeat == "object" ? this.repeat : {}
6927             );
6928             repeater.on("click", this.onClick,  this);
6929         }
6930         
6931         this.fireEvent('render', this);
6932         
6933     },
6934     /**
6935      * Returns the button's underlying element
6936      * @return {Roo.Element} The element
6937      */
6938     getEl : function(){
6939         return this.el;  
6940     },
6941     
6942     /**
6943      * Destroys this Button and removes any listeners.
6944      */
6945     destroy : function(){
6946         Roo.ButtonToggleMgr.unregister(this);
6947         this.el.removeAllListeners();
6948         this.purgeListeners();
6949         this.el.remove();
6950     },
6951
6952     // private
6953     autoWidth : function(){
6954         if(this.el){
6955             this.el.setWidth("auto");
6956             if(Roo.isIE7 && Roo.isStrict){
6957                 var ib = this.el.child('button');
6958                 if(ib && ib.getWidth() > 20){
6959                     ib.clip();
6960                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6961                 }
6962             }
6963             if(this.minWidth){
6964                 if(this.hidden){
6965                     this.el.beginMeasure();
6966                 }
6967                 if(this.el.getWidth() < this.minWidth){
6968                     this.el.setWidth(this.minWidth);
6969                 }
6970                 if(this.hidden){
6971                     this.el.endMeasure();
6972                 }
6973             }
6974         }
6975     },
6976
6977     /**
6978      * Assigns this button's click handler
6979      * @param {Function} handler The function to call when the button is clicked
6980      * @param {Object} scope (optional) Scope for the function passed in
6981      */
6982     setHandler : function(handler, scope){
6983         this.handler = handler;
6984         this.scope = scope;  
6985     },
6986     
6987     /**
6988      * Sets this button's text
6989      * @param {String} text The button text
6990      */
6991     setText : function(text){
6992         this.text = text;
6993         if(this.el){
6994             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6995         }
6996         this.autoWidth();
6997     },
6998     
6999     /**
7000      * Gets the text for this button
7001      * @return {String} The button text
7002      */
7003     getText : function(){
7004         return this.text;  
7005     },
7006     
7007     /**
7008      * Show this button
7009      */
7010     show: function(){
7011         this.hidden = false;
7012         if(this.el){
7013             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7014         }
7015     },
7016     
7017     /**
7018      * Hide this button
7019      */
7020     hide: function(){
7021         this.hidden = true;
7022         if(this.el){
7023             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7024         }
7025     },
7026     
7027     /**
7028      * Convenience function for boolean show/hide
7029      * @param {Boolean} visible True to show, false to hide
7030      */
7031     setVisible: function(visible){
7032         if(visible) {
7033             this.show();
7034         }else{
7035             this.hide();
7036         }
7037     },
7038     
7039     /**
7040      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7041      * @param {Boolean} state (optional) Force a particular state
7042      */
7043     toggle : function(state){
7044         state = state === undefined ? !this.pressed : state;
7045         if(state != this.pressed){
7046             if(state){
7047                 this.el.addClass("x-btn-pressed");
7048                 this.pressed = true;
7049                 this.fireEvent("toggle", this, true);
7050             }else{
7051                 this.el.removeClass("x-btn-pressed");
7052                 this.pressed = false;
7053                 this.fireEvent("toggle", this, false);
7054             }
7055             if(this.toggleHandler){
7056                 this.toggleHandler.call(this.scope || this, this, state);
7057             }
7058         }
7059     },
7060     
7061     /**
7062      * Focus the button
7063      */
7064     focus : function(){
7065         this.el.child('button:first').focus();
7066     },
7067     
7068     /**
7069      * Disable this button
7070      */
7071     disable : function(){
7072         if(this.el){
7073             this.el.addClass("x-btn-disabled");
7074         }
7075         this.disabled = true;
7076     },
7077     
7078     /**
7079      * Enable this button
7080      */
7081     enable : function(){
7082         if(this.el){
7083             this.el.removeClass("x-btn-disabled");
7084         }
7085         this.disabled = false;
7086     },
7087
7088     /**
7089      * Convenience function for boolean enable/disable
7090      * @param {Boolean} enabled True to enable, false to disable
7091      */
7092     setDisabled : function(v){
7093         this[v !== true ? "enable" : "disable"]();
7094     },
7095
7096     // private
7097     onClick : function(e)
7098     {
7099         if(e){
7100             e.preventDefault();
7101         }
7102         if(e.button != 0){
7103             return;
7104         }
7105         if(!this.disabled){
7106             if(this.enableToggle){
7107                 this.toggle();
7108             }
7109             if(this.menu && !this.menu.isVisible()){
7110                 this.menu.show(this.el, this.menuAlign);
7111             }
7112             this.fireEvent("click", this, e);
7113             if(this.handler){
7114                 this.el.removeClass("x-btn-over");
7115                 this.handler.call(this.scope || this, this, e);
7116             }
7117         }
7118     },
7119     // private
7120     onMouseOver : function(e){
7121         if(!this.disabled){
7122             this.el.addClass("x-btn-over");
7123             this.fireEvent('mouseover', this, e);
7124         }
7125     },
7126     // private
7127     onMouseOut : function(e){
7128         if(!e.within(this.el,  true)){
7129             this.el.removeClass("x-btn-over");
7130             this.fireEvent('mouseout', this, e);
7131         }
7132     },
7133     // private
7134     onFocus : function(e){
7135         if(!this.disabled){
7136             this.el.addClass("x-btn-focus");
7137         }
7138     },
7139     // private
7140     onBlur : function(e){
7141         this.el.removeClass("x-btn-focus");
7142     },
7143     // private
7144     onMouseDown : function(e){
7145         if(!this.disabled && e.button == 0){
7146             this.el.addClass("x-btn-click");
7147             Roo.get(document).on('mouseup', this.onMouseUp, this);
7148         }
7149     },
7150     // private
7151     onMouseUp : function(e){
7152         if(e.button == 0){
7153             this.el.removeClass("x-btn-click");
7154             Roo.get(document).un('mouseup', this.onMouseUp, this);
7155         }
7156     },
7157     // private
7158     onMenuShow : function(e){
7159         this.el.addClass("x-btn-menu-active");
7160     },
7161     // private
7162     onMenuHide : function(e){
7163         this.el.removeClass("x-btn-menu-active");
7164     }   
7165 });
7166
7167 // Private utility class used by Button
7168 Roo.ButtonToggleMgr = function(){
7169    var groups = {};
7170    
7171    function toggleGroup(btn, state){
7172        if(state){
7173            var g = groups[btn.toggleGroup];
7174            for(var i = 0, l = g.length; i < l; i++){
7175                if(g[i] != btn){
7176                    g[i].toggle(false);
7177                }
7178            }
7179        }
7180    }
7181    
7182    return {
7183        register : function(btn){
7184            if(!btn.toggleGroup){
7185                return;
7186            }
7187            var g = groups[btn.toggleGroup];
7188            if(!g){
7189                g = groups[btn.toggleGroup] = [];
7190            }
7191            g.push(btn);
7192            btn.on("toggle", toggleGroup);
7193        },
7194        
7195        unregister : function(btn){
7196            if(!btn.toggleGroup){
7197                return;
7198            }
7199            var g = groups[btn.toggleGroup];
7200            if(g){
7201                g.remove(btn);
7202                btn.un("toggle", toggleGroup);
7203            }
7204        }
7205    };
7206 }();/*
7207  * Based on:
7208  * Ext JS Library 1.1.1
7209  * Copyright(c) 2006-2007, Ext JS, LLC.
7210  *
7211  * Originally Released Under LGPL - original licence link has changed is not relivant.
7212  *
7213  * Fork - LGPL
7214  * <script type="text/javascript">
7215  */
7216  
7217 /**
7218  * @class Roo.SplitButton
7219  * @extends Roo.Button
7220  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7221  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7222  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7223  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7224  * @cfg {String} arrowTooltip The title attribute of the arrow
7225  * @constructor
7226  * Create a new menu button
7227  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7228  * @param {Object} config The config object
7229  */
7230 Roo.SplitButton = function(renderTo, config){
7231     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7232     /**
7233      * @event arrowclick
7234      * Fires when this button's arrow is clicked
7235      * @param {SplitButton} this
7236      * @param {EventObject} e The click event
7237      */
7238     this.addEvents({"arrowclick":true});
7239 };
7240
7241 Roo.extend(Roo.SplitButton, Roo.Button, {
7242     render : function(renderTo){
7243         // this is one sweet looking template!
7244         var tpl = new Roo.Template(
7245             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7246             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7247             '<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>',
7248             "</tbody></table></td><td>",
7249             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7250             '<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>',
7251             "</tbody></table></td></tr></table>"
7252         );
7253         var btn = tpl.append(renderTo, [this.text, this.type], true);
7254         var btnEl = btn.child("button");
7255         if(this.cls){
7256             btn.addClass(this.cls);
7257         }
7258         if(this.icon){
7259             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7260         }
7261         if(this.iconCls){
7262             btnEl.addClass(this.iconCls);
7263             if(!this.cls){
7264                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7265             }
7266         }
7267         this.el = btn;
7268         if(this.handleMouseEvents){
7269             btn.on("mouseover", this.onMouseOver, this);
7270             btn.on("mouseout", this.onMouseOut, this);
7271             btn.on("mousedown", this.onMouseDown, this);
7272             btn.on("mouseup", this.onMouseUp, this);
7273         }
7274         btn.on(this.clickEvent, this.onClick, this);
7275         if(this.tooltip){
7276             if(typeof this.tooltip == 'object'){
7277                 Roo.QuickTips.tips(Roo.apply({
7278                       target: btnEl.id
7279                 }, this.tooltip));
7280             } else {
7281                 btnEl.dom[this.tooltipType] = this.tooltip;
7282             }
7283         }
7284         if(this.arrowTooltip){
7285             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7286         }
7287         if(this.hidden){
7288             this.hide();
7289         }
7290         if(this.disabled){
7291             this.disable();
7292         }
7293         if(this.pressed){
7294             this.el.addClass("x-btn-pressed");
7295         }
7296         if(Roo.isIE && !Roo.isIE7){
7297             this.autoWidth.defer(1, this);
7298         }else{
7299             this.autoWidth();
7300         }
7301         if(this.menu){
7302             this.menu.on("show", this.onMenuShow, this);
7303             this.menu.on("hide", this.onMenuHide, this);
7304         }
7305         this.fireEvent('render', this);
7306     },
7307
7308     // private
7309     autoWidth : function(){
7310         if(this.el){
7311             var tbl = this.el.child("table:first");
7312             var tbl2 = this.el.child("table:last");
7313             this.el.setWidth("auto");
7314             tbl.setWidth("auto");
7315             if(Roo.isIE7 && Roo.isStrict){
7316                 var ib = this.el.child('button:first');
7317                 if(ib && ib.getWidth() > 20){
7318                     ib.clip();
7319                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7320                 }
7321             }
7322             if(this.minWidth){
7323                 if(this.hidden){
7324                     this.el.beginMeasure();
7325                 }
7326                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7327                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7328                 }
7329                 if(this.hidden){
7330                     this.el.endMeasure();
7331                 }
7332             }
7333             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7334         } 
7335     },
7336     /**
7337      * Sets this button's click handler
7338      * @param {Function} handler The function to call when the button is clicked
7339      * @param {Object} scope (optional) Scope for the function passed above
7340      */
7341     setHandler : function(handler, scope){
7342         this.handler = handler;
7343         this.scope = scope;  
7344     },
7345     
7346     /**
7347      * Sets this button's arrow click handler
7348      * @param {Function} handler The function to call when the arrow is clicked
7349      * @param {Object} scope (optional) Scope for the function passed above
7350      */
7351     setArrowHandler : function(handler, scope){
7352         this.arrowHandler = handler;
7353         this.scope = scope;  
7354     },
7355     
7356     /**
7357      * Focus the button
7358      */
7359     focus : function(){
7360         if(this.el){
7361             this.el.child("button:first").focus();
7362         }
7363     },
7364
7365     // private
7366     onClick : function(e){
7367         e.preventDefault();
7368         if(!this.disabled){
7369             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7370                 if(this.menu && !this.menu.isVisible()){
7371                     this.menu.show(this.el, this.menuAlign);
7372                 }
7373                 this.fireEvent("arrowclick", this, e);
7374                 if(this.arrowHandler){
7375                     this.arrowHandler.call(this.scope || this, this, e);
7376                 }
7377             }else{
7378                 this.fireEvent("click", this, e);
7379                 if(this.handler){
7380                     this.handler.call(this.scope || this, this, e);
7381                 }
7382             }
7383         }
7384     },
7385     // private
7386     onMouseDown : function(e){
7387         if(!this.disabled){
7388             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7389         }
7390     },
7391     // private
7392     onMouseUp : function(e){
7393         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7394     }   
7395 });
7396
7397
7398 // backwards compat
7399 Roo.MenuButton = Roo.SplitButton;/*
7400  * Based on:
7401  * Ext JS Library 1.1.1
7402  * Copyright(c) 2006-2007, Ext JS, LLC.
7403  *
7404  * Originally Released Under LGPL - original licence link has changed is not relivant.
7405  *
7406  * Fork - LGPL
7407  * <script type="text/javascript">
7408  */
7409
7410 /**
7411  * @class Roo.Toolbar
7412  * Basic Toolbar class.
7413  * @constructor
7414  * Creates a new Toolbar
7415  * @param {Object} container The config object
7416  */ 
7417 Roo.Toolbar = function(container, buttons, config)
7418 {
7419     /// old consturctor format still supported..
7420     if(container instanceof Array){ // omit the container for later rendering
7421         buttons = container;
7422         config = buttons;
7423         container = null;
7424     }
7425     if (typeof(container) == 'object' && container.xtype) {
7426         config = container;
7427         container = config.container;
7428         buttons = config.buttons || []; // not really - use items!!
7429     }
7430     var xitems = [];
7431     if (config && config.items) {
7432         xitems = config.items;
7433         delete config.items;
7434     }
7435     Roo.apply(this, config);
7436     this.buttons = buttons;
7437     
7438     if(container){
7439         this.render(container);
7440     }
7441     this.xitems = xitems;
7442     Roo.each(xitems, function(b) {
7443         this.add(b);
7444     }, this);
7445     
7446 };
7447
7448 Roo.Toolbar.prototype = {
7449     /**
7450      * @cfg {Array} items
7451      * array of button configs or elements to add (will be converted to a MixedCollection)
7452      */
7453     
7454     /**
7455      * @cfg {String/HTMLElement/Element} container
7456      * The id or element that will contain the toolbar
7457      */
7458     // private
7459     render : function(ct){
7460         this.el = Roo.get(ct);
7461         if(this.cls){
7462             this.el.addClass(this.cls);
7463         }
7464         // using a table allows for vertical alignment
7465         // 100% width is needed by Safari...
7466         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7467         this.tr = this.el.child("tr", true);
7468         var autoId = 0;
7469         this.items = new Roo.util.MixedCollection(false, function(o){
7470             return o.id || ("item" + (++autoId));
7471         });
7472         if(this.buttons){
7473             this.add.apply(this, this.buttons);
7474             delete this.buttons;
7475         }
7476     },
7477
7478     /**
7479      * Adds element(s) to the toolbar -- this function takes a variable number of 
7480      * arguments of mixed type and adds them to the toolbar.
7481      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7482      * <ul>
7483      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7484      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7485      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7486      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7487      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7488      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7489      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7490      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7491      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7492      * </ul>
7493      * @param {Mixed} arg2
7494      * @param {Mixed} etc.
7495      */
7496     add : function(){
7497         var a = arguments, l = a.length;
7498         for(var i = 0; i < l; i++){
7499             this._add(a[i]);
7500         }
7501     },
7502     // private..
7503     _add : function(el) {
7504         
7505         if (el.xtype) {
7506             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7507         }
7508         
7509         if (el.applyTo){ // some kind of form field
7510             return this.addField(el);
7511         } 
7512         if (el.render){ // some kind of Toolbar.Item
7513             return this.addItem(el);
7514         }
7515         if (typeof el == "string"){ // string
7516             if(el == "separator" || el == "-"){
7517                 return this.addSeparator();
7518             }
7519             if (el == " "){
7520                 return this.addSpacer();
7521             }
7522             if(el == "->"){
7523                 return this.addFill();
7524             }
7525             return this.addText(el);
7526             
7527         }
7528         if(el.tagName){ // element
7529             return this.addElement(el);
7530         }
7531         if(typeof el == "object"){ // must be button config?
7532             return this.addButton(el);
7533         }
7534         // and now what?!?!
7535         return false;
7536         
7537     },
7538     
7539     /**
7540      * Add an Xtype element
7541      * @param {Object} xtype Xtype Object
7542      * @return {Object} created Object
7543      */
7544     addxtype : function(e){
7545         return this.add(e);  
7546     },
7547     
7548     /**
7549      * Returns the Element for this toolbar.
7550      * @return {Roo.Element}
7551      */
7552     getEl : function(){
7553         return this.el;  
7554     },
7555     
7556     /**
7557      * Adds a separator
7558      * @return {Roo.Toolbar.Item} The separator item
7559      */
7560     addSeparator : function(){
7561         return this.addItem(new Roo.Toolbar.Separator());
7562     },
7563
7564     /**
7565      * Adds a spacer element
7566      * @return {Roo.Toolbar.Spacer} The spacer item
7567      */
7568     addSpacer : function(){
7569         return this.addItem(new Roo.Toolbar.Spacer());
7570     },
7571
7572     /**
7573      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7574      * @return {Roo.Toolbar.Fill} The fill item
7575      */
7576     addFill : function(){
7577         return this.addItem(new Roo.Toolbar.Fill());
7578     },
7579
7580     /**
7581      * Adds any standard HTML element to the toolbar
7582      * @param {String/HTMLElement/Element} el The element or id of the element to add
7583      * @return {Roo.Toolbar.Item} The element's item
7584      */
7585     addElement : function(el){
7586         return this.addItem(new Roo.Toolbar.Item(el));
7587     },
7588     /**
7589      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7590      * @type Roo.util.MixedCollection  
7591      */
7592     items : false,
7593      
7594     /**
7595      * Adds any Toolbar.Item or subclass
7596      * @param {Roo.Toolbar.Item} item
7597      * @return {Roo.Toolbar.Item} The item
7598      */
7599     addItem : function(item){
7600         var td = this.nextBlock();
7601         item.render(td);
7602         this.items.add(item);
7603         return item;
7604     },
7605     
7606     /**
7607      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7608      * @param {Object/Array} config A button config or array of configs
7609      * @return {Roo.Toolbar.Button/Array}
7610      */
7611     addButton : function(config){
7612         if(config instanceof Array){
7613             var buttons = [];
7614             for(var i = 0, len = config.length; i < len; i++) {
7615                 buttons.push(this.addButton(config[i]));
7616             }
7617             return buttons;
7618         }
7619         var b = config;
7620         if(!(config instanceof Roo.Toolbar.Button)){
7621             b = config.split ?
7622                 new Roo.Toolbar.SplitButton(config) :
7623                 new Roo.Toolbar.Button(config);
7624         }
7625         var td = this.nextBlock();
7626         b.render(td);
7627         this.items.add(b);
7628         return b;
7629     },
7630     
7631     /**
7632      * Adds text to the toolbar
7633      * @param {String} text The text to add
7634      * @return {Roo.Toolbar.Item} The element's item
7635      */
7636     addText : function(text){
7637         return this.addItem(new Roo.Toolbar.TextItem(text));
7638     },
7639     
7640     /**
7641      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7642      * @param {Number} index The index where the item is to be inserted
7643      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7644      * @return {Roo.Toolbar.Button/Item}
7645      */
7646     insertButton : function(index, item){
7647         if(item instanceof Array){
7648             var buttons = [];
7649             for(var i = 0, len = item.length; i < len; i++) {
7650                buttons.push(this.insertButton(index + i, item[i]));
7651             }
7652             return buttons;
7653         }
7654         if (!(item instanceof Roo.Toolbar.Button)){
7655            item = new Roo.Toolbar.Button(item);
7656         }
7657         var td = document.createElement("td");
7658         this.tr.insertBefore(td, this.tr.childNodes[index]);
7659         item.render(td);
7660         this.items.insert(index, item);
7661         return item;
7662     },
7663     
7664     /**
7665      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7666      * @param {Object} config
7667      * @return {Roo.Toolbar.Item} The element's item
7668      */
7669     addDom : function(config, returnEl){
7670         var td = this.nextBlock();
7671         Roo.DomHelper.overwrite(td, config);
7672         var ti = new Roo.Toolbar.Item(td.firstChild);
7673         ti.render(td);
7674         this.items.add(ti);
7675         return ti;
7676     },
7677
7678     /**
7679      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7680      * @type Roo.util.MixedCollection  
7681      */
7682     fields : false,
7683     
7684     /**
7685      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7686      * Note: the field should not have been rendered yet. For a field that has already been
7687      * rendered, use {@link #addElement}.
7688      * @param {Roo.form.Field} field
7689      * @return {Roo.ToolbarItem}
7690      */
7691      
7692       
7693     addField : function(field) {
7694         if (!this.fields) {
7695             var autoId = 0;
7696             this.fields = new Roo.util.MixedCollection(false, function(o){
7697                 return o.id || ("item" + (++autoId));
7698             });
7699
7700         }
7701         
7702         var td = this.nextBlock();
7703         field.render(td);
7704         var ti = new Roo.Toolbar.Item(td.firstChild);
7705         ti.render(td);
7706         this.items.add(ti);
7707         this.fields.add(field);
7708         return ti;
7709     },
7710     /**
7711      * Hide the toolbar
7712      * @method hide
7713      */
7714      
7715       
7716     hide : function()
7717     {
7718         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7719         this.el.child('div').hide();
7720     },
7721     /**
7722      * Show the toolbar
7723      * @method show
7724      */
7725     show : function()
7726     {
7727         this.el.child('div').show();
7728     },
7729       
7730     // private
7731     nextBlock : function(){
7732         var td = document.createElement("td");
7733         this.tr.appendChild(td);
7734         return td;
7735     },
7736
7737     // private
7738     destroy : function(){
7739         if(this.items){ // rendered?
7740             Roo.destroy.apply(Roo, this.items.items);
7741         }
7742         if(this.fields){ // rendered?
7743             Roo.destroy.apply(Roo, this.fields.items);
7744         }
7745         Roo.Element.uncache(this.el, this.tr);
7746     }
7747 };
7748
7749 /**
7750  * @class Roo.Toolbar.Item
7751  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7752  * @constructor
7753  * Creates a new Item
7754  * @param {HTMLElement} el 
7755  */
7756 Roo.Toolbar.Item = function(el){
7757     var cfg = {};
7758     if (typeof (el.xtype) != 'undefined') {
7759         cfg = el;
7760         el = cfg.el;
7761     }
7762     
7763     this.el = Roo.getDom(el);
7764     this.id = Roo.id(this.el);
7765     this.hidden = false;
7766     
7767     this.addEvents({
7768          /**
7769              * @event render
7770              * Fires when the button is rendered
7771              * @param {Button} this
7772              */
7773         'render': true
7774     });
7775     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7776 };
7777 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7778 //Roo.Toolbar.Item.prototype = {
7779     
7780     /**
7781      * Get this item's HTML Element
7782      * @return {HTMLElement}
7783      */
7784     getEl : function(){
7785        return this.el;  
7786     },
7787
7788     // private
7789     render : function(td){
7790         
7791          this.td = td;
7792         td.appendChild(this.el);
7793         
7794         this.fireEvent('render', this);
7795     },
7796     
7797     /**
7798      * Removes and destroys this item.
7799      */
7800     destroy : function(){
7801         this.td.parentNode.removeChild(this.td);
7802     },
7803     
7804     /**
7805      * Shows this item.
7806      */
7807     show: function(){
7808         this.hidden = false;
7809         this.td.style.display = "";
7810     },
7811     
7812     /**
7813      * Hides this item.
7814      */
7815     hide: function(){
7816         this.hidden = true;
7817         this.td.style.display = "none";
7818     },
7819     
7820     /**
7821      * Convenience function for boolean show/hide.
7822      * @param {Boolean} visible true to show/false to hide
7823      */
7824     setVisible: function(visible){
7825         if(visible) {
7826             this.show();
7827         }else{
7828             this.hide();
7829         }
7830     },
7831     
7832     /**
7833      * Try to focus this item.
7834      */
7835     focus : function(){
7836         Roo.fly(this.el).focus();
7837     },
7838     
7839     /**
7840      * Disables this item.
7841      */
7842     disable : function(){
7843         Roo.fly(this.td).addClass("x-item-disabled");
7844         this.disabled = true;
7845         this.el.disabled = true;
7846     },
7847     
7848     /**
7849      * Enables this item.
7850      */
7851     enable : function(){
7852         Roo.fly(this.td).removeClass("x-item-disabled");
7853         this.disabled = false;
7854         this.el.disabled = false;
7855     }
7856 });
7857
7858
7859 /**
7860  * @class Roo.Toolbar.Separator
7861  * @extends Roo.Toolbar.Item
7862  * A simple toolbar separator class
7863  * @constructor
7864  * Creates a new Separator
7865  */
7866 Roo.Toolbar.Separator = function(cfg){
7867     
7868     var s = document.createElement("span");
7869     s.className = "ytb-sep";
7870     if (cfg) {
7871         cfg.el = s;
7872     }
7873     
7874     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7875 };
7876 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7877     enable:Roo.emptyFn,
7878     disable:Roo.emptyFn,
7879     focus:Roo.emptyFn
7880 });
7881
7882 /**
7883  * @class Roo.Toolbar.Spacer
7884  * @extends Roo.Toolbar.Item
7885  * A simple element that adds extra horizontal space to a toolbar.
7886  * @constructor
7887  * Creates a new Spacer
7888  */
7889 Roo.Toolbar.Spacer = function(cfg){
7890     var s = document.createElement("div");
7891     s.className = "ytb-spacer";
7892     if (cfg) {
7893         cfg.el = s;
7894     }
7895     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7896 };
7897 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7898     enable:Roo.emptyFn,
7899     disable:Roo.emptyFn,
7900     focus:Roo.emptyFn
7901 });
7902
7903 /**
7904  * @class Roo.Toolbar.Fill
7905  * @extends Roo.Toolbar.Spacer
7906  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7907  * @constructor
7908  * Creates a new Spacer
7909  */
7910 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7911     // private
7912     render : function(td){
7913         td.style.width = '100%';
7914         Roo.Toolbar.Fill.superclass.render.call(this, td);
7915     }
7916 });
7917
7918 /**
7919  * @class Roo.Toolbar.TextItem
7920  * @extends Roo.Toolbar.Item
7921  * A simple class that renders text directly into a toolbar.
7922  * @constructor
7923  * Creates a new TextItem
7924  * @param {String} text
7925  */
7926 Roo.Toolbar.TextItem = function(cfg){
7927     var  text = cfg || "";
7928     if (typeof(cfg) == 'object') {
7929         text = cfg.text || "";
7930     }  else {
7931         cfg = null;
7932     }
7933     var s = document.createElement("span");
7934     s.className = "ytb-text";
7935     s.innerHTML = text;
7936     if (cfg) {
7937         cfg.el  = s;
7938     }
7939     
7940     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7941 };
7942 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7943     
7944      
7945     enable:Roo.emptyFn,
7946     disable:Roo.emptyFn,
7947     focus:Roo.emptyFn
7948 });
7949
7950 /**
7951  * @class Roo.Toolbar.Button
7952  * @extends Roo.Button
7953  * A button that renders into a toolbar.
7954  * @constructor
7955  * Creates a new Button
7956  * @param {Object} config A standard {@link Roo.Button} config object
7957  */
7958 Roo.Toolbar.Button = function(config){
7959     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7960 };
7961 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7962     render : function(td){
7963         this.td = td;
7964         Roo.Toolbar.Button.superclass.render.call(this, td);
7965     },
7966     
7967     /**
7968      * Removes and destroys this button
7969      */
7970     destroy : function(){
7971         Roo.Toolbar.Button.superclass.destroy.call(this);
7972         this.td.parentNode.removeChild(this.td);
7973     },
7974     
7975     /**
7976      * Shows this button
7977      */
7978     show: function(){
7979         this.hidden = false;
7980         this.td.style.display = "";
7981     },
7982     
7983     /**
7984      * Hides this button
7985      */
7986     hide: function(){
7987         this.hidden = true;
7988         this.td.style.display = "none";
7989     },
7990
7991     /**
7992      * Disables this item
7993      */
7994     disable : function(){
7995         Roo.fly(this.td).addClass("x-item-disabled");
7996         this.disabled = true;
7997     },
7998
7999     /**
8000      * Enables this item
8001      */
8002     enable : function(){
8003         Roo.fly(this.td).removeClass("x-item-disabled");
8004         this.disabled = false;
8005     }
8006 });
8007 // backwards compat
8008 Roo.ToolbarButton = Roo.Toolbar.Button;
8009
8010 /**
8011  * @class Roo.Toolbar.SplitButton
8012  * @extends Roo.SplitButton
8013  * A menu button that renders into a toolbar.
8014  * @constructor
8015  * Creates a new SplitButton
8016  * @param {Object} config A standard {@link Roo.SplitButton} config object
8017  */
8018 Roo.Toolbar.SplitButton = function(config){
8019     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8020 };
8021 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8022     render : function(td){
8023         this.td = td;
8024         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8025     },
8026     
8027     /**
8028      * Removes and destroys this button
8029      */
8030     destroy : function(){
8031         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8032         this.td.parentNode.removeChild(this.td);
8033     },
8034     
8035     /**
8036      * Shows this button
8037      */
8038     show: function(){
8039         this.hidden = false;
8040         this.td.style.display = "";
8041     },
8042     
8043     /**
8044      * Hides this button
8045      */
8046     hide: function(){
8047         this.hidden = true;
8048         this.td.style.display = "none";
8049     }
8050 });
8051
8052 // backwards compat
8053 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8054  * Based on:
8055  * Ext JS Library 1.1.1
8056  * Copyright(c) 2006-2007, Ext JS, LLC.
8057  *
8058  * Originally Released Under LGPL - original licence link has changed is not relivant.
8059  *
8060  * Fork - LGPL
8061  * <script type="text/javascript">
8062  */
8063  
8064 /**
8065  * @class Roo.PagingToolbar
8066  * @extends Roo.Toolbar
8067  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8068  * @constructor
8069  * Create a new PagingToolbar
8070  * @param {Object} config The config object
8071  */
8072 Roo.PagingToolbar = function(el, ds, config)
8073 {
8074     // old args format still supported... - xtype is prefered..
8075     if (typeof(el) == 'object' && el.xtype) {
8076         // created from xtype...
8077         config = el;
8078         ds = el.dataSource;
8079         el = config.container;
8080     }
8081     var items = [];
8082     if (config.items) {
8083         items = config.items;
8084         config.items = [];
8085     }
8086     
8087     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8088     this.ds = ds;
8089     this.cursor = 0;
8090     this.renderButtons(this.el);
8091     this.bind(ds);
8092     
8093     // supprot items array.
8094    
8095     Roo.each(items, function(e) {
8096         this.add(Roo.factory(e));
8097     },this);
8098     
8099 };
8100
8101 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8102     /**
8103      * @cfg {Roo.data.Store} dataSource
8104      * The underlying data store providing the paged data
8105      */
8106     /**
8107      * @cfg {String/HTMLElement/Element} container
8108      * container The id or element that will contain the toolbar
8109      */
8110     /**
8111      * @cfg {Boolean} displayInfo
8112      * True to display the displayMsg (defaults to false)
8113      */
8114     /**
8115      * @cfg {Number} pageSize
8116      * The number of records to display per page (defaults to 20)
8117      */
8118     pageSize: 20,
8119     /**
8120      * @cfg {String} displayMsg
8121      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8122      */
8123     displayMsg : 'Displaying {0} - {1} of {2}',
8124     /**
8125      * @cfg {String} emptyMsg
8126      * The message to display when no records are found (defaults to "No data to display")
8127      */
8128     emptyMsg : 'No data to display',
8129     /**
8130      * Customizable piece of the default paging text (defaults to "Page")
8131      * @type String
8132      */
8133     beforePageText : "Page",
8134     /**
8135      * Customizable piece of the default paging text (defaults to "of %0")
8136      * @type String
8137      */
8138     afterPageText : "of {0}",
8139     /**
8140      * Customizable piece of the default paging text (defaults to "First Page")
8141      * @type String
8142      */
8143     firstText : "First Page",
8144     /**
8145      * Customizable piece of the default paging text (defaults to "Previous Page")
8146      * @type String
8147      */
8148     prevText : "Previous Page",
8149     /**
8150      * Customizable piece of the default paging text (defaults to "Next Page")
8151      * @type String
8152      */
8153     nextText : "Next Page",
8154     /**
8155      * Customizable piece of the default paging text (defaults to "Last Page")
8156      * @type String
8157      */
8158     lastText : "Last Page",
8159     /**
8160      * Customizable piece of the default paging text (defaults to "Refresh")
8161      * @type String
8162      */
8163     refreshText : "Refresh",
8164
8165     // private
8166     renderButtons : function(el){
8167         Roo.PagingToolbar.superclass.render.call(this, el);
8168         this.first = this.addButton({
8169             tooltip: this.firstText,
8170             cls: "x-btn-icon x-grid-page-first",
8171             disabled: true,
8172             handler: this.onClick.createDelegate(this, ["first"])
8173         });
8174         this.prev = this.addButton({
8175             tooltip: this.prevText,
8176             cls: "x-btn-icon x-grid-page-prev",
8177             disabled: true,
8178             handler: this.onClick.createDelegate(this, ["prev"])
8179         });
8180         //this.addSeparator();
8181         this.add(this.beforePageText);
8182         this.field = Roo.get(this.addDom({
8183            tag: "input",
8184            type: "text",
8185            size: "3",
8186            value: "1",
8187            cls: "x-grid-page-number"
8188         }).el);
8189         this.field.on("keydown", this.onPagingKeydown, this);
8190         this.field.on("focus", function(){this.dom.select();});
8191         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8192         this.field.setHeight(18);
8193         //this.addSeparator();
8194         this.next = this.addButton({
8195             tooltip: this.nextText,
8196             cls: "x-btn-icon x-grid-page-next",
8197             disabled: true,
8198             handler: this.onClick.createDelegate(this, ["next"])
8199         });
8200         this.last = this.addButton({
8201             tooltip: this.lastText,
8202             cls: "x-btn-icon x-grid-page-last",
8203             disabled: true,
8204             handler: this.onClick.createDelegate(this, ["last"])
8205         });
8206         //this.addSeparator();
8207         this.loading = this.addButton({
8208             tooltip: this.refreshText,
8209             cls: "x-btn-icon x-grid-loading",
8210             handler: this.onClick.createDelegate(this, ["refresh"])
8211         });
8212
8213         if(this.displayInfo){
8214             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8215         }
8216     },
8217
8218     // private
8219     updateInfo : function(){
8220         if(this.displayEl){
8221             var count = this.ds.getCount();
8222             var msg = count == 0 ?
8223                 this.emptyMsg :
8224                 String.format(
8225                     this.displayMsg,
8226                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8227                 );
8228             this.displayEl.update(msg);
8229         }
8230     },
8231
8232     // private
8233     onLoad : function(ds, r, o){
8234        this.cursor = o.params ? o.params.start : 0;
8235        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8236
8237        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8238        this.field.dom.value = ap;
8239        this.first.setDisabled(ap == 1);
8240        this.prev.setDisabled(ap == 1);
8241        this.next.setDisabled(ap == ps);
8242        this.last.setDisabled(ap == ps);
8243        this.loading.enable();
8244        this.updateInfo();
8245     },
8246
8247     // private
8248     getPageData : function(){
8249         var total = this.ds.getTotalCount();
8250         return {
8251             total : total,
8252             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8253             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8254         };
8255     },
8256
8257     // private
8258     onLoadError : function(){
8259         this.loading.enable();
8260     },
8261
8262     // private
8263     onPagingKeydown : function(e){
8264         var k = e.getKey();
8265         var d = this.getPageData();
8266         if(k == e.RETURN){
8267             var v = this.field.dom.value, pageNum;
8268             if(!v || isNaN(pageNum = parseInt(v, 10))){
8269                 this.field.dom.value = d.activePage;
8270                 return;
8271             }
8272             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8273             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8274             e.stopEvent();
8275         }
8276         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))
8277         {
8278           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8279           this.field.dom.value = pageNum;
8280           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8281           e.stopEvent();
8282         }
8283         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8284         {
8285           var v = this.field.dom.value, pageNum; 
8286           var increment = (e.shiftKey) ? 10 : 1;
8287           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8288             increment *= -1;
8289           }
8290           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8291             this.field.dom.value = d.activePage;
8292             return;
8293           }
8294           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8295           {
8296             this.field.dom.value = parseInt(v, 10) + increment;
8297             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8298             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8299           }
8300           e.stopEvent();
8301         }
8302     },
8303
8304     // private
8305     beforeLoad : function(){
8306         if(this.loading){
8307             this.loading.disable();
8308         }
8309     },
8310
8311     // private
8312     onClick : function(which){
8313         var ds = this.ds;
8314         switch(which){
8315             case "first":
8316                 ds.load({params:{start: 0, limit: this.pageSize}});
8317             break;
8318             case "prev":
8319                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8320             break;
8321             case "next":
8322                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8323             break;
8324             case "last":
8325                 var total = ds.getTotalCount();
8326                 var extra = total % this.pageSize;
8327                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8328                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8329             break;
8330             case "refresh":
8331                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8332             break;
8333         }
8334     },
8335
8336     /**
8337      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8338      * @param {Roo.data.Store} store The data store to unbind
8339      */
8340     unbind : function(ds){
8341         ds.un("beforeload", this.beforeLoad, this);
8342         ds.un("load", this.onLoad, this);
8343         ds.un("loadexception", this.onLoadError, this);
8344         ds.un("remove", this.updateInfo, this);
8345         ds.un("add", this.updateInfo, this);
8346         this.ds = undefined;
8347     },
8348
8349     /**
8350      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8351      * @param {Roo.data.Store} store The data store to bind
8352      */
8353     bind : function(ds){
8354         ds.on("beforeload", this.beforeLoad, this);
8355         ds.on("load", this.onLoad, this);
8356         ds.on("loadexception", this.onLoadError, this);
8357         ds.on("remove", this.updateInfo, this);
8358         ds.on("add", this.updateInfo, this);
8359         this.ds = ds;
8360     }
8361 });/*
8362  * Based on:
8363  * Ext JS Library 1.1.1
8364  * Copyright(c) 2006-2007, Ext JS, LLC.
8365  *
8366  * Originally Released Under LGPL - original licence link has changed is not relivant.
8367  *
8368  * Fork - LGPL
8369  * <script type="text/javascript">
8370  */
8371
8372 /**
8373  * @class Roo.Resizable
8374  * @extends Roo.util.Observable
8375  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8376  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8377  * 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
8378  * the element will be wrapped for you automatically.</p>
8379  * <p>Here is the list of valid resize handles:</p>
8380  * <pre>
8381 Value   Description
8382 ------  -------------------
8383  'n'     north
8384  's'     south
8385  'e'     east
8386  'w'     west
8387  'nw'    northwest
8388  'sw'    southwest
8389  'se'    southeast
8390  'ne'    northeast
8391  'hd'    horizontal drag
8392  'all'   all
8393 </pre>
8394  * <p>Here's an example showing the creation of a typical Resizable:</p>
8395  * <pre><code>
8396 var resizer = new Roo.Resizable("element-id", {
8397     handles: 'all',
8398     minWidth: 200,
8399     minHeight: 100,
8400     maxWidth: 500,
8401     maxHeight: 400,
8402     pinned: true
8403 });
8404 resizer.on("resize", myHandler);
8405 </code></pre>
8406  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8407  * resizer.east.setDisplayed(false);</p>
8408  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8409  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8410  * resize operation's new size (defaults to [0, 0])
8411  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8412  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8413  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8414  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8415  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8416  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8417  * @cfg {Number} width The width of the element in pixels (defaults to null)
8418  * @cfg {Number} height The height of the element in pixels (defaults to null)
8419  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8420  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8421  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8422  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8423  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8424  * in favor of the handles config option (defaults to false)
8425  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8426  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8427  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8428  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8429  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8430  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8431  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8432  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8433  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8434  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8435  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8436  * @constructor
8437  * Create a new resizable component
8438  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8439  * @param {Object} config configuration options
8440   */
8441 Roo.Resizable = function(el, config)
8442 {
8443     this.el = Roo.get(el);
8444
8445     if(config && config.wrap){
8446         config.resizeChild = this.el;
8447         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8448         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8449         this.el.setStyle("overflow", "hidden");
8450         this.el.setPositioning(config.resizeChild.getPositioning());
8451         config.resizeChild.clearPositioning();
8452         if(!config.width || !config.height){
8453             var csize = config.resizeChild.getSize();
8454             this.el.setSize(csize.width, csize.height);
8455         }
8456         if(config.pinned && !config.adjustments){
8457             config.adjustments = "auto";
8458         }
8459     }
8460
8461     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8462     this.proxy.unselectable();
8463     this.proxy.enableDisplayMode('block');
8464
8465     Roo.apply(this, config);
8466
8467     if(this.pinned){
8468         this.disableTrackOver = true;
8469         this.el.addClass("x-resizable-pinned");
8470     }
8471     // if the element isn't positioned, make it relative
8472     var position = this.el.getStyle("position");
8473     if(position != "absolute" && position != "fixed"){
8474         this.el.setStyle("position", "relative");
8475     }
8476     if(!this.handles){ // no handles passed, must be legacy style
8477         this.handles = 's,e,se';
8478         if(this.multiDirectional){
8479             this.handles += ',n,w';
8480         }
8481     }
8482     if(this.handles == "all"){
8483         this.handles = "n s e w ne nw se sw";
8484     }
8485     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8486     var ps = Roo.Resizable.positions;
8487     for(var i = 0, len = hs.length; i < len; i++){
8488         if(hs[i] && ps[hs[i]]){
8489             var pos = ps[hs[i]];
8490             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8491         }
8492     }
8493     // legacy
8494     this.corner = this.southeast;
8495     
8496     // updateBox = the box can move..
8497     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8498         this.updateBox = true;
8499     }
8500
8501     this.activeHandle = null;
8502
8503     if(this.resizeChild){
8504         if(typeof this.resizeChild == "boolean"){
8505             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8506         }else{
8507             this.resizeChild = Roo.get(this.resizeChild, true);
8508         }
8509     }
8510     
8511     if(this.adjustments == "auto"){
8512         var rc = this.resizeChild;
8513         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8514         if(rc && (hw || hn)){
8515             rc.position("relative");
8516             rc.setLeft(hw ? hw.el.getWidth() : 0);
8517             rc.setTop(hn ? hn.el.getHeight() : 0);
8518         }
8519         this.adjustments = [
8520             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8521             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8522         ];
8523     }
8524
8525     if(this.draggable){
8526         this.dd = this.dynamic ?
8527             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8528         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8529     }
8530
8531     // public events
8532     this.addEvents({
8533         /**
8534          * @event beforeresize
8535          * Fired before resize is allowed. Set enabled to false to cancel resize.
8536          * @param {Roo.Resizable} this
8537          * @param {Roo.EventObject} e The mousedown event
8538          */
8539         "beforeresize" : true,
8540         /**
8541          * @event resizing
8542          * Fired a resizing.
8543          * @param {Roo.Resizable} this
8544          * @param {Number} x The new x position
8545          * @param {Number} y The new y position
8546          * @param {Number} w The new w width
8547          * @param {Number} h The new h hight
8548          * @param {Roo.EventObject} e The mouseup event
8549          */
8550         "resizing" : true,
8551         /**
8552          * @event resize
8553          * Fired after a resize.
8554          * @param {Roo.Resizable} this
8555          * @param {Number} width The new width
8556          * @param {Number} height The new height
8557          * @param {Roo.EventObject} e The mouseup event
8558          */
8559         "resize" : true
8560     });
8561
8562     if(this.width !== null && this.height !== null){
8563         this.resizeTo(this.width, this.height);
8564     }else{
8565         this.updateChildSize();
8566     }
8567     if(Roo.isIE){
8568         this.el.dom.style.zoom = 1;
8569     }
8570     Roo.Resizable.superclass.constructor.call(this);
8571 };
8572
8573 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8574         resizeChild : false,
8575         adjustments : [0, 0],
8576         minWidth : 5,
8577         minHeight : 5,
8578         maxWidth : 10000,
8579         maxHeight : 10000,
8580         enabled : true,
8581         animate : false,
8582         duration : .35,
8583         dynamic : false,
8584         handles : false,
8585         multiDirectional : false,
8586         disableTrackOver : false,
8587         easing : 'easeOutStrong',
8588         widthIncrement : 0,
8589         heightIncrement : 0,
8590         pinned : false,
8591         width : null,
8592         height : null,
8593         preserveRatio : false,
8594         transparent: false,
8595         minX: 0,
8596         minY: 0,
8597         draggable: false,
8598
8599         /**
8600          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8601          */
8602         constrainTo: undefined,
8603         /**
8604          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8605          */
8606         resizeRegion: undefined,
8607
8608
8609     /**
8610      * Perform a manual resize
8611      * @param {Number} width
8612      * @param {Number} height
8613      */
8614     resizeTo : function(width, height){
8615         this.el.setSize(width, height);
8616         this.updateChildSize();
8617         this.fireEvent("resize", this, width, height, null);
8618     },
8619
8620     // private
8621     startSizing : function(e, handle){
8622         this.fireEvent("beforeresize", this, e);
8623         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8624
8625             if(!this.overlay){
8626                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8627                 this.overlay.unselectable();
8628                 this.overlay.enableDisplayMode("block");
8629                 this.overlay.on("mousemove", this.onMouseMove, this);
8630                 this.overlay.on("mouseup", this.onMouseUp, this);
8631             }
8632             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8633
8634             this.resizing = true;
8635             this.startBox = this.el.getBox();
8636             this.startPoint = e.getXY();
8637             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8638                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8639
8640             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8641             this.overlay.show();
8642
8643             if(this.constrainTo) {
8644                 var ct = Roo.get(this.constrainTo);
8645                 this.resizeRegion = ct.getRegion().adjust(
8646                     ct.getFrameWidth('t'),
8647                     ct.getFrameWidth('l'),
8648                     -ct.getFrameWidth('b'),
8649                     -ct.getFrameWidth('r')
8650                 );
8651             }
8652
8653             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8654             this.proxy.show();
8655             this.proxy.setBox(this.startBox);
8656             if(!this.dynamic){
8657                 this.proxy.setStyle('visibility', 'visible');
8658             }
8659         }
8660     },
8661
8662     // private
8663     onMouseDown : function(handle, e){
8664         if(this.enabled){
8665             e.stopEvent();
8666             this.activeHandle = handle;
8667             this.startSizing(e, handle);
8668         }
8669     },
8670
8671     // private
8672     onMouseUp : function(e){
8673         var size = this.resizeElement();
8674         this.resizing = false;
8675         this.handleOut();
8676         this.overlay.hide();
8677         this.proxy.hide();
8678         this.fireEvent("resize", this, size.width, size.height, e);
8679     },
8680
8681     // private
8682     updateChildSize : function(){
8683         
8684         if(this.resizeChild){
8685             var el = this.el;
8686             var child = this.resizeChild;
8687             var adj = this.adjustments;
8688             if(el.dom.offsetWidth){
8689                 var b = el.getSize(true);
8690                 child.setSize(b.width+adj[0], b.height+adj[1]);
8691             }
8692             // Second call here for IE
8693             // The first call enables instant resizing and
8694             // the second call corrects scroll bars if they
8695             // exist
8696             if(Roo.isIE){
8697                 setTimeout(function(){
8698                     if(el.dom.offsetWidth){
8699                         var b = el.getSize(true);
8700                         child.setSize(b.width+adj[0], b.height+adj[1]);
8701                     }
8702                 }, 10);
8703             }
8704         }
8705     },
8706
8707     // private
8708     snap : function(value, inc, min){
8709         if(!inc || !value) {
8710             return value;
8711         }
8712         var newValue = value;
8713         var m = value % inc;
8714         if(m > 0){
8715             if(m > (inc/2)){
8716                 newValue = value + (inc-m);
8717             }else{
8718                 newValue = value - m;
8719             }
8720         }
8721         return Math.max(min, newValue);
8722     },
8723
8724     // private
8725     resizeElement : function(){
8726         var box = this.proxy.getBox();
8727         if(this.updateBox){
8728             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8729         }else{
8730             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8731         }
8732         this.updateChildSize();
8733         if(!this.dynamic){
8734             this.proxy.hide();
8735         }
8736         return box;
8737     },
8738
8739     // private
8740     constrain : function(v, diff, m, mx){
8741         if(v - diff < m){
8742             diff = v - m;
8743         }else if(v - diff > mx){
8744             diff = mx - v;
8745         }
8746         return diff;
8747     },
8748
8749     // private
8750     onMouseMove : function(e){
8751         
8752         if(this.enabled){
8753             try{// try catch so if something goes wrong the user doesn't get hung
8754
8755             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8756                 return;
8757             }
8758
8759             //var curXY = this.startPoint;
8760             var curSize = this.curSize || this.startBox;
8761             var x = this.startBox.x, y = this.startBox.y;
8762             var ox = x, oy = y;
8763             var w = curSize.width, h = curSize.height;
8764             var ow = w, oh = h;
8765             var mw = this.minWidth, mh = this.minHeight;
8766             var mxw = this.maxWidth, mxh = this.maxHeight;
8767             var wi = this.widthIncrement;
8768             var hi = this.heightIncrement;
8769
8770             var eventXY = e.getXY();
8771             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8772             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8773
8774             var pos = this.activeHandle.position;
8775
8776             switch(pos){
8777                 case "east":
8778                     w += diffX;
8779                     w = Math.min(Math.max(mw, w), mxw);
8780                     break;
8781              
8782                 case "south":
8783                     h += diffY;
8784                     h = Math.min(Math.max(mh, h), mxh);
8785                     break;
8786                 case "southeast":
8787                     w += diffX;
8788                     h += diffY;
8789                     w = Math.min(Math.max(mw, w), mxw);
8790                     h = Math.min(Math.max(mh, h), mxh);
8791                     break;
8792                 case "north":
8793                     diffY = this.constrain(h, diffY, mh, mxh);
8794                     y += diffY;
8795                     h -= diffY;
8796                     break;
8797                 case "hdrag":
8798                     
8799                     if (wi) {
8800                         var adiffX = Math.abs(diffX);
8801                         var sub = (adiffX % wi); // how much 
8802                         if (sub > (wi/2)) { // far enough to snap
8803                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8804                         } else {
8805                             // remove difference.. 
8806                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8807                         }
8808                     }
8809                     x += diffX;
8810                     x = Math.max(this.minX, x);
8811                     break;
8812                 case "west":
8813                     diffX = this.constrain(w, diffX, mw, mxw);
8814                     x += diffX;
8815                     w -= diffX;
8816                     break;
8817                 case "northeast":
8818                     w += diffX;
8819                     w = Math.min(Math.max(mw, w), mxw);
8820                     diffY = this.constrain(h, diffY, mh, mxh);
8821                     y += diffY;
8822                     h -= diffY;
8823                     break;
8824                 case "northwest":
8825                     diffX = this.constrain(w, diffX, mw, mxw);
8826                     diffY = this.constrain(h, diffY, mh, mxh);
8827                     y += diffY;
8828                     h -= diffY;
8829                     x += diffX;
8830                     w -= diffX;
8831                     break;
8832                case "southwest":
8833                     diffX = this.constrain(w, diffX, mw, mxw);
8834                     h += diffY;
8835                     h = Math.min(Math.max(mh, h), mxh);
8836                     x += diffX;
8837                     w -= diffX;
8838                     break;
8839             }
8840
8841             var sw = this.snap(w, wi, mw);
8842             var sh = this.snap(h, hi, mh);
8843             if(sw != w || sh != h){
8844                 switch(pos){
8845                     case "northeast":
8846                         y -= sh - h;
8847                     break;
8848                     case "north":
8849                         y -= sh - h;
8850                         break;
8851                     case "southwest":
8852                         x -= sw - w;
8853                     break;
8854                     case "west":
8855                         x -= sw - w;
8856                         break;
8857                     case "northwest":
8858                         x -= sw - w;
8859                         y -= sh - h;
8860                     break;
8861                 }
8862                 w = sw;
8863                 h = sh;
8864             }
8865
8866             if(this.preserveRatio){
8867                 switch(pos){
8868                     case "southeast":
8869                     case "east":
8870                         h = oh * (w/ow);
8871                         h = Math.min(Math.max(mh, h), mxh);
8872                         w = ow * (h/oh);
8873                        break;
8874                     case "south":
8875                         w = ow * (h/oh);
8876                         w = Math.min(Math.max(mw, w), mxw);
8877                         h = oh * (w/ow);
8878                         break;
8879                     case "northeast":
8880                         w = ow * (h/oh);
8881                         w = Math.min(Math.max(mw, w), mxw);
8882                         h = oh * (w/ow);
8883                     break;
8884                     case "north":
8885                         var tw = w;
8886                         w = ow * (h/oh);
8887                         w = Math.min(Math.max(mw, w), mxw);
8888                         h = oh * (w/ow);
8889                         x += (tw - w) / 2;
8890                         break;
8891                     case "southwest":
8892                         h = oh * (w/ow);
8893                         h = Math.min(Math.max(mh, h), mxh);
8894                         var tw = w;
8895                         w = ow * (h/oh);
8896                         x += tw - w;
8897                         break;
8898                     case "west":
8899                         var th = h;
8900                         h = oh * (w/ow);
8901                         h = Math.min(Math.max(mh, h), mxh);
8902                         y += (th - h) / 2;
8903                         var tw = w;
8904                         w = ow * (h/oh);
8905                         x += tw - w;
8906                        break;
8907                     case "northwest":
8908                         var tw = w;
8909                         var th = h;
8910                         h = oh * (w/ow);
8911                         h = Math.min(Math.max(mh, h), mxh);
8912                         w = ow * (h/oh);
8913                         y += th - h;
8914                         x += tw - w;
8915                        break;
8916
8917                 }
8918             }
8919             if (pos == 'hdrag') {
8920                 w = ow;
8921             }
8922             this.proxy.setBounds(x, y, w, h);
8923             if(this.dynamic){
8924                 this.resizeElement();
8925             }
8926             }catch(e){}
8927         }
8928         this.fireEvent("resizing", this, x, y, w, h, e);
8929     },
8930
8931     // private
8932     handleOver : function(){
8933         if(this.enabled){
8934             this.el.addClass("x-resizable-over");
8935         }
8936     },
8937
8938     // private
8939     handleOut : function(){
8940         if(!this.resizing){
8941             this.el.removeClass("x-resizable-over");
8942         }
8943     },
8944
8945     /**
8946      * Returns the element this component is bound to.
8947      * @return {Roo.Element}
8948      */
8949     getEl : function(){
8950         return this.el;
8951     },
8952
8953     /**
8954      * Returns the resizeChild element (or null).
8955      * @return {Roo.Element}
8956      */
8957     getResizeChild : function(){
8958         return this.resizeChild;
8959     },
8960     groupHandler : function()
8961     {
8962         
8963     },
8964     /**
8965      * Destroys this resizable. If the element was wrapped and
8966      * removeEl is not true then the element remains.
8967      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8968      */
8969     destroy : function(removeEl){
8970         this.proxy.remove();
8971         if(this.overlay){
8972             this.overlay.removeAllListeners();
8973             this.overlay.remove();
8974         }
8975         var ps = Roo.Resizable.positions;
8976         for(var k in ps){
8977             if(typeof ps[k] != "function" && this[ps[k]]){
8978                 var h = this[ps[k]];
8979                 h.el.removeAllListeners();
8980                 h.el.remove();
8981             }
8982         }
8983         if(removeEl){
8984             this.el.update("");
8985             this.el.remove();
8986         }
8987     }
8988 });
8989
8990 // private
8991 // hash to map config positions to true positions
8992 Roo.Resizable.positions = {
8993     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8994     hd: "hdrag"
8995 };
8996
8997 // private
8998 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8999     if(!this.tpl){
9000         // only initialize the template if resizable is used
9001         var tpl = Roo.DomHelper.createTemplate(
9002             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
9003         );
9004         tpl.compile();
9005         Roo.Resizable.Handle.prototype.tpl = tpl;
9006     }
9007     this.position = pos;
9008     this.rz = rz;
9009     // show north drag fro topdra
9010     var handlepos = pos == 'hdrag' ? 'north' : pos;
9011     
9012     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9013     if (pos == 'hdrag') {
9014         this.el.setStyle('cursor', 'pointer');
9015     }
9016     this.el.unselectable();
9017     if(transparent){
9018         this.el.setOpacity(0);
9019     }
9020     this.el.on("mousedown", this.onMouseDown, this);
9021     if(!disableTrackOver){
9022         this.el.on("mouseover", this.onMouseOver, this);
9023         this.el.on("mouseout", this.onMouseOut, this);
9024     }
9025 };
9026
9027 // private
9028 Roo.Resizable.Handle.prototype = {
9029     afterResize : function(rz){
9030         Roo.log('after?');
9031         // do nothing
9032     },
9033     // private
9034     onMouseDown : function(e){
9035         this.rz.onMouseDown(this, e);
9036     },
9037     // private
9038     onMouseOver : function(e){
9039         this.rz.handleOver(this, e);
9040     },
9041     // private
9042     onMouseOut : function(e){
9043         this.rz.handleOut(this, e);
9044     }
9045 };/*
9046  * Based on:
9047  * Ext JS Library 1.1.1
9048  * Copyright(c) 2006-2007, Ext JS, LLC.
9049  *
9050  * Originally Released Under LGPL - original licence link has changed is not relivant.
9051  *
9052  * Fork - LGPL
9053  * <script type="text/javascript">
9054  */
9055
9056 /**
9057  * @class Roo.Editor
9058  * @extends Roo.Component
9059  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9060  * @constructor
9061  * Create a new Editor
9062  * @param {Roo.form.Field} field The Field object (or descendant)
9063  * @param {Object} config The config object
9064  */
9065 Roo.Editor = function(field, config){
9066     Roo.Editor.superclass.constructor.call(this, config);
9067     this.field = field;
9068     this.addEvents({
9069         /**
9070              * @event beforestartedit
9071              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9072              * false from the handler of this event.
9073              * @param {Editor} this
9074              * @param {Roo.Element} boundEl The underlying element bound to this editor
9075              * @param {Mixed} value The field value being set
9076              */
9077         "beforestartedit" : true,
9078         /**
9079              * @event startedit
9080              * Fires when this editor is displayed
9081              * @param {Roo.Element} boundEl The underlying element bound to this editor
9082              * @param {Mixed} value The starting field value
9083              */
9084         "startedit" : true,
9085         /**
9086              * @event beforecomplete
9087              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9088              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9089              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9090              * event will not fire since no edit actually occurred.
9091              * @param {Editor} this
9092              * @param {Mixed} value The current field value
9093              * @param {Mixed} startValue The original field value
9094              */
9095         "beforecomplete" : true,
9096         /**
9097              * @event complete
9098              * Fires after editing is complete and any changed value has been written to the underlying field.
9099              * @param {Editor} this
9100              * @param {Mixed} value The current field value
9101              * @param {Mixed} startValue The original field value
9102              */
9103         "complete" : true,
9104         /**
9105          * @event specialkey
9106          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9107          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9108          * @param {Roo.form.Field} this
9109          * @param {Roo.EventObject} e The event object
9110          */
9111         "specialkey" : true
9112     });
9113 };
9114
9115 Roo.extend(Roo.Editor, Roo.Component, {
9116     /**
9117      * @cfg {Boolean/String} autosize
9118      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9119      * or "height" to adopt the height only (defaults to false)
9120      */
9121     /**
9122      * @cfg {Boolean} revertInvalid
9123      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9124      * validation fails (defaults to true)
9125      */
9126     /**
9127      * @cfg {Boolean} ignoreNoChange
9128      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9129      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9130      * will never be ignored.
9131      */
9132     /**
9133      * @cfg {Boolean} hideEl
9134      * False to keep the bound element visible while the editor is displayed (defaults to true)
9135      */
9136     /**
9137      * @cfg {Mixed} value
9138      * The data value of the underlying field (defaults to "")
9139      */
9140     value : "",
9141     /**
9142      * @cfg {String} alignment
9143      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9144      */
9145     alignment: "c-c?",
9146     /**
9147      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9148      * for bottom-right shadow (defaults to "frame")
9149      */
9150     shadow : "frame",
9151     /**
9152      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9153      */
9154     constrain : false,
9155     /**
9156      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9157      */
9158     completeOnEnter : false,
9159     /**
9160      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9161      */
9162     cancelOnEsc : false,
9163     /**
9164      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9165      */
9166     updateEl : false,
9167
9168     // private
9169     onRender : function(ct, position){
9170         this.el = new Roo.Layer({
9171             shadow: this.shadow,
9172             cls: "x-editor",
9173             parentEl : ct,
9174             shim : this.shim,
9175             shadowOffset:4,
9176             id: this.id,
9177             constrain: this.constrain
9178         });
9179         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9180         if(this.field.msgTarget != 'title'){
9181             this.field.msgTarget = 'qtip';
9182         }
9183         this.field.render(this.el);
9184         if(Roo.isGecko){
9185             this.field.el.dom.setAttribute('autocomplete', 'off');
9186         }
9187         this.field.on("specialkey", this.onSpecialKey, this);
9188         if(this.swallowKeys){
9189             this.field.el.swallowEvent(['keydown','keypress']);
9190         }
9191         this.field.show();
9192         this.field.on("blur", this.onBlur, this);
9193         if(this.field.grow){
9194             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9195         }
9196     },
9197
9198     onSpecialKey : function(field, e)
9199     {
9200         //Roo.log('editor onSpecialKey');
9201         if(this.completeOnEnter && e.getKey() == e.ENTER){
9202             e.stopEvent();
9203             this.completeEdit();
9204             return;
9205         }
9206         // do not fire special key otherwise it might hide close the editor...
9207         if(e.getKey() == e.ENTER){    
9208             return;
9209         }
9210         if(this.cancelOnEsc && e.getKey() == e.ESC){
9211             this.cancelEdit();
9212             return;
9213         } 
9214         this.fireEvent('specialkey', field, e);
9215     
9216     },
9217
9218     /**
9219      * Starts the editing process and shows the editor.
9220      * @param {String/HTMLElement/Element} el The element to edit
9221      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9222       * to the innerHTML of el.
9223      */
9224     startEdit : function(el, value){
9225         if(this.editing){
9226             this.completeEdit();
9227         }
9228         this.boundEl = Roo.get(el);
9229         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9230         if(!this.rendered){
9231             this.render(this.parentEl || document.body);
9232         }
9233         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9234             return;
9235         }
9236         this.startValue = v;
9237         this.field.setValue(v);
9238         if(this.autoSize){
9239             var sz = this.boundEl.getSize();
9240             switch(this.autoSize){
9241                 case "width":
9242                 this.setSize(sz.width,  "");
9243                 break;
9244                 case "height":
9245                 this.setSize("",  sz.height);
9246                 break;
9247                 default:
9248                 this.setSize(sz.width,  sz.height);
9249             }
9250         }
9251         this.el.alignTo(this.boundEl, this.alignment);
9252         this.editing = true;
9253         if(Roo.QuickTips){
9254             Roo.QuickTips.disable();
9255         }
9256         this.show();
9257     },
9258
9259     /**
9260      * Sets the height and width of this editor.
9261      * @param {Number} width The new width
9262      * @param {Number} height The new height
9263      */
9264     setSize : function(w, h){
9265         this.field.setSize(w, h);
9266         if(this.el){
9267             this.el.sync();
9268         }
9269     },
9270
9271     /**
9272      * Realigns the editor to the bound field based on the current alignment config value.
9273      */
9274     realign : function(){
9275         this.el.alignTo(this.boundEl, this.alignment);
9276     },
9277
9278     /**
9279      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9280      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9281      */
9282     completeEdit : function(remainVisible){
9283         if(!this.editing){
9284             return;
9285         }
9286         var v = this.getValue();
9287         if(this.revertInvalid !== false && !this.field.isValid()){
9288             v = this.startValue;
9289             this.cancelEdit(true);
9290         }
9291         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9292             this.editing = false;
9293             this.hide();
9294             return;
9295         }
9296         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9297             this.editing = false;
9298             if(this.updateEl && this.boundEl){
9299                 this.boundEl.update(v);
9300             }
9301             if(remainVisible !== true){
9302                 this.hide();
9303             }
9304             this.fireEvent("complete", this, v, this.startValue);
9305         }
9306     },
9307
9308     // private
9309     onShow : function(){
9310         this.el.show();
9311         if(this.hideEl !== false){
9312             this.boundEl.hide();
9313         }
9314         this.field.show();
9315         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9316             this.fixIEFocus = true;
9317             this.deferredFocus.defer(50, this);
9318         }else{
9319             this.field.focus();
9320         }
9321         this.fireEvent("startedit", this.boundEl, this.startValue);
9322     },
9323
9324     deferredFocus : function(){
9325         if(this.editing){
9326             this.field.focus();
9327         }
9328     },
9329
9330     /**
9331      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9332      * reverted to the original starting value.
9333      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9334      * cancel (defaults to false)
9335      */
9336     cancelEdit : function(remainVisible){
9337         if(this.editing){
9338             this.setValue(this.startValue);
9339             if(remainVisible !== true){
9340                 this.hide();
9341             }
9342         }
9343     },
9344
9345     // private
9346     onBlur : function(){
9347         if(this.allowBlur !== true && this.editing){
9348             this.completeEdit();
9349         }
9350     },
9351
9352     // private
9353     onHide : function(){
9354         if(this.editing){
9355             this.completeEdit();
9356             return;
9357         }
9358         this.field.blur();
9359         if(this.field.collapse){
9360             this.field.collapse();
9361         }
9362         this.el.hide();
9363         if(this.hideEl !== false){
9364             this.boundEl.show();
9365         }
9366         if(Roo.QuickTips){
9367             Roo.QuickTips.enable();
9368         }
9369     },
9370
9371     /**
9372      * Sets the data value of the editor
9373      * @param {Mixed} value Any valid value supported by the underlying field
9374      */
9375     setValue : function(v){
9376         this.field.setValue(v);
9377     },
9378
9379     /**
9380      * Gets the data value of the editor
9381      * @return {Mixed} The data value
9382      */
9383     getValue : function(){
9384         return this.field.getValue();
9385     }
9386 });/*
9387  * Based on:
9388  * Ext JS Library 1.1.1
9389  * Copyright(c) 2006-2007, Ext JS, LLC.
9390  *
9391  * Originally Released Under LGPL - original licence link has changed is not relivant.
9392  *
9393  * Fork - LGPL
9394  * <script type="text/javascript">
9395  */
9396  
9397 /**
9398  * @class Roo.BasicDialog
9399  * @extends Roo.util.Observable
9400  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9401  * <pre><code>
9402 var dlg = new Roo.BasicDialog("my-dlg", {
9403     height: 200,
9404     width: 300,
9405     minHeight: 100,
9406     minWidth: 150,
9407     modal: true,
9408     proxyDrag: true,
9409     shadow: true
9410 });
9411 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9412 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9413 dlg.addButton('Cancel', dlg.hide, dlg);
9414 dlg.show();
9415 </code></pre>
9416   <b>A Dialog should always be a direct child of the body element.</b>
9417  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9418  * @cfg {String} title Default text to display in the title bar (defaults to null)
9419  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9420  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9421  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9422  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9423  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9424  * (defaults to null with no animation)
9425  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9426  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9427  * property for valid values (defaults to 'all')
9428  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9429  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9430  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9431  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9432  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9433  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9434  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9435  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9436  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9437  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9438  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9439  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9440  * draggable = true (defaults to false)
9441  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9442  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9443  * shadow (defaults to false)
9444  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9445  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9446  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9447  * @cfg {Array} buttons Array of buttons
9448  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9449  * @constructor
9450  * Create a new BasicDialog.
9451  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9452  * @param {Object} config Configuration options
9453  */
9454 Roo.BasicDialog = function(el, config){
9455     this.el = Roo.get(el);
9456     var dh = Roo.DomHelper;
9457     if(!this.el && config && config.autoCreate){
9458         if(typeof config.autoCreate == "object"){
9459             if(!config.autoCreate.id){
9460                 config.autoCreate.id = el;
9461             }
9462             this.el = dh.append(document.body,
9463                         config.autoCreate, true);
9464         }else{
9465             this.el = dh.append(document.body,
9466                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9467         }
9468     }
9469     el = this.el;
9470     el.setDisplayed(true);
9471     el.hide = this.hideAction;
9472     this.id = el.id;
9473     el.addClass("x-dlg");
9474
9475     Roo.apply(this, config);
9476
9477     this.proxy = el.createProxy("x-dlg-proxy");
9478     this.proxy.hide = this.hideAction;
9479     this.proxy.setOpacity(.5);
9480     this.proxy.hide();
9481
9482     if(config.width){
9483         el.setWidth(config.width);
9484     }
9485     if(config.height){
9486         el.setHeight(config.height);
9487     }
9488     this.size = el.getSize();
9489     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9490         this.xy = [config.x,config.y];
9491     }else{
9492         this.xy = el.getCenterXY(true);
9493     }
9494     /** The header element @type Roo.Element */
9495     this.header = el.child("> .x-dlg-hd");
9496     /** The body element @type Roo.Element */
9497     this.body = el.child("> .x-dlg-bd");
9498     /** The footer element @type Roo.Element */
9499     this.footer = el.child("> .x-dlg-ft");
9500
9501     if(!this.header){
9502         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9503     }
9504     if(!this.body){
9505         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9506     }
9507
9508     this.header.unselectable();
9509     if(this.title){
9510         this.header.update(this.title);
9511     }
9512     // this element allows the dialog to be focused for keyboard event
9513     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9514     this.focusEl.swallowEvent("click", true);
9515
9516     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9517
9518     // wrap the body and footer for special rendering
9519     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9520     if(this.footer){
9521         this.bwrap.dom.appendChild(this.footer.dom);
9522     }
9523
9524     this.bg = this.el.createChild({
9525         tag: "div", cls:"x-dlg-bg",
9526         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9527     });
9528     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9529
9530
9531     if(this.autoScroll !== false && !this.autoTabs){
9532         this.body.setStyle("overflow", "auto");
9533     }
9534
9535     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9536
9537     if(this.closable !== false){
9538         this.el.addClass("x-dlg-closable");
9539         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9540         this.close.on("click", this.closeClick, this);
9541         this.close.addClassOnOver("x-dlg-close-over");
9542     }
9543     if(this.collapsible !== false){
9544         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9545         this.collapseBtn.on("click", this.collapseClick, this);
9546         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9547         this.header.on("dblclick", this.collapseClick, this);
9548     }
9549     if(this.resizable !== false){
9550         this.el.addClass("x-dlg-resizable");
9551         this.resizer = new Roo.Resizable(el, {
9552             minWidth: this.minWidth || 80,
9553             minHeight:this.minHeight || 80,
9554             handles: this.resizeHandles || "all",
9555             pinned: true
9556         });
9557         this.resizer.on("beforeresize", this.beforeResize, this);
9558         this.resizer.on("resize", this.onResize, this);
9559     }
9560     if(this.draggable !== false){
9561         el.addClass("x-dlg-draggable");
9562         if (!this.proxyDrag) {
9563             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9564         }
9565         else {
9566             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9567         }
9568         dd.setHandleElId(this.header.id);
9569         dd.endDrag = this.endMove.createDelegate(this);
9570         dd.startDrag = this.startMove.createDelegate(this);
9571         dd.onDrag = this.onDrag.createDelegate(this);
9572         dd.scroll = false;
9573         this.dd = dd;
9574     }
9575     if(this.modal){
9576         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9577         this.mask.enableDisplayMode("block");
9578         this.mask.hide();
9579         this.el.addClass("x-dlg-modal");
9580     }
9581     if(this.shadow){
9582         this.shadow = new Roo.Shadow({
9583             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9584             offset : this.shadowOffset
9585         });
9586     }else{
9587         this.shadowOffset = 0;
9588     }
9589     if(Roo.useShims && this.shim !== false){
9590         this.shim = this.el.createShim();
9591         this.shim.hide = this.hideAction;
9592         this.shim.hide();
9593     }else{
9594         this.shim = false;
9595     }
9596     if(this.autoTabs){
9597         this.initTabs();
9598     }
9599     if (this.buttons) { 
9600         var bts= this.buttons;
9601         this.buttons = [];
9602         Roo.each(bts, function(b) {
9603             this.addButton(b);
9604         }, this);
9605     }
9606     
9607     
9608     this.addEvents({
9609         /**
9610          * @event keydown
9611          * Fires when a key is pressed
9612          * @param {Roo.BasicDialog} this
9613          * @param {Roo.EventObject} e
9614          */
9615         "keydown" : true,
9616         /**
9617          * @event move
9618          * Fires when this dialog is moved by the user.
9619          * @param {Roo.BasicDialog} this
9620          * @param {Number} x The new page X
9621          * @param {Number} y The new page Y
9622          */
9623         "move" : true,
9624         /**
9625          * @event resize
9626          * Fires when this dialog is resized by the user.
9627          * @param {Roo.BasicDialog} this
9628          * @param {Number} width The new width
9629          * @param {Number} height The new height
9630          */
9631         "resize" : true,
9632         /**
9633          * @event beforehide
9634          * Fires before this dialog is hidden.
9635          * @param {Roo.BasicDialog} this
9636          */
9637         "beforehide" : true,
9638         /**
9639          * @event hide
9640          * Fires when this dialog is hidden.
9641          * @param {Roo.BasicDialog} this
9642          */
9643         "hide" : true,
9644         /**
9645          * @event beforeshow
9646          * Fires before this dialog is shown.
9647          * @param {Roo.BasicDialog} this
9648          */
9649         "beforeshow" : true,
9650         /**
9651          * @event show
9652          * Fires when this dialog is shown.
9653          * @param {Roo.BasicDialog} this
9654          */
9655         "show" : true
9656     });
9657     el.on("keydown", this.onKeyDown, this);
9658     el.on("mousedown", this.toFront, this);
9659     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9660     this.el.hide();
9661     Roo.DialogManager.register(this);
9662     Roo.BasicDialog.superclass.constructor.call(this);
9663 };
9664
9665 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9666     shadowOffset: Roo.isIE ? 6 : 5,
9667     minHeight: 80,
9668     minWidth: 200,
9669     minButtonWidth: 75,
9670     defaultButton: null,
9671     buttonAlign: "right",
9672     tabTag: 'div',
9673     firstShow: true,
9674
9675     /**
9676      * Sets the dialog title text
9677      * @param {String} text The title text to display
9678      * @return {Roo.BasicDialog} this
9679      */
9680     setTitle : function(text){
9681         this.header.update(text);
9682         return this;
9683     },
9684
9685     // private
9686     closeClick : function(){
9687         this.hide();
9688     },
9689
9690     // private
9691     collapseClick : function(){
9692         this[this.collapsed ? "expand" : "collapse"]();
9693     },
9694
9695     /**
9696      * Collapses the dialog to its minimized state (only the title bar is visible).
9697      * Equivalent to the user clicking the collapse dialog button.
9698      */
9699     collapse : function(){
9700         if(!this.collapsed){
9701             this.collapsed = true;
9702             this.el.addClass("x-dlg-collapsed");
9703             this.restoreHeight = this.el.getHeight();
9704             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9705         }
9706     },
9707
9708     /**
9709      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9710      * clicking the expand dialog button.
9711      */
9712     expand : function(){
9713         if(this.collapsed){
9714             this.collapsed = false;
9715             this.el.removeClass("x-dlg-collapsed");
9716             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9717         }
9718     },
9719
9720     /**
9721      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9722      * @return {Roo.TabPanel} The tabs component
9723      */
9724     initTabs : function(){
9725         var tabs = this.getTabs();
9726         while(tabs.getTab(0)){
9727             tabs.removeTab(0);
9728         }
9729         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9730             var dom = el.dom;
9731             tabs.addTab(Roo.id(dom), dom.title);
9732             dom.title = "";
9733         });
9734         tabs.activate(0);
9735         return tabs;
9736     },
9737
9738     // private
9739     beforeResize : function(){
9740         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9741     },
9742
9743     // private
9744     onResize : function(){
9745         this.refreshSize();
9746         this.syncBodyHeight();
9747         this.adjustAssets();
9748         this.focus();
9749         this.fireEvent("resize", this, this.size.width, this.size.height);
9750     },
9751
9752     // private
9753     onKeyDown : function(e){
9754         if(this.isVisible()){
9755             this.fireEvent("keydown", this, e);
9756         }
9757     },
9758
9759     /**
9760      * Resizes the dialog.
9761      * @param {Number} width
9762      * @param {Number} height
9763      * @return {Roo.BasicDialog} this
9764      */
9765     resizeTo : function(width, height){
9766         this.el.setSize(width, height);
9767         this.size = {width: width, height: height};
9768         this.syncBodyHeight();
9769         if(this.fixedcenter){
9770             this.center();
9771         }
9772         if(this.isVisible()){
9773             this.constrainXY();
9774             this.adjustAssets();
9775         }
9776         this.fireEvent("resize", this, width, height);
9777         return this;
9778     },
9779
9780
9781     /**
9782      * Resizes the dialog to fit the specified content size.
9783      * @param {Number} width
9784      * @param {Number} height
9785      * @return {Roo.BasicDialog} this
9786      */
9787     setContentSize : function(w, h){
9788         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9789         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9790         //if(!this.el.isBorderBox()){
9791             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9792             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9793         //}
9794         if(this.tabs){
9795             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9796             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9797         }
9798         this.resizeTo(w, h);
9799         return this;
9800     },
9801
9802     /**
9803      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9804      * executed in response to a particular key being pressed while the dialog is active.
9805      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9806      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9807      * @param {Function} fn The function to call
9808      * @param {Object} scope (optional) The scope of the function
9809      * @return {Roo.BasicDialog} this
9810      */
9811     addKeyListener : function(key, fn, scope){
9812         var keyCode, shift, ctrl, alt;
9813         if(typeof key == "object" && !(key instanceof Array)){
9814             keyCode = key["key"];
9815             shift = key["shift"];
9816             ctrl = key["ctrl"];
9817             alt = key["alt"];
9818         }else{
9819             keyCode = key;
9820         }
9821         var handler = function(dlg, e){
9822             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9823                 var k = e.getKey();
9824                 if(keyCode instanceof Array){
9825                     for(var i = 0, len = keyCode.length; i < len; i++){
9826                         if(keyCode[i] == k){
9827                           fn.call(scope || window, dlg, k, e);
9828                           return;
9829                         }
9830                     }
9831                 }else{
9832                     if(k == keyCode){
9833                         fn.call(scope || window, dlg, k, e);
9834                     }
9835                 }
9836             }
9837         };
9838         this.on("keydown", handler);
9839         return this;
9840     },
9841
9842     /**
9843      * Returns the TabPanel component (creates it if it doesn't exist).
9844      * Note: If you wish to simply check for the existence of tabs without creating them,
9845      * check for a null 'tabs' property.
9846      * @return {Roo.TabPanel} The tabs component
9847      */
9848     getTabs : function(){
9849         if(!this.tabs){
9850             this.el.addClass("x-dlg-auto-tabs");
9851             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9852             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9853         }
9854         return this.tabs;
9855     },
9856
9857     /**
9858      * Adds a button to the footer section of the dialog.
9859      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9860      * object or a valid Roo.DomHelper element config
9861      * @param {Function} handler The function called when the button is clicked
9862      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9863      * @return {Roo.Button} The new button
9864      */
9865     addButton : function(config, handler, scope){
9866         var dh = Roo.DomHelper;
9867         if(!this.footer){
9868             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9869         }
9870         if(!this.btnContainer){
9871             var tb = this.footer.createChild({
9872
9873                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9874                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9875             }, null, true);
9876             this.btnContainer = tb.firstChild.firstChild.firstChild;
9877         }
9878         var bconfig = {
9879             handler: handler,
9880             scope: scope,
9881             minWidth: this.minButtonWidth,
9882             hideParent:true
9883         };
9884         if(typeof config == "string"){
9885             bconfig.text = config;
9886         }else{
9887             if(config.tag){
9888                 bconfig.dhconfig = config;
9889             }else{
9890                 Roo.apply(bconfig, config);
9891             }
9892         }
9893         var fc = false;
9894         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9895             bconfig.position = Math.max(0, bconfig.position);
9896             fc = this.btnContainer.childNodes[bconfig.position];
9897         }
9898          
9899         var btn = new Roo.Button(
9900             fc ? 
9901                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9902                 : this.btnContainer.appendChild(document.createElement("td")),
9903             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9904             bconfig
9905         );
9906         this.syncBodyHeight();
9907         if(!this.buttons){
9908             /**
9909              * Array of all the buttons that have been added to this dialog via addButton
9910              * @type Array
9911              */
9912             this.buttons = [];
9913         }
9914         this.buttons.push(btn);
9915         return btn;
9916     },
9917
9918     /**
9919      * Sets the default button to be focused when the dialog is displayed.
9920      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9921      * @return {Roo.BasicDialog} this
9922      */
9923     setDefaultButton : function(btn){
9924         this.defaultButton = btn;
9925         return this;
9926     },
9927
9928     // private
9929     getHeaderFooterHeight : function(safe){
9930         var height = 0;
9931         if(this.header){
9932            height += this.header.getHeight();
9933         }
9934         if(this.footer){
9935            var fm = this.footer.getMargins();
9936             height += (this.footer.getHeight()+fm.top+fm.bottom);
9937         }
9938         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9939         height += this.centerBg.getPadding("tb");
9940         return height;
9941     },
9942
9943     // private
9944     syncBodyHeight : function()
9945     {
9946         var bd = this.body, // the text
9947             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9948             bw = this.bwrap;
9949         var height = this.size.height - this.getHeaderFooterHeight(false);
9950         bd.setHeight(height-bd.getMargins("tb"));
9951         var hh = this.header.getHeight();
9952         var h = this.size.height-hh;
9953         cb.setHeight(h);
9954         
9955         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9956         bw.setHeight(h-cb.getPadding("tb"));
9957         
9958         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9959         bd.setWidth(bw.getWidth(true));
9960         if(this.tabs){
9961             this.tabs.syncHeight();
9962             if(Roo.isIE){
9963                 this.tabs.el.repaint();
9964             }
9965         }
9966     },
9967
9968     /**
9969      * Restores the previous state of the dialog if Roo.state is configured.
9970      * @return {Roo.BasicDialog} this
9971      */
9972     restoreState : function(){
9973         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9974         if(box && box.width){
9975             this.xy = [box.x, box.y];
9976             this.resizeTo(box.width, box.height);
9977         }
9978         return this;
9979     },
9980
9981     // private
9982     beforeShow : function(){
9983         this.expand();
9984         if(this.fixedcenter){
9985             this.xy = this.el.getCenterXY(true);
9986         }
9987         if(this.modal){
9988             Roo.get(document.body).addClass("x-body-masked");
9989             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9990             this.mask.show();
9991         }
9992         this.constrainXY();
9993     },
9994
9995     // private
9996     animShow : function(){
9997         var b = Roo.get(this.animateTarget).getBox();
9998         this.proxy.setSize(b.width, b.height);
9999         this.proxy.setLocation(b.x, b.y);
10000         this.proxy.show();
10001         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
10002                     true, .35, this.showEl.createDelegate(this));
10003     },
10004
10005     /**
10006      * Shows the dialog.
10007      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10008      * @return {Roo.BasicDialog} this
10009      */
10010     show : function(animateTarget){
10011         if (this.fireEvent("beforeshow", this) === false){
10012             return;
10013         }
10014         if(this.syncHeightBeforeShow){
10015             this.syncBodyHeight();
10016         }else if(this.firstShow){
10017             this.firstShow = false;
10018             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10019         }
10020         this.animateTarget = animateTarget || this.animateTarget;
10021         if(!this.el.isVisible()){
10022             this.beforeShow();
10023             if(this.animateTarget && Roo.get(this.animateTarget)){
10024                 this.animShow();
10025             }else{
10026                 this.showEl();
10027             }
10028         }
10029         return this;
10030     },
10031
10032     // private
10033     showEl : function(){
10034         this.proxy.hide();
10035         this.el.setXY(this.xy);
10036         this.el.show();
10037         this.adjustAssets(true);
10038         this.toFront();
10039         this.focus();
10040         // IE peekaboo bug - fix found by Dave Fenwick
10041         if(Roo.isIE){
10042             this.el.repaint();
10043         }
10044         this.fireEvent("show", this);
10045     },
10046
10047     /**
10048      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10049      * dialog itself will receive focus.
10050      */
10051     focus : function(){
10052         if(this.defaultButton){
10053             this.defaultButton.focus();
10054         }else{
10055             this.focusEl.focus();
10056         }
10057     },
10058
10059     // private
10060     constrainXY : function(){
10061         if(this.constraintoviewport !== false){
10062             if(!this.viewSize){
10063                 if(this.container){
10064                     var s = this.container.getSize();
10065                     this.viewSize = [s.width, s.height];
10066                 }else{
10067                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10068                 }
10069             }
10070             var s = Roo.get(this.container||document).getScroll();
10071
10072             var x = this.xy[0], y = this.xy[1];
10073             var w = this.size.width, h = this.size.height;
10074             var vw = this.viewSize[0], vh = this.viewSize[1];
10075             // only move it if it needs it
10076             var moved = false;
10077             // first validate right/bottom
10078             if(x + w > vw+s.left){
10079                 x = vw - w;
10080                 moved = true;
10081             }
10082             if(y + h > vh+s.top){
10083                 y = vh - h;
10084                 moved = true;
10085             }
10086             // then make sure top/left isn't negative
10087             if(x < s.left){
10088                 x = s.left;
10089                 moved = true;
10090             }
10091             if(y < s.top){
10092                 y = s.top;
10093                 moved = true;
10094             }
10095             if(moved){
10096                 // cache xy
10097                 this.xy = [x, y];
10098                 if(this.isVisible()){
10099                     this.el.setLocation(x, y);
10100                     this.adjustAssets();
10101                 }
10102             }
10103         }
10104     },
10105
10106     // private
10107     onDrag : function(){
10108         if(!this.proxyDrag){
10109             this.xy = this.el.getXY();
10110             this.adjustAssets();
10111         }
10112     },
10113
10114     // private
10115     adjustAssets : function(doShow){
10116         var x = this.xy[0], y = this.xy[1];
10117         var w = this.size.width, h = this.size.height;
10118         if(doShow === true){
10119             if(this.shadow){
10120                 this.shadow.show(this.el);
10121             }
10122             if(this.shim){
10123                 this.shim.show();
10124             }
10125         }
10126         if(this.shadow && this.shadow.isVisible()){
10127             this.shadow.show(this.el);
10128         }
10129         if(this.shim && this.shim.isVisible()){
10130             this.shim.setBounds(x, y, w, h);
10131         }
10132     },
10133
10134     // private
10135     adjustViewport : function(w, h){
10136         if(!w || !h){
10137             w = Roo.lib.Dom.getViewWidth();
10138             h = Roo.lib.Dom.getViewHeight();
10139         }
10140         // cache the size
10141         this.viewSize = [w, h];
10142         if(this.modal && this.mask.isVisible()){
10143             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10144             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10145         }
10146         if(this.isVisible()){
10147             this.constrainXY();
10148         }
10149     },
10150
10151     /**
10152      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10153      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10154      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10155      */
10156     destroy : function(removeEl){
10157         if(this.isVisible()){
10158             this.animateTarget = null;
10159             this.hide();
10160         }
10161         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10162         if(this.tabs){
10163             this.tabs.destroy(removeEl);
10164         }
10165         Roo.destroy(
10166              this.shim,
10167              this.proxy,
10168              this.resizer,
10169              this.close,
10170              this.mask
10171         );
10172         if(this.dd){
10173             this.dd.unreg();
10174         }
10175         if(this.buttons){
10176            for(var i = 0, len = this.buttons.length; i < len; i++){
10177                this.buttons[i].destroy();
10178            }
10179         }
10180         this.el.removeAllListeners();
10181         if(removeEl === true){
10182             this.el.update("");
10183             this.el.remove();
10184         }
10185         Roo.DialogManager.unregister(this);
10186     },
10187
10188     // private
10189     startMove : function(){
10190         if(this.proxyDrag){
10191             this.proxy.show();
10192         }
10193         if(this.constraintoviewport !== false){
10194             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10195         }
10196     },
10197
10198     // private
10199     endMove : function(){
10200         if(!this.proxyDrag){
10201             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10202         }else{
10203             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10204             this.proxy.hide();
10205         }
10206         this.refreshSize();
10207         this.adjustAssets();
10208         this.focus();
10209         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10210     },
10211
10212     /**
10213      * Brings this dialog to the front of any other visible dialogs
10214      * @return {Roo.BasicDialog} this
10215      */
10216     toFront : function(){
10217         Roo.DialogManager.bringToFront(this);
10218         return this;
10219     },
10220
10221     /**
10222      * Sends this dialog to the back (under) of any other visible dialogs
10223      * @return {Roo.BasicDialog} this
10224      */
10225     toBack : function(){
10226         Roo.DialogManager.sendToBack(this);
10227         return this;
10228     },
10229
10230     /**
10231      * Centers this dialog in the viewport
10232      * @return {Roo.BasicDialog} this
10233      */
10234     center : function(){
10235         var xy = this.el.getCenterXY(true);
10236         this.moveTo(xy[0], xy[1]);
10237         return this;
10238     },
10239
10240     /**
10241      * Moves the dialog's top-left corner to the specified point
10242      * @param {Number} x
10243      * @param {Number} y
10244      * @return {Roo.BasicDialog} this
10245      */
10246     moveTo : function(x, y){
10247         this.xy = [x,y];
10248         if(this.isVisible()){
10249             this.el.setXY(this.xy);
10250             this.adjustAssets();
10251         }
10252         return this;
10253     },
10254
10255     /**
10256      * Aligns the dialog to the specified element
10257      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10258      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10259      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10260      * @return {Roo.BasicDialog} this
10261      */
10262     alignTo : function(element, position, offsets){
10263         this.xy = this.el.getAlignToXY(element, position, offsets);
10264         if(this.isVisible()){
10265             this.el.setXY(this.xy);
10266             this.adjustAssets();
10267         }
10268         return this;
10269     },
10270
10271     /**
10272      * Anchors an element to another element and realigns it when the window is resized.
10273      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10274      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10275      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10276      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10277      * is a number, it is used as the buffer delay (defaults to 50ms).
10278      * @return {Roo.BasicDialog} this
10279      */
10280     anchorTo : function(el, alignment, offsets, monitorScroll){
10281         var action = function(){
10282             this.alignTo(el, alignment, offsets);
10283         };
10284         Roo.EventManager.onWindowResize(action, this);
10285         var tm = typeof monitorScroll;
10286         if(tm != 'undefined'){
10287             Roo.EventManager.on(window, 'scroll', action, this,
10288                 {buffer: tm == 'number' ? monitorScroll : 50});
10289         }
10290         action.call(this);
10291         return this;
10292     },
10293
10294     /**
10295      * Returns true if the dialog is visible
10296      * @return {Boolean}
10297      */
10298     isVisible : function(){
10299         return this.el.isVisible();
10300     },
10301
10302     // private
10303     animHide : function(callback){
10304         var b = Roo.get(this.animateTarget).getBox();
10305         this.proxy.show();
10306         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10307         this.el.hide();
10308         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10309                     this.hideEl.createDelegate(this, [callback]));
10310     },
10311
10312     /**
10313      * Hides the dialog.
10314      * @param {Function} callback (optional) Function to call when the dialog is hidden
10315      * @return {Roo.BasicDialog} this
10316      */
10317     hide : function(callback){
10318         if (this.fireEvent("beforehide", this) === false){
10319             return;
10320         }
10321         if(this.shadow){
10322             this.shadow.hide();
10323         }
10324         if(this.shim) {
10325           this.shim.hide();
10326         }
10327         // sometimes animateTarget seems to get set.. causing problems...
10328         // this just double checks..
10329         if(this.animateTarget && Roo.get(this.animateTarget)) {
10330            this.animHide(callback);
10331         }else{
10332             this.el.hide();
10333             this.hideEl(callback);
10334         }
10335         return this;
10336     },
10337
10338     // private
10339     hideEl : function(callback){
10340         this.proxy.hide();
10341         if(this.modal){
10342             this.mask.hide();
10343             Roo.get(document.body).removeClass("x-body-masked");
10344         }
10345         this.fireEvent("hide", this);
10346         if(typeof callback == "function"){
10347             callback();
10348         }
10349     },
10350
10351     // private
10352     hideAction : function(){
10353         this.setLeft("-10000px");
10354         this.setTop("-10000px");
10355         this.setStyle("visibility", "hidden");
10356     },
10357
10358     // private
10359     refreshSize : function(){
10360         this.size = this.el.getSize();
10361         this.xy = this.el.getXY();
10362         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10363     },
10364
10365     // private
10366     // z-index is managed by the DialogManager and may be overwritten at any time
10367     setZIndex : function(index){
10368         if(this.modal){
10369             this.mask.setStyle("z-index", index);
10370         }
10371         if(this.shim){
10372             this.shim.setStyle("z-index", ++index);
10373         }
10374         if(this.shadow){
10375             this.shadow.setZIndex(++index);
10376         }
10377         this.el.setStyle("z-index", ++index);
10378         if(this.proxy){
10379             this.proxy.setStyle("z-index", ++index);
10380         }
10381         if(this.resizer){
10382             this.resizer.proxy.setStyle("z-index", ++index);
10383         }
10384
10385         this.lastZIndex = index;
10386     },
10387
10388     /**
10389      * Returns the element for this dialog
10390      * @return {Roo.Element} The underlying dialog Element
10391      */
10392     getEl : function(){
10393         return this.el;
10394     }
10395 });
10396
10397 /**
10398  * @class Roo.DialogManager
10399  * Provides global access to BasicDialogs that have been created and
10400  * support for z-indexing (layering) multiple open dialogs.
10401  */
10402 Roo.DialogManager = function(){
10403     var list = {};
10404     var accessList = [];
10405     var front = null;
10406
10407     // private
10408     var sortDialogs = function(d1, d2){
10409         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10410     };
10411
10412     // private
10413     var orderDialogs = function(){
10414         accessList.sort(sortDialogs);
10415         var seed = Roo.DialogManager.zseed;
10416         for(var i = 0, len = accessList.length; i < len; i++){
10417             var dlg = accessList[i];
10418             if(dlg){
10419                 dlg.setZIndex(seed + (i*10));
10420             }
10421         }
10422     };
10423
10424     return {
10425         /**
10426          * The starting z-index for BasicDialogs (defaults to 9000)
10427          * @type Number The z-index value
10428          */
10429         zseed : 9000,
10430
10431         // private
10432         register : function(dlg){
10433             list[dlg.id] = dlg;
10434             accessList.push(dlg);
10435         },
10436
10437         // private
10438         unregister : function(dlg){
10439             delete list[dlg.id];
10440             var i=0;
10441             var len=0;
10442             if(!accessList.indexOf){
10443                 for(  i = 0, len = accessList.length; i < len; i++){
10444                     if(accessList[i] == dlg){
10445                         accessList.splice(i, 1);
10446                         return;
10447                     }
10448                 }
10449             }else{
10450                  i = accessList.indexOf(dlg);
10451                 if(i != -1){
10452                     accessList.splice(i, 1);
10453                 }
10454             }
10455         },
10456
10457         /**
10458          * Gets a registered dialog by id
10459          * @param {String/Object} id The id of the dialog or a dialog
10460          * @return {Roo.BasicDialog} this
10461          */
10462         get : function(id){
10463             return typeof id == "object" ? id : list[id];
10464         },
10465
10466         /**
10467          * Brings the specified dialog to the front
10468          * @param {String/Object} dlg The id of the dialog or a dialog
10469          * @return {Roo.BasicDialog} this
10470          */
10471         bringToFront : function(dlg){
10472             dlg = this.get(dlg);
10473             if(dlg != front){
10474                 front = dlg;
10475                 dlg._lastAccess = new Date().getTime();
10476                 orderDialogs();
10477             }
10478             return dlg;
10479         },
10480
10481         /**
10482          * Sends the specified dialog to the back
10483          * @param {String/Object} dlg The id of the dialog or a dialog
10484          * @return {Roo.BasicDialog} this
10485          */
10486         sendToBack : function(dlg){
10487             dlg = this.get(dlg);
10488             dlg._lastAccess = -(new Date().getTime());
10489             orderDialogs();
10490             return dlg;
10491         },
10492
10493         /**
10494          * Hides all dialogs
10495          */
10496         hideAll : function(){
10497             for(var id in list){
10498                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10499                     list[id].hide();
10500                 }
10501             }
10502         }
10503     };
10504 }();
10505
10506 /**
10507  * @class Roo.LayoutDialog
10508  * @extends Roo.BasicDialog
10509  * Dialog which provides adjustments for working with a layout in a Dialog.
10510  * Add your necessary layout config options to the dialog's config.<br>
10511  * Example usage (including a nested layout):
10512  * <pre><code>
10513 if(!dialog){
10514     dialog = new Roo.LayoutDialog("download-dlg", {
10515         modal: true,
10516         width:600,
10517         height:450,
10518         shadow:true,
10519         minWidth:500,
10520         minHeight:350,
10521         autoTabs:true,
10522         proxyDrag:true,
10523         // layout config merges with the dialog config
10524         center:{
10525             tabPosition: "top",
10526             alwaysShowTabs: true
10527         }
10528     });
10529     dialog.addKeyListener(27, dialog.hide, dialog);
10530     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10531     dialog.addButton("Build It!", this.getDownload, this);
10532
10533     // we can even add nested layouts
10534     var innerLayout = new Roo.BorderLayout("dl-inner", {
10535         east: {
10536             initialSize: 200,
10537             autoScroll:true,
10538             split:true
10539         },
10540         center: {
10541             autoScroll:true
10542         }
10543     });
10544     innerLayout.beginUpdate();
10545     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10546     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10547     innerLayout.endUpdate(true);
10548
10549     var layout = dialog.getLayout();
10550     layout.beginUpdate();
10551     layout.add("center", new Roo.ContentPanel("standard-panel",
10552                         {title: "Download the Source", fitToFrame:true}));
10553     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10554                {title: "Build your own roo.js"}));
10555     layout.getRegion("center").showPanel(sp);
10556     layout.endUpdate();
10557 }
10558 </code></pre>
10559     * @constructor
10560     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10561     * @param {Object} config configuration options
10562   */
10563 Roo.LayoutDialog = function(el, cfg){
10564     
10565     var config=  cfg;
10566     if (typeof(cfg) == 'undefined') {
10567         config = Roo.apply({}, el);
10568         // not sure why we use documentElement here.. - it should always be body.
10569         // IE7 borks horribly if we use documentElement.
10570         // webkit also does not like documentElement - it creates a body element...
10571         el = Roo.get( document.body || document.documentElement ).createChild();
10572         //config.autoCreate = true;
10573     }
10574     
10575     
10576     config.autoTabs = false;
10577     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10578     this.body.setStyle({overflow:"hidden", position:"relative"});
10579     this.layout = new Roo.BorderLayout(this.body.dom, config);
10580     this.layout.monitorWindowResize = false;
10581     this.el.addClass("x-dlg-auto-layout");
10582     // fix case when center region overwrites center function
10583     this.center = Roo.BasicDialog.prototype.center;
10584     this.on("show", this.layout.layout, this.layout, true);
10585     if (config.items) {
10586         var xitems = config.items;
10587         delete config.items;
10588         Roo.each(xitems, this.addxtype, this);
10589     }
10590     
10591     
10592 };
10593 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10594     /**
10595      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10596      * @deprecated
10597      */
10598     endUpdate : function(){
10599         this.layout.endUpdate();
10600     },
10601
10602     /**
10603      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10604      *  @deprecated
10605      */
10606     beginUpdate : function(){
10607         this.layout.beginUpdate();
10608     },
10609
10610     /**
10611      * Get the BorderLayout for this dialog
10612      * @return {Roo.BorderLayout}
10613      */
10614     getLayout : function(){
10615         return this.layout;
10616     },
10617
10618     showEl : function(){
10619         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10620         if(Roo.isIE7){
10621             this.layout.layout();
10622         }
10623     },
10624
10625     // private
10626     // Use the syncHeightBeforeShow config option to control this automatically
10627     syncBodyHeight : function(){
10628         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10629         if(this.layout){this.layout.layout();}
10630     },
10631     
10632       /**
10633      * Add an xtype element (actually adds to the layout.)
10634      * @return {Object} xdata xtype object data.
10635      */
10636     
10637     addxtype : function(c) {
10638         return this.layout.addxtype(c);
10639     }
10640 });/*
10641  * Based on:
10642  * Ext JS Library 1.1.1
10643  * Copyright(c) 2006-2007, Ext JS, LLC.
10644  *
10645  * Originally Released Under LGPL - original licence link has changed is not relivant.
10646  *
10647  * Fork - LGPL
10648  * <script type="text/javascript">
10649  */
10650  
10651 /**
10652  * @class Roo.MessageBox
10653  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10654  * Example usage:
10655  *<pre><code>
10656 // Basic alert:
10657 Roo.Msg.alert('Status', 'Changes saved successfully.');
10658
10659 // Prompt for user data:
10660 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10661     if (btn == 'ok'){
10662         // process text value...
10663     }
10664 });
10665
10666 // Show a dialog using config options:
10667 Roo.Msg.show({
10668    title:'Save Changes?',
10669    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10670    buttons: Roo.Msg.YESNOCANCEL,
10671    fn: processResult,
10672    animEl: 'elId'
10673 });
10674 </code></pre>
10675  * @singleton
10676  */
10677 Roo.MessageBox = function(){
10678     var dlg, opt, mask, waitTimer;
10679     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10680     var buttons, activeTextEl, bwidth;
10681
10682     // private
10683     var handleButton = function(button){
10684         dlg.hide();
10685         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10686     };
10687
10688     // private
10689     var handleHide = function(){
10690         if(opt && opt.cls){
10691             dlg.el.removeClass(opt.cls);
10692         }
10693         if(waitTimer){
10694             Roo.TaskMgr.stop(waitTimer);
10695             waitTimer = null;
10696         }
10697     };
10698
10699     // private
10700     var updateButtons = function(b){
10701         var width = 0;
10702         if(!b){
10703             buttons["ok"].hide();
10704             buttons["cancel"].hide();
10705             buttons["yes"].hide();
10706             buttons["no"].hide();
10707             dlg.footer.dom.style.display = 'none';
10708             return width;
10709         }
10710         dlg.footer.dom.style.display = '';
10711         for(var k in buttons){
10712             if(typeof buttons[k] != "function"){
10713                 if(b[k]){
10714                     buttons[k].show();
10715                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10716                     width += buttons[k].el.getWidth()+15;
10717                 }else{
10718                     buttons[k].hide();
10719                 }
10720             }
10721         }
10722         return width;
10723     };
10724
10725     // private
10726     var handleEsc = function(d, k, e){
10727         if(opt && opt.closable !== false){
10728             dlg.hide();
10729         }
10730         if(e){
10731             e.stopEvent();
10732         }
10733     };
10734
10735     return {
10736         /**
10737          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10738          * @return {Roo.BasicDialog} The BasicDialog element
10739          */
10740         getDialog : function(){
10741            if(!dlg){
10742                 dlg = new Roo.BasicDialog("x-msg-box", {
10743                     autoCreate : true,
10744                     shadow: true,
10745                     draggable: true,
10746                     resizable:false,
10747                     constraintoviewport:false,
10748                     fixedcenter:true,
10749                     collapsible : false,
10750                     shim:true,
10751                     modal: true,
10752                     width:400, height:100,
10753                     buttonAlign:"center",
10754                     closeClick : function(){
10755                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10756                             handleButton("no");
10757                         }else{
10758                             handleButton("cancel");
10759                         }
10760                     }
10761                 });
10762                 dlg.on("hide", handleHide);
10763                 mask = dlg.mask;
10764                 dlg.addKeyListener(27, handleEsc);
10765                 buttons = {};
10766                 var bt = this.buttonText;
10767                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10768                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10769                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10770                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10771                 bodyEl = dlg.body.createChild({
10772
10773                     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>'
10774                 });
10775                 msgEl = bodyEl.dom.firstChild;
10776                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10777                 textboxEl.enableDisplayMode();
10778                 textboxEl.addKeyListener([10,13], function(){
10779                     if(dlg.isVisible() && opt && opt.buttons){
10780                         if(opt.buttons.ok){
10781                             handleButton("ok");
10782                         }else if(opt.buttons.yes){
10783                             handleButton("yes");
10784                         }
10785                     }
10786                 });
10787                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10788                 textareaEl.enableDisplayMode();
10789                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10790                 progressEl.enableDisplayMode();
10791                 var pf = progressEl.dom.firstChild;
10792                 if (pf) {
10793                     pp = Roo.get(pf.firstChild);
10794                     pp.setHeight(pf.offsetHeight);
10795                 }
10796                 
10797             }
10798             return dlg;
10799         },
10800
10801         /**
10802          * Updates the message box body text
10803          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10804          * the XHTML-compliant non-breaking space character '&amp;#160;')
10805          * @return {Roo.MessageBox} This message box
10806          */
10807         updateText : function(text){
10808             if(!dlg.isVisible() && !opt.width){
10809                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10810             }
10811             msgEl.innerHTML = text || '&#160;';
10812       
10813             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10814             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10815             var w = Math.max(
10816                     Math.min(opt.width || cw , this.maxWidth), 
10817                     Math.max(opt.minWidth || this.minWidth, bwidth)
10818             );
10819             if(opt.prompt){
10820                 activeTextEl.setWidth(w);
10821             }
10822             if(dlg.isVisible()){
10823                 dlg.fixedcenter = false;
10824             }
10825             // to big, make it scroll. = But as usual stupid IE does not support
10826             // !important..
10827             
10828             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10829                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10830                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10831             } else {
10832                 bodyEl.dom.style.height = '';
10833                 bodyEl.dom.style.overflowY = '';
10834             }
10835             if (cw > w) {
10836                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10837             } else {
10838                 bodyEl.dom.style.overflowX = '';
10839             }
10840             
10841             dlg.setContentSize(w, bodyEl.getHeight());
10842             if(dlg.isVisible()){
10843                 dlg.fixedcenter = true;
10844             }
10845             return this;
10846         },
10847
10848         /**
10849          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10850          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10851          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10852          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10853          * @return {Roo.MessageBox} This message box
10854          */
10855         updateProgress : function(value, text){
10856             if(text){
10857                 this.updateText(text);
10858             }
10859             if (pp) { // weird bug on my firefox - for some reason this is not defined
10860                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10861             }
10862             return this;
10863         },        
10864
10865         /**
10866          * Returns true if the message box is currently displayed
10867          * @return {Boolean} True if the message box is visible, else false
10868          */
10869         isVisible : function(){
10870             return dlg && dlg.isVisible();  
10871         },
10872
10873         /**
10874          * Hides the message box if it is displayed
10875          */
10876         hide : function(){
10877             if(this.isVisible()){
10878                 dlg.hide();
10879             }  
10880         },
10881
10882         /**
10883          * Displays a new message box, or reinitializes an existing message box, based on the config options
10884          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10885          * The following config object properties are supported:
10886          * <pre>
10887 Property    Type             Description
10888 ----------  ---------------  ------------------------------------------------------------------------------------
10889 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10890                                    closes (defaults to undefined)
10891 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10892                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10893 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10894                                    progress and wait dialogs will ignore this property and always hide the
10895                                    close button as they can only be closed programmatically.
10896 cls               String           A custom CSS class to apply to the message box element
10897 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10898                                    displayed (defaults to 75)
10899 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10900                                    function will be btn (the name of the button that was clicked, if applicable,
10901                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10902                                    Progress and wait dialogs will ignore this option since they do not respond to
10903                                    user actions and can only be closed programmatically, so any required function
10904                                    should be called by the same code after it closes the dialog.
10905 icon              String           A CSS class that provides a background image to be used as an icon for
10906                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10907 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10908 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10909 modal             Boolean          False to allow user interaction with the page while the message box is
10910                                    displayed (defaults to true)
10911 msg               String           A string that will replace the existing message box body text (defaults
10912                                    to the XHTML-compliant non-breaking space character '&#160;')
10913 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10914 progress          Boolean          True to display a progress bar (defaults to false)
10915 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10916 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10917 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10918 title             String           The title text
10919 value             String           The string value to set into the active textbox element if displayed
10920 wait              Boolean          True to display a progress bar (defaults to false)
10921 width             Number           The width of the dialog in pixels
10922 </pre>
10923          *
10924          * Example usage:
10925          * <pre><code>
10926 Roo.Msg.show({
10927    title: 'Address',
10928    msg: 'Please enter your address:',
10929    width: 300,
10930    buttons: Roo.MessageBox.OKCANCEL,
10931    multiline: true,
10932    fn: saveAddress,
10933    animEl: 'addAddressBtn'
10934 });
10935 </code></pre>
10936          * @param {Object} config Configuration options
10937          * @return {Roo.MessageBox} This message box
10938          */
10939         show : function(options)
10940         {
10941             
10942             // this causes nightmares if you show one dialog after another
10943             // especially on callbacks..
10944              
10945             if(this.isVisible()){
10946                 
10947                 this.hide();
10948                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10949                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10950                 Roo.log("New Dialog Message:" +  options.msg )
10951                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10952                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10953                 
10954             }
10955             var d = this.getDialog();
10956             opt = options;
10957             d.setTitle(opt.title || "&#160;");
10958             d.close.setDisplayed(opt.closable !== false);
10959             activeTextEl = textboxEl;
10960             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10961             if(opt.prompt){
10962                 if(opt.multiline){
10963                     textboxEl.hide();
10964                     textareaEl.show();
10965                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10966                         opt.multiline : this.defaultTextHeight);
10967                     activeTextEl = textareaEl;
10968                 }else{
10969                     textboxEl.show();
10970                     textareaEl.hide();
10971                 }
10972             }else{
10973                 textboxEl.hide();
10974                 textareaEl.hide();
10975             }
10976             progressEl.setDisplayed(opt.progress === true);
10977             this.updateProgress(0);
10978             activeTextEl.dom.value = opt.value || "";
10979             if(opt.prompt){
10980                 dlg.setDefaultButton(activeTextEl);
10981             }else{
10982                 var bs = opt.buttons;
10983                 var db = null;
10984                 if(bs && bs.ok){
10985                     db = buttons["ok"];
10986                 }else if(bs && bs.yes){
10987                     db = buttons["yes"];
10988                 }
10989                 dlg.setDefaultButton(db);
10990             }
10991             bwidth = updateButtons(opt.buttons);
10992             this.updateText(opt.msg);
10993             if(opt.cls){
10994                 d.el.addClass(opt.cls);
10995             }
10996             d.proxyDrag = opt.proxyDrag === true;
10997             d.modal = opt.modal !== false;
10998             d.mask = opt.modal !== false ? mask : false;
10999             if(!d.isVisible()){
11000                 // force it to the end of the z-index stack so it gets a cursor in FF
11001                 document.body.appendChild(dlg.el.dom);
11002                 d.animateTarget = null;
11003                 d.show(options.animEl);
11004             }
11005             return this;
11006         },
11007
11008         /**
11009          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
11010          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11011          * and closing the message box when the process is complete.
11012          * @param {String} title The title bar text
11013          * @param {String} msg The message box body text
11014          * @return {Roo.MessageBox} This message box
11015          */
11016         progress : function(title, msg){
11017             this.show({
11018                 title : title,
11019                 msg : msg,
11020                 buttons: false,
11021                 progress:true,
11022                 closable:false,
11023                 minWidth: this.minProgressWidth,
11024                 modal : true
11025             });
11026             return this;
11027         },
11028
11029         /**
11030          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11031          * If a callback function is passed it will be called after the user clicks the button, and the
11032          * id of the button that was clicked will be passed as the only parameter to the callback
11033          * (could also be the top-right close button).
11034          * @param {String} title The title bar text
11035          * @param {String} msg The message box body text
11036          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11037          * @param {Object} scope (optional) The scope of the callback function
11038          * @return {Roo.MessageBox} This message box
11039          */
11040         alert : function(title, msg, fn, scope){
11041             this.show({
11042                 title : title,
11043                 msg : msg,
11044                 buttons: this.OK,
11045                 fn: fn,
11046                 scope : scope,
11047                 modal : true
11048             });
11049             return this;
11050         },
11051
11052         /**
11053          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11054          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11055          * You are responsible for closing the message box when the process is complete.
11056          * @param {String} msg The message box body text
11057          * @param {String} title (optional) The title bar text
11058          * @return {Roo.MessageBox} This message box
11059          */
11060         wait : function(msg, title){
11061             this.show({
11062                 title : title,
11063                 msg : msg,
11064                 buttons: false,
11065                 closable:false,
11066                 progress:true,
11067                 modal:true,
11068                 width:300,
11069                 wait:true
11070             });
11071             waitTimer = Roo.TaskMgr.start({
11072                 run: function(i){
11073                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11074                 },
11075                 interval: 1000
11076             });
11077             return this;
11078         },
11079
11080         /**
11081          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11082          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11083          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11084          * @param {String} title The title bar text
11085          * @param {String} msg The message box body text
11086          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11087          * @param {Object} scope (optional) The scope of the callback function
11088          * @return {Roo.MessageBox} This message box
11089          */
11090         confirm : function(title, msg, fn, scope){
11091             this.show({
11092                 title : title,
11093                 msg : msg,
11094                 buttons: this.YESNO,
11095                 fn: fn,
11096                 scope : scope,
11097                 modal : true
11098             });
11099             return this;
11100         },
11101
11102         /**
11103          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11104          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11105          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11106          * (could also be the top-right close button) and the text that was entered will be passed as the two
11107          * parameters to the callback.
11108          * @param {String} title The title bar text
11109          * @param {String} msg The message box body text
11110          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11111          * @param {Object} scope (optional) The scope of the callback function
11112          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11113          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11114          * @return {Roo.MessageBox} This message box
11115          */
11116         prompt : function(title, msg, fn, scope, multiline){
11117             this.show({
11118                 title : title,
11119                 msg : msg,
11120                 buttons: this.OKCANCEL,
11121                 fn: fn,
11122                 minWidth:250,
11123                 scope : scope,
11124                 prompt:true,
11125                 multiline: multiline,
11126                 modal : true
11127             });
11128             return this;
11129         },
11130
11131         /**
11132          * Button config that displays a single OK button
11133          * @type Object
11134          */
11135         OK : {ok:true},
11136         /**
11137          * Button config that displays Yes and No buttons
11138          * @type Object
11139          */
11140         YESNO : {yes:true, no:true},
11141         /**
11142          * Button config that displays OK and Cancel buttons
11143          * @type Object
11144          */
11145         OKCANCEL : {ok:true, cancel:true},
11146         /**
11147          * Button config that displays Yes, No and Cancel buttons
11148          * @type Object
11149          */
11150         YESNOCANCEL : {yes:true, no:true, cancel:true},
11151
11152         /**
11153          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11154          * @type Number
11155          */
11156         defaultTextHeight : 75,
11157         /**
11158          * The maximum width in pixels of the message box (defaults to 600)
11159          * @type Number
11160          */
11161         maxWidth : 600,
11162         /**
11163          * The minimum width in pixels of the message box (defaults to 100)
11164          * @type Number
11165          */
11166         minWidth : 100,
11167         /**
11168          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11169          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11170          * @type Number
11171          */
11172         minProgressWidth : 250,
11173         /**
11174          * An object containing the default button text strings that can be overriden for localized language support.
11175          * Supported properties are: ok, cancel, yes and no.
11176          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11177          * @type Object
11178          */
11179         buttonText : {
11180             ok : "OK",
11181             cancel : "Cancel",
11182             yes : "Yes",
11183             no : "No"
11184         }
11185     };
11186 }();
11187
11188 /**
11189  * Shorthand for {@link Roo.MessageBox}
11190  */
11191 Roo.Msg = Roo.MessageBox;/*
11192  * Based on:
11193  * Ext JS Library 1.1.1
11194  * Copyright(c) 2006-2007, Ext JS, LLC.
11195  *
11196  * Originally Released Under LGPL - original licence link has changed is not relivant.
11197  *
11198  * Fork - LGPL
11199  * <script type="text/javascript">
11200  */
11201 /**
11202  * @class Roo.QuickTips
11203  * Provides attractive and customizable tooltips for any element.
11204  * @singleton
11205  */
11206 Roo.QuickTips = function(){
11207     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11208     var ce, bd, xy, dd;
11209     var visible = false, disabled = true, inited = false;
11210     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11211     
11212     var onOver = function(e){
11213         if(disabled){
11214             return;
11215         }
11216         var t = e.getTarget();
11217         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11218             return;
11219         }
11220         if(ce && t == ce.el){
11221             clearTimeout(hideProc);
11222             return;
11223         }
11224         if(t && tagEls[t.id]){
11225             tagEls[t.id].el = t;
11226             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11227             return;
11228         }
11229         var ttp, et = Roo.fly(t);
11230         var ns = cfg.namespace;
11231         if(tm.interceptTitles && t.title){
11232             ttp = t.title;
11233             t.qtip = ttp;
11234             t.removeAttribute("title");
11235             e.preventDefault();
11236         }else{
11237             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11238         }
11239         if(ttp){
11240             showProc = show.defer(tm.showDelay, tm, [{
11241                 el: t, 
11242                 text: ttp.replace(/\\n/g,'<br/>'),
11243                 width: et.getAttributeNS(ns, cfg.width),
11244                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11245                 title: et.getAttributeNS(ns, cfg.title),
11246                     cls: et.getAttributeNS(ns, cfg.cls)
11247             }]);
11248         }
11249     };
11250     
11251     var onOut = function(e){
11252         clearTimeout(showProc);
11253         var t = e.getTarget();
11254         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11255             hideProc = setTimeout(hide, tm.hideDelay);
11256         }
11257     };
11258     
11259     var onMove = function(e){
11260         if(disabled){
11261             return;
11262         }
11263         xy = e.getXY();
11264         xy[1] += 18;
11265         if(tm.trackMouse && ce){
11266             el.setXY(xy);
11267         }
11268     };
11269     
11270     var onDown = function(e){
11271         clearTimeout(showProc);
11272         clearTimeout(hideProc);
11273         if(!e.within(el)){
11274             if(tm.hideOnClick){
11275                 hide();
11276                 tm.disable();
11277                 tm.enable.defer(100, tm);
11278             }
11279         }
11280     };
11281     
11282     var getPad = function(){
11283         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11284     };
11285
11286     var show = function(o){
11287         if(disabled){
11288             return;
11289         }
11290         clearTimeout(dismissProc);
11291         ce = o;
11292         if(removeCls){ // in case manually hidden
11293             el.removeClass(removeCls);
11294             removeCls = null;
11295         }
11296         if(ce.cls){
11297             el.addClass(ce.cls);
11298             removeCls = ce.cls;
11299         }
11300         if(ce.title){
11301             tipTitle.update(ce.title);
11302             tipTitle.show();
11303         }else{
11304             tipTitle.update('');
11305             tipTitle.hide();
11306         }
11307         el.dom.style.width  = tm.maxWidth+'px';
11308         //tipBody.dom.style.width = '';
11309         tipBodyText.update(o.text);
11310         var p = getPad(), w = ce.width;
11311         if(!w){
11312             var td = tipBodyText.dom;
11313             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11314             if(aw > tm.maxWidth){
11315                 w = tm.maxWidth;
11316             }else if(aw < tm.minWidth){
11317                 w = tm.minWidth;
11318             }else{
11319                 w = aw;
11320             }
11321         }
11322         //tipBody.setWidth(w);
11323         el.setWidth(parseInt(w, 10) + p);
11324         if(ce.autoHide === false){
11325             close.setDisplayed(true);
11326             if(dd){
11327                 dd.unlock();
11328             }
11329         }else{
11330             close.setDisplayed(false);
11331             if(dd){
11332                 dd.lock();
11333             }
11334         }
11335         if(xy){
11336             el.avoidY = xy[1]-18;
11337             el.setXY(xy);
11338         }
11339         if(tm.animate){
11340             el.setOpacity(.1);
11341             el.setStyle("visibility", "visible");
11342             el.fadeIn({callback: afterShow});
11343         }else{
11344             afterShow();
11345         }
11346     };
11347     
11348     var afterShow = function(){
11349         if(ce){
11350             el.show();
11351             esc.enable();
11352             if(tm.autoDismiss && ce.autoHide !== false){
11353                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11354             }
11355         }
11356     };
11357     
11358     var hide = function(noanim){
11359         clearTimeout(dismissProc);
11360         clearTimeout(hideProc);
11361         ce = null;
11362         if(el.isVisible()){
11363             esc.disable();
11364             if(noanim !== true && tm.animate){
11365                 el.fadeOut({callback: afterHide});
11366             }else{
11367                 afterHide();
11368             } 
11369         }
11370     };
11371     
11372     var afterHide = function(){
11373         el.hide();
11374         if(removeCls){
11375             el.removeClass(removeCls);
11376             removeCls = null;
11377         }
11378     };
11379     
11380     return {
11381         /**
11382         * @cfg {Number} minWidth
11383         * The minimum width of the quick tip (defaults to 40)
11384         */
11385        minWidth : 40,
11386         /**
11387         * @cfg {Number} maxWidth
11388         * The maximum width of the quick tip (defaults to 300)
11389         */
11390        maxWidth : 300,
11391         /**
11392         * @cfg {Boolean} interceptTitles
11393         * True to automatically use the element's DOM title value if available (defaults to false)
11394         */
11395        interceptTitles : false,
11396         /**
11397         * @cfg {Boolean} trackMouse
11398         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11399         */
11400        trackMouse : false,
11401         /**
11402         * @cfg {Boolean} hideOnClick
11403         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11404         */
11405        hideOnClick : true,
11406         /**
11407         * @cfg {Number} showDelay
11408         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11409         */
11410        showDelay : 500,
11411         /**
11412         * @cfg {Number} hideDelay
11413         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11414         */
11415        hideDelay : 200,
11416         /**
11417         * @cfg {Boolean} autoHide
11418         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11419         * Used in conjunction with hideDelay.
11420         */
11421        autoHide : true,
11422         /**
11423         * @cfg {Boolean}
11424         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11425         * (defaults to true).  Used in conjunction with autoDismissDelay.
11426         */
11427        autoDismiss : true,
11428         /**
11429         * @cfg {Number}
11430         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11431         */
11432        autoDismissDelay : 5000,
11433        /**
11434         * @cfg {Boolean} animate
11435         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11436         */
11437        animate : false,
11438
11439        /**
11440         * @cfg {String} title
11441         * Title text to display (defaults to '').  This can be any valid HTML markup.
11442         */
11443         title: '',
11444        /**
11445         * @cfg {String} text
11446         * Body text to display (defaults to '').  This can be any valid HTML markup.
11447         */
11448         text : '',
11449        /**
11450         * @cfg {String} cls
11451         * A CSS class to apply to the base quick tip element (defaults to '').
11452         */
11453         cls : '',
11454        /**
11455         * @cfg {Number} width
11456         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11457         * minWidth or maxWidth.
11458         */
11459         width : null,
11460
11461     /**
11462      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11463      * or display QuickTips in a page.
11464      */
11465        init : function(){
11466           tm = Roo.QuickTips;
11467           cfg = tm.tagConfig;
11468           if(!inited){
11469               if(!Roo.isReady){ // allow calling of init() before onReady
11470                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11471                   return;
11472               }
11473               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11474               el.fxDefaults = {stopFx: true};
11475               // maximum custom styling
11476               //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>');
11477               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>');              
11478               tipTitle = el.child('h3');
11479               tipTitle.enableDisplayMode("block");
11480               tipBody = el.child('div.x-tip-bd');
11481               tipBodyText = el.child('div.x-tip-bd-inner');
11482               //bdLeft = el.child('div.x-tip-bd-left');
11483               //bdRight = el.child('div.x-tip-bd-right');
11484               close = el.child('div.x-tip-close');
11485               close.enableDisplayMode("block");
11486               close.on("click", hide);
11487               var d = Roo.get(document);
11488               d.on("mousedown", onDown);
11489               d.on("mouseover", onOver);
11490               d.on("mouseout", onOut);
11491               d.on("mousemove", onMove);
11492               esc = d.addKeyListener(27, hide);
11493               esc.disable();
11494               if(Roo.dd.DD){
11495                   dd = el.initDD("default", null, {
11496                       onDrag : function(){
11497                           el.sync();  
11498                       }
11499                   });
11500                   dd.setHandleElId(tipTitle.id);
11501                   dd.lock();
11502               }
11503               inited = true;
11504           }
11505           this.enable(); 
11506        },
11507
11508     /**
11509      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11510      * are supported:
11511      * <pre>
11512 Property    Type                   Description
11513 ----------  ---------------------  ------------------------------------------------------------------------
11514 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11515      * </ul>
11516      * @param {Object} config The config object
11517      */
11518        register : function(config){
11519            var cs = config instanceof Array ? config : arguments;
11520            for(var i = 0, len = cs.length; i < len; i++) {
11521                var c = cs[i];
11522                var target = c.target;
11523                if(target){
11524                    if(target instanceof Array){
11525                        for(var j = 0, jlen = target.length; j < jlen; j++){
11526                            tagEls[target[j]] = c;
11527                        }
11528                    }else{
11529                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11530                    }
11531                }
11532            }
11533        },
11534
11535     /**
11536      * Removes this quick tip from its element and destroys it.
11537      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11538      */
11539        unregister : function(el){
11540            delete tagEls[Roo.id(el)];
11541        },
11542
11543     /**
11544      * Enable this quick tip.
11545      */
11546        enable : function(){
11547            if(inited && disabled){
11548                locks.pop();
11549                if(locks.length < 1){
11550                    disabled = false;
11551                }
11552            }
11553        },
11554
11555     /**
11556      * Disable this quick tip.
11557      */
11558        disable : function(){
11559           disabled = true;
11560           clearTimeout(showProc);
11561           clearTimeout(hideProc);
11562           clearTimeout(dismissProc);
11563           if(ce){
11564               hide(true);
11565           }
11566           locks.push(1);
11567        },
11568
11569     /**
11570      * Returns true if the quick tip is enabled, else false.
11571      */
11572        isEnabled : function(){
11573             return !disabled;
11574        },
11575
11576         // private
11577        tagConfig : {
11578            namespace : "roo", // was ext?? this may break..
11579            alt_namespace : "ext",
11580            attribute : "qtip",
11581            width : "width",
11582            target : "target",
11583            title : "qtitle",
11584            hide : "hide",
11585            cls : "qclass"
11586        }
11587    };
11588 }();
11589
11590 // backwards compat
11591 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11592  * Based on:
11593  * Ext JS Library 1.1.1
11594  * Copyright(c) 2006-2007, Ext JS, LLC.
11595  *
11596  * Originally Released Under LGPL - original licence link has changed is not relivant.
11597  *
11598  * Fork - LGPL
11599  * <script type="text/javascript">
11600  */
11601  
11602
11603 /**
11604  * @class Roo.tree.TreePanel
11605  * @extends Roo.data.Tree
11606
11607  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11608  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11609  * @cfg {Boolean} enableDD true to enable drag and drop
11610  * @cfg {Boolean} enableDrag true to enable just drag
11611  * @cfg {Boolean} enableDrop true to enable just drop
11612  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11613  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11614  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11615  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11616  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11617  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11618  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11619  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11620  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11621  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11622  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11623  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11624  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11625  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11626  * @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>
11627  * @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>
11628  * 
11629  * @constructor
11630  * @param {String/HTMLElement/Element} el The container element
11631  * @param {Object} config
11632  */
11633 Roo.tree.TreePanel = function(el, config){
11634     var root = false;
11635     var loader = false;
11636     if (config.root) {
11637         root = config.root;
11638         delete config.root;
11639     }
11640     if (config.loader) {
11641         loader = config.loader;
11642         delete config.loader;
11643     }
11644     
11645     Roo.apply(this, config);
11646     Roo.tree.TreePanel.superclass.constructor.call(this);
11647     this.el = Roo.get(el);
11648     this.el.addClass('x-tree');
11649     //console.log(root);
11650     if (root) {
11651         this.setRootNode( Roo.factory(root, Roo.tree));
11652     }
11653     if (loader) {
11654         this.loader = Roo.factory(loader, Roo.tree);
11655     }
11656    /**
11657     * Read-only. The id of the container element becomes this TreePanel's id.
11658     */
11659     this.id = this.el.id;
11660     this.addEvents({
11661         /**
11662         * @event beforeload
11663         * Fires before a node is loaded, return false to cancel
11664         * @param {Node} node The node being loaded
11665         */
11666         "beforeload" : true,
11667         /**
11668         * @event load
11669         * Fires when a node is loaded
11670         * @param {Node} node The node that was loaded
11671         */
11672         "load" : true,
11673         /**
11674         * @event textchange
11675         * Fires when the text for a node is changed
11676         * @param {Node} node The node
11677         * @param {String} text The new text
11678         * @param {String} oldText The old text
11679         */
11680         "textchange" : true,
11681         /**
11682         * @event beforeexpand
11683         * Fires before a node is expanded, return false to cancel.
11684         * @param {Node} node The node
11685         * @param {Boolean} deep
11686         * @param {Boolean} anim
11687         */
11688         "beforeexpand" : true,
11689         /**
11690         * @event beforecollapse
11691         * Fires before a node is collapsed, return false to cancel.
11692         * @param {Node} node The node
11693         * @param {Boolean} deep
11694         * @param {Boolean} anim
11695         */
11696         "beforecollapse" : true,
11697         /**
11698         * @event expand
11699         * Fires when a node is expanded
11700         * @param {Node} node The node
11701         */
11702         "expand" : true,
11703         /**
11704         * @event disabledchange
11705         * Fires when the disabled status of a node changes
11706         * @param {Node} node The node
11707         * @param {Boolean} disabled
11708         */
11709         "disabledchange" : true,
11710         /**
11711         * @event collapse
11712         * Fires when a node is collapsed
11713         * @param {Node} node The node
11714         */
11715         "collapse" : true,
11716         /**
11717         * @event beforeclick
11718         * Fires before click processing on a node. Return false to cancel the default action.
11719         * @param {Node} node The node
11720         * @param {Roo.EventObject} e The event object
11721         */
11722         "beforeclick":true,
11723         /**
11724         * @event checkchange
11725         * Fires when a node with a checkbox's checked property changes
11726         * @param {Node} this This node
11727         * @param {Boolean} checked
11728         */
11729         "checkchange":true,
11730         /**
11731         * @event click
11732         * Fires when a node is clicked
11733         * @param {Node} node The node
11734         * @param {Roo.EventObject} e The event object
11735         */
11736         "click":true,
11737         /**
11738         * @event dblclick
11739         * Fires when a node is double clicked
11740         * @param {Node} node The node
11741         * @param {Roo.EventObject} e The event object
11742         */
11743         "dblclick":true,
11744         /**
11745         * @event contextmenu
11746         * Fires when a node is right clicked
11747         * @param {Node} node The node
11748         * @param {Roo.EventObject} e The event object
11749         */
11750         "contextmenu":true,
11751         /**
11752         * @event beforechildrenrendered
11753         * Fires right before the child nodes for a node are rendered
11754         * @param {Node} node The node
11755         */
11756         "beforechildrenrendered":true,
11757         /**
11758         * @event startdrag
11759         * Fires when a node starts being dragged
11760         * @param {Roo.tree.TreePanel} this
11761         * @param {Roo.tree.TreeNode} node
11762         * @param {event} e The raw browser event
11763         */ 
11764        "startdrag" : true,
11765        /**
11766         * @event enddrag
11767         * Fires when a drag operation is complete
11768         * @param {Roo.tree.TreePanel} this
11769         * @param {Roo.tree.TreeNode} node
11770         * @param {event} e The raw browser event
11771         */
11772        "enddrag" : true,
11773        /**
11774         * @event dragdrop
11775         * Fires when a dragged node is dropped on a valid DD target
11776         * @param {Roo.tree.TreePanel} this
11777         * @param {Roo.tree.TreeNode} node
11778         * @param {DD} dd The dd it was dropped on
11779         * @param {event} e The raw browser event
11780         */
11781        "dragdrop" : true,
11782        /**
11783         * @event beforenodedrop
11784         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11785         * passed to handlers has the following properties:<br />
11786         * <ul style="padding:5px;padding-left:16px;">
11787         * <li>tree - The TreePanel</li>
11788         * <li>target - The node being targeted for the drop</li>
11789         * <li>data - The drag data from the drag source</li>
11790         * <li>point - The point of the drop - append, above or below</li>
11791         * <li>source - The drag source</li>
11792         * <li>rawEvent - Raw mouse event</li>
11793         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11794         * to be inserted by setting them on this object.</li>
11795         * <li>cancel - Set this to true to cancel the drop.</li>
11796         * </ul>
11797         * @param {Object} dropEvent
11798         */
11799        "beforenodedrop" : true,
11800        /**
11801         * @event nodedrop
11802         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11803         * passed to handlers has the following properties:<br />
11804         * <ul style="padding:5px;padding-left:16px;">
11805         * <li>tree - The TreePanel</li>
11806         * <li>target - The node being targeted for the drop</li>
11807         * <li>data - The drag data from the drag source</li>
11808         * <li>point - The point of the drop - append, above or below</li>
11809         * <li>source - The drag source</li>
11810         * <li>rawEvent - Raw mouse event</li>
11811         * <li>dropNode - Dropped node(s).</li>
11812         * </ul>
11813         * @param {Object} dropEvent
11814         */
11815        "nodedrop" : true,
11816         /**
11817         * @event nodedragover
11818         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11819         * passed to handlers has the following properties:<br />
11820         * <ul style="padding:5px;padding-left:16px;">
11821         * <li>tree - The TreePanel</li>
11822         * <li>target - The node being targeted for the drop</li>
11823         * <li>data - The drag data from the drag source</li>
11824         * <li>point - The point of the drop - append, above or below</li>
11825         * <li>source - The drag source</li>
11826         * <li>rawEvent - Raw mouse event</li>
11827         * <li>dropNode - Drop node(s) provided by the source.</li>
11828         * <li>cancel - Set this to true to signal drop not allowed.</li>
11829         * </ul>
11830         * @param {Object} dragOverEvent
11831         */
11832        "nodedragover" : true,
11833        /**
11834         * @event appendnode
11835         * Fires when append node to the tree
11836         * @param {Roo.tree.TreePanel} this
11837         * @param {Roo.tree.TreeNode} node
11838         * @param {Number} index The index of the newly appended node
11839         */
11840        "appendnode" : true
11841         
11842     });
11843     if(this.singleExpand){
11844        this.on("beforeexpand", this.restrictExpand, this);
11845     }
11846     if (this.editor) {
11847         this.editor.tree = this;
11848         this.editor = Roo.factory(this.editor, Roo.tree);
11849     }
11850     
11851     if (this.selModel) {
11852         this.selModel = Roo.factory(this.selModel, Roo.tree);
11853     }
11854    
11855 };
11856 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11857     rootVisible : true,
11858     animate: Roo.enableFx,
11859     lines : true,
11860     enableDD : false,
11861     hlDrop : Roo.enableFx,
11862   
11863     renderer: false,
11864     
11865     rendererTip: false,
11866     // private
11867     restrictExpand : function(node){
11868         var p = node.parentNode;
11869         if(p){
11870             if(p.expandedChild && p.expandedChild.parentNode == p){
11871                 p.expandedChild.collapse();
11872             }
11873             p.expandedChild = node;
11874         }
11875     },
11876
11877     // private override
11878     setRootNode : function(node){
11879         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11880         if(!this.rootVisible){
11881             node.ui = new Roo.tree.RootTreeNodeUI(node);
11882         }
11883         return node;
11884     },
11885
11886     /**
11887      * Returns the container element for this TreePanel
11888      */
11889     getEl : function(){
11890         return this.el;
11891     },
11892
11893     /**
11894      * Returns the default TreeLoader for this TreePanel
11895      */
11896     getLoader : function(){
11897         return this.loader;
11898     },
11899
11900     /**
11901      * Expand all nodes
11902      */
11903     expandAll : function(){
11904         this.root.expand(true);
11905     },
11906
11907     /**
11908      * Collapse all nodes
11909      */
11910     collapseAll : function(){
11911         this.root.collapse(true);
11912     },
11913
11914     /**
11915      * Returns the selection model used by this TreePanel
11916      */
11917     getSelectionModel : function(){
11918         if(!this.selModel){
11919             this.selModel = new Roo.tree.DefaultSelectionModel();
11920         }
11921         return this.selModel;
11922     },
11923
11924     /**
11925      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11926      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11927      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11928      * @return {Array}
11929      */
11930     getChecked : function(a, startNode){
11931         startNode = startNode || this.root;
11932         var r = [];
11933         var f = function(){
11934             if(this.attributes.checked){
11935                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11936             }
11937         }
11938         startNode.cascade(f);
11939         return r;
11940     },
11941
11942     /**
11943      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11944      * @param {String} path
11945      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11946      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11947      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11948      */
11949     expandPath : function(path, attr, callback){
11950         attr = attr || "id";
11951         var keys = path.split(this.pathSeparator);
11952         var curNode = this.root;
11953         if(curNode.attributes[attr] != keys[1]){ // invalid root
11954             if(callback){
11955                 callback(false, null);
11956             }
11957             return;
11958         }
11959         var index = 1;
11960         var f = function(){
11961             if(++index == keys.length){
11962                 if(callback){
11963                     callback(true, curNode);
11964                 }
11965                 return;
11966             }
11967             var c = curNode.findChild(attr, keys[index]);
11968             if(!c){
11969                 if(callback){
11970                     callback(false, curNode);
11971                 }
11972                 return;
11973             }
11974             curNode = c;
11975             c.expand(false, false, f);
11976         };
11977         curNode.expand(false, false, f);
11978     },
11979
11980     /**
11981      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11982      * @param {String} path
11983      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11984      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11985      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11986      */
11987     selectPath : function(path, attr, callback){
11988         attr = attr || "id";
11989         var keys = path.split(this.pathSeparator);
11990         var v = keys.pop();
11991         if(keys.length > 0){
11992             var f = function(success, node){
11993                 if(success && node){
11994                     var n = node.findChild(attr, v);
11995                     if(n){
11996                         n.select();
11997                         if(callback){
11998                             callback(true, n);
11999                         }
12000                     }else if(callback){
12001                         callback(false, n);
12002                     }
12003                 }else{
12004                     if(callback){
12005                         callback(false, n);
12006                     }
12007                 }
12008             };
12009             this.expandPath(keys.join(this.pathSeparator), attr, f);
12010         }else{
12011             this.root.select();
12012             if(callback){
12013                 callback(true, this.root);
12014             }
12015         }
12016     },
12017
12018     getTreeEl : function(){
12019         return this.el;
12020     },
12021
12022     /**
12023      * Trigger rendering of this TreePanel
12024      */
12025     render : function(){
12026         if (this.innerCt) {
12027             return this; // stop it rendering more than once!!
12028         }
12029         
12030         this.innerCt = this.el.createChild({tag:"ul",
12031                cls:"x-tree-root-ct " +
12032                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12033
12034         if(this.containerScroll){
12035             Roo.dd.ScrollManager.register(this.el);
12036         }
12037         if((this.enableDD || this.enableDrop) && !this.dropZone){
12038            /**
12039             * The dropZone used by this tree if drop is enabled
12040             * @type Roo.tree.TreeDropZone
12041             */
12042              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12043                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12044            });
12045         }
12046         if((this.enableDD || this.enableDrag) && !this.dragZone){
12047            /**
12048             * The dragZone used by this tree if drag is enabled
12049             * @type Roo.tree.TreeDragZone
12050             */
12051             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12052                ddGroup: this.ddGroup || "TreeDD",
12053                scroll: this.ddScroll
12054            });
12055         }
12056         this.getSelectionModel().init(this);
12057         if (!this.root) {
12058             Roo.log("ROOT not set in tree");
12059             return this;
12060         }
12061         this.root.render();
12062         if(!this.rootVisible){
12063             this.root.renderChildren();
12064         }
12065         return this;
12066     }
12067 });/*
12068  * Based on:
12069  * Ext JS Library 1.1.1
12070  * Copyright(c) 2006-2007, Ext JS, LLC.
12071  *
12072  * Originally Released Under LGPL - original licence link has changed is not relivant.
12073  *
12074  * Fork - LGPL
12075  * <script type="text/javascript">
12076  */
12077  
12078
12079 /**
12080  * @class Roo.tree.DefaultSelectionModel
12081  * @extends Roo.util.Observable
12082  * The default single selection for a TreePanel.
12083  * @param {Object} cfg Configuration
12084  */
12085 Roo.tree.DefaultSelectionModel = function(cfg){
12086    this.selNode = null;
12087    
12088    
12089    
12090    this.addEvents({
12091        /**
12092         * @event selectionchange
12093         * Fires when the selected node changes
12094         * @param {DefaultSelectionModel} this
12095         * @param {TreeNode} node the new selection
12096         */
12097        "selectionchange" : true,
12098
12099        /**
12100         * @event beforeselect
12101         * Fires before the selected node changes, return false to cancel the change
12102         * @param {DefaultSelectionModel} this
12103         * @param {TreeNode} node the new selection
12104         * @param {TreeNode} node the old selection
12105         */
12106        "beforeselect" : true
12107    });
12108    
12109     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12110 };
12111
12112 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12113     init : function(tree){
12114         this.tree = tree;
12115         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12116         tree.on("click", this.onNodeClick, this);
12117     },
12118     
12119     onNodeClick : function(node, e){
12120         if (e.ctrlKey && this.selNode == node)  {
12121             this.unselect(node);
12122             return;
12123         }
12124         this.select(node);
12125     },
12126     
12127     /**
12128      * Select a node.
12129      * @param {TreeNode} node The node to select
12130      * @return {TreeNode} The selected node
12131      */
12132     select : function(node){
12133         var last = this.selNode;
12134         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12135             if(last){
12136                 last.ui.onSelectedChange(false);
12137             }
12138             this.selNode = node;
12139             node.ui.onSelectedChange(true);
12140             this.fireEvent("selectionchange", this, node, last);
12141         }
12142         return node;
12143     },
12144     
12145     /**
12146      * Deselect a node.
12147      * @param {TreeNode} node The node to unselect
12148      */
12149     unselect : function(node){
12150         if(this.selNode == node){
12151             this.clearSelections();
12152         }    
12153     },
12154     
12155     /**
12156      * Clear all selections
12157      */
12158     clearSelections : function(){
12159         var n = this.selNode;
12160         if(n){
12161             n.ui.onSelectedChange(false);
12162             this.selNode = null;
12163             this.fireEvent("selectionchange", this, null);
12164         }
12165         return n;
12166     },
12167     
12168     /**
12169      * Get the selected node
12170      * @return {TreeNode} The selected node
12171      */
12172     getSelectedNode : function(){
12173         return this.selNode;    
12174     },
12175     
12176     /**
12177      * Returns true if the node is selected
12178      * @param {TreeNode} node The node to check
12179      * @return {Boolean}
12180      */
12181     isSelected : function(node){
12182         return this.selNode == node;  
12183     },
12184
12185     /**
12186      * Selects the node above the selected node in the tree, intelligently walking the nodes
12187      * @return TreeNode The new selection
12188      */
12189     selectPrevious : function(){
12190         var s = this.selNode || this.lastSelNode;
12191         if(!s){
12192             return null;
12193         }
12194         var ps = s.previousSibling;
12195         if(ps){
12196             if(!ps.isExpanded() || ps.childNodes.length < 1){
12197                 return this.select(ps);
12198             } else{
12199                 var lc = ps.lastChild;
12200                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12201                     lc = lc.lastChild;
12202                 }
12203                 return this.select(lc);
12204             }
12205         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12206             return this.select(s.parentNode);
12207         }
12208         return null;
12209     },
12210
12211     /**
12212      * Selects the node above the selected node in the tree, intelligently walking the nodes
12213      * @return TreeNode The new selection
12214      */
12215     selectNext : function(){
12216         var s = this.selNode || this.lastSelNode;
12217         if(!s){
12218             return null;
12219         }
12220         if(s.firstChild && s.isExpanded()){
12221              return this.select(s.firstChild);
12222          }else if(s.nextSibling){
12223              return this.select(s.nextSibling);
12224          }else if(s.parentNode){
12225             var newS = null;
12226             s.parentNode.bubble(function(){
12227                 if(this.nextSibling){
12228                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12229                     return false;
12230                 }
12231             });
12232             return newS;
12233          }
12234         return null;
12235     },
12236
12237     onKeyDown : function(e){
12238         var s = this.selNode || this.lastSelNode;
12239         // undesirable, but required
12240         var sm = this;
12241         if(!s){
12242             return;
12243         }
12244         var k = e.getKey();
12245         switch(k){
12246              case e.DOWN:
12247                  e.stopEvent();
12248                  this.selectNext();
12249              break;
12250              case e.UP:
12251                  e.stopEvent();
12252                  this.selectPrevious();
12253              break;
12254              case e.RIGHT:
12255                  e.preventDefault();
12256                  if(s.hasChildNodes()){
12257                      if(!s.isExpanded()){
12258                          s.expand();
12259                      }else if(s.firstChild){
12260                          this.select(s.firstChild, e);
12261                      }
12262                  }
12263              break;
12264              case e.LEFT:
12265                  e.preventDefault();
12266                  if(s.hasChildNodes() && s.isExpanded()){
12267                      s.collapse();
12268                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12269                      this.select(s.parentNode, e);
12270                  }
12271              break;
12272         };
12273     }
12274 });
12275
12276 /**
12277  * @class Roo.tree.MultiSelectionModel
12278  * @extends Roo.util.Observable
12279  * Multi selection for a TreePanel.
12280  * @param {Object} cfg Configuration
12281  */
12282 Roo.tree.MultiSelectionModel = function(){
12283    this.selNodes = [];
12284    this.selMap = {};
12285    this.addEvents({
12286        /**
12287         * @event selectionchange
12288         * Fires when the selected nodes change
12289         * @param {MultiSelectionModel} this
12290         * @param {Array} nodes Array of the selected nodes
12291         */
12292        "selectionchange" : true
12293    });
12294    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12295    
12296 };
12297
12298 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12299     init : function(tree){
12300         this.tree = tree;
12301         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12302         tree.on("click", this.onNodeClick, this);
12303     },
12304     
12305     onNodeClick : function(node, e){
12306         this.select(node, e, e.ctrlKey);
12307     },
12308     
12309     /**
12310      * Select a node.
12311      * @param {TreeNode} node The node to select
12312      * @param {EventObject} e (optional) An event associated with the selection
12313      * @param {Boolean} keepExisting True to retain existing selections
12314      * @return {TreeNode} The selected node
12315      */
12316     select : function(node, e, keepExisting){
12317         if(keepExisting !== true){
12318             this.clearSelections(true);
12319         }
12320         if(this.isSelected(node)){
12321             this.lastSelNode = node;
12322             return node;
12323         }
12324         this.selNodes.push(node);
12325         this.selMap[node.id] = node;
12326         this.lastSelNode = node;
12327         node.ui.onSelectedChange(true);
12328         this.fireEvent("selectionchange", this, this.selNodes);
12329         return node;
12330     },
12331     
12332     /**
12333      * Deselect a node.
12334      * @param {TreeNode} node The node to unselect
12335      */
12336     unselect : function(node){
12337         if(this.selMap[node.id]){
12338             node.ui.onSelectedChange(false);
12339             var sn = this.selNodes;
12340             var index = -1;
12341             if(sn.indexOf){
12342                 index = sn.indexOf(node);
12343             }else{
12344                 for(var i = 0, len = sn.length; i < len; i++){
12345                     if(sn[i] == node){
12346                         index = i;
12347                         break;
12348                     }
12349                 }
12350             }
12351             if(index != -1){
12352                 this.selNodes.splice(index, 1);
12353             }
12354             delete this.selMap[node.id];
12355             this.fireEvent("selectionchange", this, this.selNodes);
12356         }
12357     },
12358     
12359     /**
12360      * Clear all selections
12361      */
12362     clearSelections : function(suppressEvent){
12363         var sn = this.selNodes;
12364         if(sn.length > 0){
12365             for(var i = 0, len = sn.length; i < len; i++){
12366                 sn[i].ui.onSelectedChange(false);
12367             }
12368             this.selNodes = [];
12369             this.selMap = {};
12370             if(suppressEvent !== true){
12371                 this.fireEvent("selectionchange", this, this.selNodes);
12372             }
12373         }
12374     },
12375     
12376     /**
12377      * Returns true if the node is selected
12378      * @param {TreeNode} node The node to check
12379      * @return {Boolean}
12380      */
12381     isSelected : function(node){
12382         return this.selMap[node.id] ? true : false;  
12383     },
12384     
12385     /**
12386      * Returns an array of the selected nodes
12387      * @return {Array}
12388      */
12389     getSelectedNodes : function(){
12390         return this.selNodes;    
12391     },
12392
12393     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12394
12395     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12396
12397     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12398 });/*
12399  * Based on:
12400  * Ext JS Library 1.1.1
12401  * Copyright(c) 2006-2007, Ext JS, LLC.
12402  *
12403  * Originally Released Under LGPL - original licence link has changed is not relivant.
12404  *
12405  * Fork - LGPL
12406  * <script type="text/javascript">
12407  */
12408  
12409 /**
12410  * @class Roo.tree.TreeNode
12411  * @extends Roo.data.Node
12412  * @cfg {String} text The text for this node
12413  * @cfg {Boolean} expanded true to start the node expanded
12414  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12415  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12416  * @cfg {Boolean} disabled true to start the node disabled
12417  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12418  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12419  * @cfg {String} cls A css class to be added to the node
12420  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12421  * @cfg {String} href URL of the link used for the node (defaults to #)
12422  * @cfg {String} hrefTarget target frame for the link
12423  * @cfg {String} qtip An Ext QuickTip for the node
12424  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12425  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12426  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12427  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12428  * (defaults to undefined with no checkbox rendered)
12429  * @constructor
12430  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12431  */
12432 Roo.tree.TreeNode = function(attributes){
12433     attributes = attributes || {};
12434     if(typeof attributes == "string"){
12435         attributes = {text: attributes};
12436     }
12437     this.childrenRendered = false;
12438     this.rendered = false;
12439     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12440     this.expanded = attributes.expanded === true;
12441     this.isTarget = attributes.isTarget !== false;
12442     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12443     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12444
12445     /**
12446      * Read-only. The text for this node. To change it use setText().
12447      * @type String
12448      */
12449     this.text = attributes.text;
12450     /**
12451      * True if this node is disabled.
12452      * @type Boolean
12453      */
12454     this.disabled = attributes.disabled === true;
12455
12456     this.addEvents({
12457         /**
12458         * @event textchange
12459         * Fires when the text for this node is changed
12460         * @param {Node} this This node
12461         * @param {String} text The new text
12462         * @param {String} oldText The old text
12463         */
12464         "textchange" : true,
12465         /**
12466         * @event beforeexpand
12467         * Fires before this node is expanded, return false to cancel.
12468         * @param {Node} this This node
12469         * @param {Boolean} deep
12470         * @param {Boolean} anim
12471         */
12472         "beforeexpand" : true,
12473         /**
12474         * @event beforecollapse
12475         * Fires before this node is collapsed, return false to cancel.
12476         * @param {Node} this This node
12477         * @param {Boolean} deep
12478         * @param {Boolean} anim
12479         */
12480         "beforecollapse" : true,
12481         /**
12482         * @event expand
12483         * Fires when this node is expanded
12484         * @param {Node} this This node
12485         */
12486         "expand" : true,
12487         /**
12488         * @event disabledchange
12489         * Fires when the disabled status of this node changes
12490         * @param {Node} this This node
12491         * @param {Boolean} disabled
12492         */
12493         "disabledchange" : true,
12494         /**
12495         * @event collapse
12496         * Fires when this node is collapsed
12497         * @param {Node} this This node
12498         */
12499         "collapse" : true,
12500         /**
12501         * @event beforeclick
12502         * Fires before click processing. Return false to cancel the default action.
12503         * @param {Node} this This node
12504         * @param {Roo.EventObject} e The event object
12505         */
12506         "beforeclick":true,
12507         /**
12508         * @event checkchange
12509         * Fires when a node with a checkbox's checked property changes
12510         * @param {Node} this This node
12511         * @param {Boolean} checked
12512         */
12513         "checkchange":true,
12514         /**
12515         * @event click
12516         * Fires when this node is clicked
12517         * @param {Node} this This node
12518         * @param {Roo.EventObject} e The event object
12519         */
12520         "click":true,
12521         /**
12522         * @event dblclick
12523         * Fires when this node is double clicked
12524         * @param {Node} this This node
12525         * @param {Roo.EventObject} e The event object
12526         */
12527         "dblclick":true,
12528         /**
12529         * @event contextmenu
12530         * Fires when this node is right clicked
12531         * @param {Node} this This node
12532         * @param {Roo.EventObject} e The event object
12533         */
12534         "contextmenu":true,
12535         /**
12536         * @event beforechildrenrendered
12537         * Fires right before the child nodes for this node are rendered
12538         * @param {Node} this This node
12539         */
12540         "beforechildrenrendered":true
12541     });
12542
12543     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12544
12545     /**
12546      * Read-only. The UI for this node
12547      * @type TreeNodeUI
12548      */
12549     this.ui = new uiClass(this);
12550     
12551     // finally support items[]
12552     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12553         return;
12554     }
12555     
12556     
12557     Roo.each(this.attributes.items, function(c) {
12558         this.appendChild(Roo.factory(c,Roo.Tree));
12559     }, this);
12560     delete this.attributes.items;
12561     
12562     
12563     
12564 };
12565 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12566     preventHScroll: true,
12567     /**
12568      * Returns true if this node is expanded
12569      * @return {Boolean}
12570      */
12571     isExpanded : function(){
12572         return this.expanded;
12573     },
12574
12575     /**
12576      * Returns the UI object for this node
12577      * @return {TreeNodeUI}
12578      */
12579     getUI : function(){
12580         return this.ui;
12581     },
12582
12583     // private override
12584     setFirstChild : function(node){
12585         var of = this.firstChild;
12586         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12587         if(this.childrenRendered && of && node != of){
12588             of.renderIndent(true, true);
12589         }
12590         if(this.rendered){
12591             this.renderIndent(true, true);
12592         }
12593     },
12594
12595     // private override
12596     setLastChild : function(node){
12597         var ol = this.lastChild;
12598         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12599         if(this.childrenRendered && ol && node != ol){
12600             ol.renderIndent(true, true);
12601         }
12602         if(this.rendered){
12603             this.renderIndent(true, true);
12604         }
12605     },
12606
12607     // these methods are overridden to provide lazy rendering support
12608     // private override
12609     appendChild : function()
12610     {
12611         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12612         if(node && this.childrenRendered){
12613             node.render();
12614         }
12615         this.ui.updateExpandIcon();
12616         return node;
12617     },
12618
12619     // private override
12620     removeChild : function(node){
12621         this.ownerTree.getSelectionModel().unselect(node);
12622         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12623         // if it's been rendered remove dom node
12624         if(this.childrenRendered){
12625             node.ui.remove();
12626         }
12627         if(this.childNodes.length < 1){
12628             this.collapse(false, false);
12629         }else{
12630             this.ui.updateExpandIcon();
12631         }
12632         if(!this.firstChild) {
12633             this.childrenRendered = false;
12634         }
12635         return node;
12636     },
12637
12638     // private override
12639     insertBefore : function(node, refNode){
12640         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12641         if(newNode && refNode && this.childrenRendered){
12642             node.render();
12643         }
12644         this.ui.updateExpandIcon();
12645         return newNode;
12646     },
12647
12648     /**
12649      * Sets the text for this node
12650      * @param {String} text
12651      */
12652     setText : function(text){
12653         var oldText = this.text;
12654         this.text = text;
12655         this.attributes.text = text;
12656         if(this.rendered){ // event without subscribing
12657             this.ui.onTextChange(this, text, oldText);
12658         }
12659         this.fireEvent("textchange", this, text, oldText);
12660     },
12661
12662     /**
12663      * Triggers selection of this node
12664      */
12665     select : function(){
12666         this.getOwnerTree().getSelectionModel().select(this);
12667     },
12668
12669     /**
12670      * Triggers deselection of this node
12671      */
12672     unselect : function(){
12673         this.getOwnerTree().getSelectionModel().unselect(this);
12674     },
12675
12676     /**
12677      * Returns true if this node is selected
12678      * @return {Boolean}
12679      */
12680     isSelected : function(){
12681         return this.getOwnerTree().getSelectionModel().isSelected(this);
12682     },
12683
12684     /**
12685      * Expand this node.
12686      * @param {Boolean} deep (optional) True to expand all children as well
12687      * @param {Boolean} anim (optional) false to cancel the default animation
12688      * @param {Function} callback (optional) A callback to be called when
12689      * expanding this node completes (does not wait for deep expand to complete).
12690      * Called with 1 parameter, this node.
12691      */
12692     expand : function(deep, anim, callback){
12693         if(!this.expanded){
12694             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12695                 return;
12696             }
12697             if(!this.childrenRendered){
12698                 this.renderChildren();
12699             }
12700             this.expanded = true;
12701             
12702             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12703                 this.ui.animExpand(function(){
12704                     this.fireEvent("expand", this);
12705                     if(typeof callback == "function"){
12706                         callback(this);
12707                     }
12708                     if(deep === true){
12709                         this.expandChildNodes(true);
12710                     }
12711                 }.createDelegate(this));
12712                 return;
12713             }else{
12714                 this.ui.expand();
12715                 this.fireEvent("expand", this);
12716                 if(typeof callback == "function"){
12717                     callback(this);
12718                 }
12719             }
12720         }else{
12721            if(typeof callback == "function"){
12722                callback(this);
12723            }
12724         }
12725         if(deep === true){
12726             this.expandChildNodes(true);
12727         }
12728     },
12729
12730     isHiddenRoot : function(){
12731         return this.isRoot && !this.getOwnerTree().rootVisible;
12732     },
12733
12734     /**
12735      * Collapse this node.
12736      * @param {Boolean} deep (optional) True to collapse all children as well
12737      * @param {Boolean} anim (optional) false to cancel the default animation
12738      */
12739     collapse : function(deep, anim){
12740         if(this.expanded && !this.isHiddenRoot()){
12741             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12742                 return;
12743             }
12744             this.expanded = false;
12745             if((this.getOwnerTree().animate && anim !== false) || anim){
12746                 this.ui.animCollapse(function(){
12747                     this.fireEvent("collapse", this);
12748                     if(deep === true){
12749                         this.collapseChildNodes(true);
12750                     }
12751                 }.createDelegate(this));
12752                 return;
12753             }else{
12754                 this.ui.collapse();
12755                 this.fireEvent("collapse", this);
12756             }
12757         }
12758         if(deep === true){
12759             var cs = this.childNodes;
12760             for(var i = 0, len = cs.length; i < len; i++) {
12761                 cs[i].collapse(true, false);
12762             }
12763         }
12764     },
12765
12766     // private
12767     delayedExpand : function(delay){
12768         if(!this.expandProcId){
12769             this.expandProcId = this.expand.defer(delay, this);
12770         }
12771     },
12772
12773     // private
12774     cancelExpand : function(){
12775         if(this.expandProcId){
12776             clearTimeout(this.expandProcId);
12777         }
12778         this.expandProcId = false;
12779     },
12780
12781     /**
12782      * Toggles expanded/collapsed state of the node
12783      */
12784     toggle : function(){
12785         if(this.expanded){
12786             this.collapse();
12787         }else{
12788             this.expand();
12789         }
12790     },
12791
12792     /**
12793      * Ensures all parent nodes are expanded
12794      */
12795     ensureVisible : function(callback){
12796         var tree = this.getOwnerTree();
12797         tree.expandPath(this.parentNode.getPath(), false, function(){
12798             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12799             Roo.callback(callback);
12800         }.createDelegate(this));
12801     },
12802
12803     /**
12804      * Expand all child nodes
12805      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12806      */
12807     expandChildNodes : function(deep){
12808         var cs = this.childNodes;
12809         for(var i = 0, len = cs.length; i < len; i++) {
12810                 cs[i].expand(deep);
12811         }
12812     },
12813
12814     /**
12815      * Collapse all child nodes
12816      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12817      */
12818     collapseChildNodes : function(deep){
12819         var cs = this.childNodes;
12820         for(var i = 0, len = cs.length; i < len; i++) {
12821                 cs[i].collapse(deep);
12822         }
12823     },
12824
12825     /**
12826      * Disables this node
12827      */
12828     disable : function(){
12829         this.disabled = true;
12830         this.unselect();
12831         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12832             this.ui.onDisableChange(this, true);
12833         }
12834         this.fireEvent("disabledchange", this, true);
12835     },
12836
12837     /**
12838      * Enables this node
12839      */
12840     enable : function(){
12841         this.disabled = false;
12842         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12843             this.ui.onDisableChange(this, false);
12844         }
12845         this.fireEvent("disabledchange", this, false);
12846     },
12847
12848     // private
12849     renderChildren : function(suppressEvent){
12850         if(suppressEvent !== false){
12851             this.fireEvent("beforechildrenrendered", this);
12852         }
12853         var cs = this.childNodes;
12854         for(var i = 0, len = cs.length; i < len; i++){
12855             cs[i].render(true);
12856         }
12857         this.childrenRendered = true;
12858     },
12859
12860     // private
12861     sort : function(fn, scope){
12862         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12863         if(this.childrenRendered){
12864             var cs = this.childNodes;
12865             for(var i = 0, len = cs.length; i < len; i++){
12866                 cs[i].render(true);
12867             }
12868         }
12869     },
12870
12871     // private
12872     render : function(bulkRender){
12873         this.ui.render(bulkRender);
12874         if(!this.rendered){
12875             this.rendered = true;
12876             if(this.expanded){
12877                 this.expanded = false;
12878                 this.expand(false, false);
12879             }
12880         }
12881     },
12882
12883     // private
12884     renderIndent : function(deep, refresh){
12885         if(refresh){
12886             this.ui.childIndent = null;
12887         }
12888         this.ui.renderIndent();
12889         if(deep === true && this.childrenRendered){
12890             var cs = this.childNodes;
12891             for(var i = 0, len = cs.length; i < len; i++){
12892                 cs[i].renderIndent(true, refresh);
12893             }
12894         }
12895     }
12896 });/*
12897  * Based on:
12898  * Ext JS Library 1.1.1
12899  * Copyright(c) 2006-2007, Ext JS, LLC.
12900  *
12901  * Originally Released Under LGPL - original licence link has changed is not relivant.
12902  *
12903  * Fork - LGPL
12904  * <script type="text/javascript">
12905  */
12906  
12907 /**
12908  * @class Roo.tree.AsyncTreeNode
12909  * @extends Roo.tree.TreeNode
12910  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12911  * @constructor
12912  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12913  */
12914  Roo.tree.AsyncTreeNode = function(config){
12915     this.loaded = false;
12916     this.loading = false;
12917     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12918     /**
12919     * @event beforeload
12920     * Fires before this node is loaded, return false to cancel
12921     * @param {Node} this This node
12922     */
12923     this.addEvents({'beforeload':true, 'load': true});
12924     /**
12925     * @event load
12926     * Fires when this node is loaded
12927     * @param {Node} this This node
12928     */
12929     /**
12930      * The loader used by this node (defaults to using the tree's defined loader)
12931      * @type TreeLoader
12932      * @property loader
12933      */
12934 };
12935 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12936     expand : function(deep, anim, callback){
12937         if(this.loading){ // if an async load is already running, waiting til it's done
12938             var timer;
12939             var f = function(){
12940                 if(!this.loading){ // done loading
12941                     clearInterval(timer);
12942                     this.expand(deep, anim, callback);
12943                 }
12944             }.createDelegate(this);
12945             timer = setInterval(f, 200);
12946             return;
12947         }
12948         if(!this.loaded){
12949             if(this.fireEvent("beforeload", this) === false){
12950                 return;
12951             }
12952             this.loading = true;
12953             this.ui.beforeLoad(this);
12954             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12955             if(loader){
12956                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12957                 return;
12958             }
12959         }
12960         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12961     },
12962     
12963     /**
12964      * Returns true if this node is currently loading
12965      * @return {Boolean}
12966      */
12967     isLoading : function(){
12968         return this.loading;  
12969     },
12970     
12971     loadComplete : function(deep, anim, callback){
12972         this.loading = false;
12973         this.loaded = true;
12974         this.ui.afterLoad(this);
12975         this.fireEvent("load", this);
12976         this.expand(deep, anim, callback);
12977     },
12978     
12979     /**
12980      * Returns true if this node has been loaded
12981      * @return {Boolean}
12982      */
12983     isLoaded : function(){
12984         return this.loaded;
12985     },
12986     
12987     hasChildNodes : function(){
12988         if(!this.isLeaf() && !this.loaded){
12989             return true;
12990         }else{
12991             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12992         }
12993     },
12994
12995     /**
12996      * Trigger a reload for this node
12997      * @param {Function} callback
12998      */
12999     reload : function(callback){
13000         this.collapse(false, false);
13001         while(this.firstChild){
13002             this.removeChild(this.firstChild);
13003         }
13004         this.childrenRendered = false;
13005         this.loaded = false;
13006         if(this.isHiddenRoot()){
13007             this.expanded = false;
13008         }
13009         this.expand(false, false, callback);
13010     }
13011 });/*
13012  * Based on:
13013  * Ext JS Library 1.1.1
13014  * Copyright(c) 2006-2007, Ext JS, LLC.
13015  *
13016  * Originally Released Under LGPL - original licence link has changed is not relivant.
13017  *
13018  * Fork - LGPL
13019  * <script type="text/javascript">
13020  */
13021  
13022 /**
13023  * @class Roo.tree.TreeNodeUI
13024  * @constructor
13025  * @param {Object} node The node to render
13026  * The TreeNode UI implementation is separate from the
13027  * tree implementation. Unless you are customizing the tree UI,
13028  * you should never have to use this directly.
13029  */
13030 Roo.tree.TreeNodeUI = function(node){
13031     this.node = node;
13032     this.rendered = false;
13033     this.animating = false;
13034     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13035 };
13036
13037 Roo.tree.TreeNodeUI.prototype = {
13038     removeChild : function(node){
13039         if(this.rendered){
13040             this.ctNode.removeChild(node.ui.getEl());
13041         }
13042     },
13043
13044     beforeLoad : function(){
13045          this.addClass("x-tree-node-loading");
13046     },
13047
13048     afterLoad : function(){
13049          this.removeClass("x-tree-node-loading");
13050     },
13051
13052     onTextChange : function(node, text, oldText){
13053         if(this.rendered){
13054             this.textNode.innerHTML = text;
13055         }
13056     },
13057
13058     onDisableChange : function(node, state){
13059         this.disabled = state;
13060         if(state){
13061             this.addClass("x-tree-node-disabled");
13062         }else{
13063             this.removeClass("x-tree-node-disabled");
13064         }
13065     },
13066
13067     onSelectedChange : function(state){
13068         if(state){
13069             this.focus();
13070             this.addClass("x-tree-selected");
13071         }else{
13072             //this.blur();
13073             this.removeClass("x-tree-selected");
13074         }
13075     },
13076
13077     onMove : function(tree, node, oldParent, newParent, index, refNode){
13078         this.childIndent = null;
13079         if(this.rendered){
13080             var targetNode = newParent.ui.getContainer();
13081             if(!targetNode){//target not rendered
13082                 this.holder = document.createElement("div");
13083                 this.holder.appendChild(this.wrap);
13084                 return;
13085             }
13086             var insertBefore = refNode ? refNode.ui.getEl() : null;
13087             if(insertBefore){
13088                 targetNode.insertBefore(this.wrap, insertBefore);
13089             }else{
13090                 targetNode.appendChild(this.wrap);
13091             }
13092             this.node.renderIndent(true);
13093         }
13094     },
13095
13096     addClass : function(cls){
13097         if(this.elNode){
13098             Roo.fly(this.elNode).addClass(cls);
13099         }
13100     },
13101
13102     removeClass : function(cls){
13103         if(this.elNode){
13104             Roo.fly(this.elNode).removeClass(cls);
13105         }
13106     },
13107
13108     remove : function(){
13109         if(this.rendered){
13110             this.holder = document.createElement("div");
13111             this.holder.appendChild(this.wrap);
13112         }
13113     },
13114
13115     fireEvent : function(){
13116         return this.node.fireEvent.apply(this.node, arguments);
13117     },
13118
13119     initEvents : function(){
13120         this.node.on("move", this.onMove, this);
13121         var E = Roo.EventManager;
13122         var a = this.anchor;
13123
13124         var el = Roo.fly(a, '_treeui');
13125
13126         if(Roo.isOpera){ // opera render bug ignores the CSS
13127             el.setStyle("text-decoration", "none");
13128         }
13129
13130         el.on("click", this.onClick, this);
13131         el.on("dblclick", this.onDblClick, this);
13132
13133         if(this.checkbox){
13134             Roo.EventManager.on(this.checkbox,
13135                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13136         }
13137
13138         el.on("contextmenu", this.onContextMenu, this);
13139
13140         var icon = Roo.fly(this.iconNode);
13141         icon.on("click", this.onClick, this);
13142         icon.on("dblclick", this.onDblClick, this);
13143         icon.on("contextmenu", this.onContextMenu, this);
13144         E.on(this.ecNode, "click", this.ecClick, this, true);
13145
13146         if(this.node.disabled){
13147             this.addClass("x-tree-node-disabled");
13148         }
13149         if(this.node.hidden){
13150             this.addClass("x-tree-node-disabled");
13151         }
13152         var ot = this.node.getOwnerTree();
13153         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
13154         if(dd && (!this.node.isRoot || ot.rootVisible)){
13155             Roo.dd.Registry.register(this.elNode, {
13156                 node: this.node,
13157                 handles: this.getDDHandles(),
13158                 isHandle: false
13159             });
13160         }
13161     },
13162
13163     getDDHandles : function(){
13164         return [this.iconNode, this.textNode];
13165     },
13166
13167     hide : function(){
13168         if(this.rendered){
13169             this.wrap.style.display = "none";
13170         }
13171     },
13172
13173     show : function(){
13174         if(this.rendered){
13175             this.wrap.style.display = "";
13176         }
13177     },
13178
13179     onContextMenu : function(e){
13180         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13181             e.preventDefault();
13182             this.focus();
13183             this.fireEvent("contextmenu", this.node, e);
13184         }
13185     },
13186
13187     onClick : function(e){
13188         if(this.dropping){
13189             e.stopEvent();
13190             return;
13191         }
13192         if(this.fireEvent("beforeclick", this.node, e) !== false){
13193             if(!this.disabled && this.node.attributes.href){
13194                 this.fireEvent("click", this.node, e);
13195                 return;
13196             }
13197             e.preventDefault();
13198             if(this.disabled){
13199                 return;
13200             }
13201
13202             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13203                 this.node.toggle();
13204             }
13205
13206             this.fireEvent("click", this.node, e);
13207         }else{
13208             e.stopEvent();
13209         }
13210     },
13211
13212     onDblClick : function(e){
13213         e.preventDefault();
13214         if(this.disabled){
13215             return;
13216         }
13217         if(this.checkbox){
13218             this.toggleCheck();
13219         }
13220         if(!this.animating && this.node.hasChildNodes()){
13221             this.node.toggle();
13222         }
13223         this.fireEvent("dblclick", this.node, e);
13224     },
13225
13226     onCheckChange : function(){
13227         var checked = this.checkbox.checked;
13228         this.node.attributes.checked = checked;
13229         this.fireEvent('checkchange', this.node, checked);
13230     },
13231
13232     ecClick : function(e){
13233         if(!this.animating && this.node.hasChildNodes()){
13234             this.node.toggle();
13235         }
13236     },
13237
13238     startDrop : function(){
13239         this.dropping = true;
13240     },
13241
13242     // delayed drop so the click event doesn't get fired on a drop
13243     endDrop : function(){
13244        setTimeout(function(){
13245            this.dropping = false;
13246        }.createDelegate(this), 50);
13247     },
13248
13249     expand : function(){
13250         this.updateExpandIcon();
13251         this.ctNode.style.display = "";
13252     },
13253
13254     focus : function(){
13255         if(!this.node.preventHScroll){
13256             try{this.anchor.focus();
13257             }catch(e){}
13258         }else if(!Roo.isIE){
13259             try{
13260                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13261                 var l = noscroll.scrollLeft;
13262                 this.anchor.focus();
13263                 noscroll.scrollLeft = l;
13264             }catch(e){}
13265         }
13266     },
13267
13268     toggleCheck : function(value){
13269         var cb = this.checkbox;
13270         if(cb){
13271             cb.checked = (value === undefined ? !cb.checked : value);
13272         }
13273     },
13274
13275     blur : function(){
13276         try{
13277             this.anchor.blur();
13278         }catch(e){}
13279     },
13280
13281     animExpand : function(callback){
13282         var ct = Roo.get(this.ctNode);
13283         ct.stopFx();
13284         if(!this.node.hasChildNodes()){
13285             this.updateExpandIcon();
13286             this.ctNode.style.display = "";
13287             Roo.callback(callback);
13288             return;
13289         }
13290         this.animating = true;
13291         this.updateExpandIcon();
13292
13293         ct.slideIn('t', {
13294            callback : function(){
13295                this.animating = false;
13296                Roo.callback(callback);
13297             },
13298             scope: this,
13299             duration: this.node.ownerTree.duration || .25
13300         });
13301     },
13302
13303     highlight : function(){
13304         var tree = this.node.getOwnerTree();
13305         Roo.fly(this.wrap).highlight(
13306             tree.hlColor || "C3DAF9",
13307             {endColor: tree.hlBaseColor}
13308         );
13309     },
13310
13311     collapse : function(){
13312         this.updateExpandIcon();
13313         this.ctNode.style.display = "none";
13314     },
13315
13316     animCollapse : function(callback){
13317         var ct = Roo.get(this.ctNode);
13318         ct.enableDisplayMode('block');
13319         ct.stopFx();
13320
13321         this.animating = true;
13322         this.updateExpandIcon();
13323
13324         ct.slideOut('t', {
13325             callback : function(){
13326                this.animating = false;
13327                Roo.callback(callback);
13328             },
13329             scope: this,
13330             duration: this.node.ownerTree.duration || .25
13331         });
13332     },
13333
13334     getContainer : function(){
13335         return this.ctNode;
13336     },
13337
13338     getEl : function(){
13339         return this.wrap;
13340     },
13341
13342     appendDDGhost : function(ghostNode){
13343         ghostNode.appendChild(this.elNode.cloneNode(true));
13344     },
13345
13346     getDDRepairXY : function(){
13347         return Roo.lib.Dom.getXY(this.iconNode);
13348     },
13349
13350     onRender : function(){
13351         this.render();
13352     },
13353
13354     render : function(bulkRender){
13355         var n = this.node, a = n.attributes;
13356         var targetNode = n.parentNode ?
13357               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13358
13359         if(!this.rendered){
13360             this.rendered = true;
13361
13362             this.renderElements(n, a, targetNode, bulkRender);
13363
13364             if(a.qtip){
13365                if(this.textNode.setAttributeNS){
13366                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13367                    if(a.qtipTitle){
13368                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13369                    }
13370                }else{
13371                    this.textNode.setAttribute("ext:qtip", a.qtip);
13372                    if(a.qtipTitle){
13373                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13374                    }
13375                }
13376             }else if(a.qtipCfg){
13377                 a.qtipCfg.target = Roo.id(this.textNode);
13378                 Roo.QuickTips.register(a.qtipCfg);
13379             }
13380             this.initEvents();
13381             if(!this.node.expanded){
13382                 this.updateExpandIcon();
13383             }
13384         }else{
13385             if(bulkRender === true) {
13386                 targetNode.appendChild(this.wrap);
13387             }
13388         }
13389     },
13390
13391     renderElements : function(n, a, targetNode, bulkRender)
13392     {
13393         // add some indent caching, this helps performance when rendering a large tree
13394         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13395         var t = n.getOwnerTree();
13396         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13397         if (typeof(n.attributes.html) != 'undefined') {
13398             txt = n.attributes.html;
13399         }
13400         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13401         var cb = typeof a.checked == 'boolean';
13402         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13403         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13404             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13405             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13406             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13407             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13408             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13409              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13410                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13411             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13412             "</li>"];
13413
13414         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13415             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13416                                 n.nextSibling.ui.getEl(), buf.join(""));
13417         }else{
13418             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13419         }
13420
13421         this.elNode = this.wrap.childNodes[0];
13422         this.ctNode = this.wrap.childNodes[1];
13423         var cs = this.elNode.childNodes;
13424         this.indentNode = cs[0];
13425         this.ecNode = cs[1];
13426         this.iconNode = cs[2];
13427         var index = 3;
13428         if(cb){
13429             this.checkbox = cs[3];
13430             index++;
13431         }
13432         this.anchor = cs[index];
13433         this.textNode = cs[index].firstChild;
13434     },
13435
13436     getAnchor : function(){
13437         return this.anchor;
13438     },
13439
13440     getTextEl : function(){
13441         return this.textNode;
13442     },
13443
13444     getIconEl : function(){
13445         return this.iconNode;
13446     },
13447
13448     isChecked : function(){
13449         return this.checkbox ? this.checkbox.checked : false;
13450     },
13451
13452     updateExpandIcon : function(){
13453         if(this.rendered){
13454             var n = this.node, c1, c2;
13455             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13456             var hasChild = n.hasChildNodes();
13457             if(hasChild){
13458                 if(n.expanded){
13459                     cls += "-minus";
13460                     c1 = "x-tree-node-collapsed";
13461                     c2 = "x-tree-node-expanded";
13462                 }else{
13463                     cls += "-plus";
13464                     c1 = "x-tree-node-expanded";
13465                     c2 = "x-tree-node-collapsed";
13466                 }
13467                 if(this.wasLeaf){
13468                     this.removeClass("x-tree-node-leaf");
13469                     this.wasLeaf = false;
13470                 }
13471                 if(this.c1 != c1 || this.c2 != c2){
13472                     Roo.fly(this.elNode).replaceClass(c1, c2);
13473                     this.c1 = c1; this.c2 = c2;
13474                 }
13475             }else{
13476                 // this changes non-leafs into leafs if they have no children.
13477                 // it's not very rational behaviour..
13478                 
13479                 if(!this.wasLeaf && this.node.leaf){
13480                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13481                     delete this.c1;
13482                     delete this.c2;
13483                     this.wasLeaf = true;
13484                 }
13485             }
13486             var ecc = "x-tree-ec-icon "+cls;
13487             if(this.ecc != ecc){
13488                 this.ecNode.className = ecc;
13489                 this.ecc = ecc;
13490             }
13491         }
13492     },
13493
13494     getChildIndent : function(){
13495         if(!this.childIndent){
13496             var buf = [];
13497             var p = this.node;
13498             while(p){
13499                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13500                     if(!p.isLast()) {
13501                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13502                     } else {
13503                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13504                     }
13505                 }
13506                 p = p.parentNode;
13507             }
13508             this.childIndent = buf.join("");
13509         }
13510         return this.childIndent;
13511     },
13512
13513     renderIndent : function(){
13514         if(this.rendered){
13515             var indent = "";
13516             var p = this.node.parentNode;
13517             if(p){
13518                 indent = p.ui.getChildIndent();
13519             }
13520             if(this.indentMarkup != indent){ // don't rerender if not required
13521                 this.indentNode.innerHTML = indent;
13522                 this.indentMarkup = indent;
13523             }
13524             this.updateExpandIcon();
13525         }
13526     }
13527 };
13528
13529 Roo.tree.RootTreeNodeUI = function(){
13530     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13531 };
13532 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13533     render : function(){
13534         if(!this.rendered){
13535             var targetNode = this.node.ownerTree.innerCt.dom;
13536             this.node.expanded = true;
13537             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13538             this.wrap = this.ctNode = targetNode.firstChild;
13539         }
13540     },
13541     collapse : function(){
13542     },
13543     expand : function(){
13544     }
13545 });/*
13546  * Based on:
13547  * Ext JS Library 1.1.1
13548  * Copyright(c) 2006-2007, Ext JS, LLC.
13549  *
13550  * Originally Released Under LGPL - original licence link has changed is not relivant.
13551  *
13552  * Fork - LGPL
13553  * <script type="text/javascript">
13554  */
13555 /**
13556  * @class Roo.tree.TreeLoader
13557  * @extends Roo.util.Observable
13558  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13559  * nodes from a specified URL. The response must be a javascript Array definition
13560  * who's elements are node definition objects. eg:
13561  * <pre><code>
13562 {  success : true,
13563    data :      [
13564    
13565     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13566     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13567     ]
13568 }
13569
13570
13571 </code></pre>
13572  * <br><br>
13573  * The old style respose with just an array is still supported, but not recommended.
13574  * <br><br>
13575  *
13576  * A server request is sent, and child nodes are loaded only when a node is expanded.
13577  * The loading node's id is passed to the server under the parameter name "node" to
13578  * enable the server to produce the correct child nodes.
13579  * <br><br>
13580  * To pass extra parameters, an event handler may be attached to the "beforeload"
13581  * event, and the parameters specified in the TreeLoader's baseParams property:
13582  * <pre><code>
13583     myTreeLoader.on("beforeload", function(treeLoader, node) {
13584         this.baseParams.category = node.attributes.category;
13585     }, this);
13586     
13587 </code></pre>
13588  *
13589  * This would pass an HTTP parameter called "category" to the server containing
13590  * the value of the Node's "category" attribute.
13591  * @constructor
13592  * Creates a new Treeloader.
13593  * @param {Object} config A config object containing config properties.
13594  */
13595 Roo.tree.TreeLoader = function(config){
13596     this.baseParams = {};
13597     this.requestMethod = "POST";
13598     Roo.apply(this, config);
13599
13600     this.addEvents({
13601     
13602         /**
13603          * @event beforeload
13604          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13605          * @param {Object} This TreeLoader object.
13606          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13607          * @param {Object} callback The callback function specified in the {@link #load} call.
13608          */
13609         beforeload : true,
13610         /**
13611          * @event load
13612          * Fires when the node has been successfuly loaded.
13613          * @param {Object} This TreeLoader object.
13614          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13615          * @param {Object} response The response object containing the data from the server.
13616          */
13617         load : true,
13618         /**
13619          * @event loadexception
13620          * Fires if the network request failed.
13621          * @param {Object} This TreeLoader object.
13622          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13623          * @param {Object} response The response object containing the data from the server.
13624          */
13625         loadexception : true,
13626         /**
13627          * @event create
13628          * Fires before a node is created, enabling you to return custom Node types 
13629          * @param {Object} This TreeLoader object.
13630          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13631          */
13632         create : true
13633     });
13634
13635     Roo.tree.TreeLoader.superclass.constructor.call(this);
13636 };
13637
13638 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13639     /**
13640     * @cfg {String} dataUrl The URL from which to request a Json string which
13641     * specifies an array of node definition object representing the child nodes
13642     * to be loaded.
13643     */
13644     /**
13645     * @cfg {String} requestMethod either GET or POST
13646     * defaults to POST (due to BC)
13647     * to be loaded.
13648     */
13649     /**
13650     * @cfg {Object} baseParams (optional) An object containing properties which
13651     * specify HTTP parameters to be passed to each request for child nodes.
13652     */
13653     /**
13654     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13655     * created by this loader. If the attributes sent by the server have an attribute in this object,
13656     * they take priority.
13657     */
13658     /**
13659     * @cfg {Object} uiProviders (optional) An object containing properties which
13660     * 
13661     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13662     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13663     * <i>uiProvider</i> attribute of a returned child node is a string rather
13664     * than a reference to a TreeNodeUI implementation, this that string value
13665     * is used as a property name in the uiProviders object. You can define the provider named
13666     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13667     */
13668     uiProviders : {},
13669
13670     /**
13671     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13672     * child nodes before loading.
13673     */
13674     clearOnLoad : true,
13675
13676     /**
13677     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13678     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13679     * Grid query { data : [ .....] }
13680     */
13681     
13682     root : false,
13683      /**
13684     * @cfg {String} queryParam (optional) 
13685     * Name of the query as it will be passed on the querystring (defaults to 'node')
13686     * eg. the request will be ?node=[id]
13687     */
13688     
13689     
13690     queryParam: false,
13691     
13692     /**
13693      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13694      * This is called automatically when a node is expanded, but may be used to reload
13695      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13696      * @param {Roo.tree.TreeNode} node
13697      * @param {Function} callback
13698      */
13699     load : function(node, callback){
13700         if(this.clearOnLoad){
13701             while(node.firstChild){
13702                 node.removeChild(node.firstChild);
13703             }
13704         }
13705         if(node.attributes.children){ // preloaded json children
13706             var cs = node.attributes.children;
13707             for(var i = 0, len = cs.length; i < len; i++){
13708                 node.appendChild(this.createNode(cs[i]));
13709             }
13710             if(typeof callback == "function"){
13711                 callback();
13712             }
13713         }else if(this.dataUrl){
13714             this.requestData(node, callback);
13715         }
13716     },
13717
13718     getParams: function(node){
13719         var buf = [], bp = this.baseParams;
13720         for(var key in bp){
13721             if(typeof bp[key] != "function"){
13722                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13723             }
13724         }
13725         var n = this.queryParam === false ? 'node' : this.queryParam;
13726         buf.push(n + "=", encodeURIComponent(node.id));
13727         return buf.join("");
13728     },
13729
13730     requestData : function(node, callback){
13731         if(this.fireEvent("beforeload", this, node, callback) !== false){
13732             this.transId = Roo.Ajax.request({
13733                 method:this.requestMethod,
13734                 url: this.dataUrl||this.url,
13735                 success: this.handleResponse,
13736                 failure: this.handleFailure,
13737                 scope: this,
13738                 argument: {callback: callback, node: node},
13739                 params: this.getParams(node)
13740             });
13741         }else{
13742             // if the load is cancelled, make sure we notify
13743             // the node that we are done
13744             if(typeof callback == "function"){
13745                 callback();
13746             }
13747         }
13748     },
13749
13750     isLoading : function(){
13751         return this.transId ? true : false;
13752     },
13753
13754     abort : function(){
13755         if(this.isLoading()){
13756             Roo.Ajax.abort(this.transId);
13757         }
13758     },
13759
13760     // private
13761     createNode : function(attr)
13762     {
13763         // apply baseAttrs, nice idea Corey!
13764         if(this.baseAttrs){
13765             Roo.applyIf(attr, this.baseAttrs);
13766         }
13767         if(this.applyLoader !== false){
13768             attr.loader = this;
13769         }
13770         // uiProvider = depreciated..
13771         
13772         if(typeof(attr.uiProvider) == 'string'){
13773            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13774                 /**  eval:var:attr */ eval(attr.uiProvider);
13775         }
13776         if(typeof(this.uiProviders['default']) != 'undefined') {
13777             attr.uiProvider = this.uiProviders['default'];
13778         }
13779         
13780         this.fireEvent('create', this, attr);
13781         
13782         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13783         return(attr.leaf ?
13784                         new Roo.tree.TreeNode(attr) :
13785                         new Roo.tree.AsyncTreeNode(attr));
13786     },
13787
13788     processResponse : function(response, node, callback)
13789     {
13790         var json = response.responseText;
13791         try {
13792             
13793             var o = Roo.decode(json);
13794             
13795             if (this.root === false && typeof(o.success) != undefined) {
13796                 this.root = 'data'; // the default behaviour for list like data..
13797                 }
13798                 
13799             if (this.root !== false &&  !o.success) {
13800                 // it's a failure condition.
13801                 var a = response.argument;
13802                 this.fireEvent("loadexception", this, a.node, response);
13803                 Roo.log("Load failed - should have a handler really");
13804                 return;
13805             }
13806             
13807             
13808             
13809             if (this.root !== false) {
13810                  o = o[this.root];
13811             }
13812             
13813             for(var i = 0, len = o.length; i < len; i++){
13814                 var n = this.createNode(o[i]);
13815                 if(n){
13816                     node.appendChild(n);
13817                 }
13818             }
13819             if(typeof callback == "function"){
13820                 callback(this, node);
13821             }
13822         }catch(e){
13823             this.handleFailure(response);
13824         }
13825     },
13826
13827     handleResponse : function(response){
13828         this.transId = false;
13829         var a = response.argument;
13830         this.processResponse(response, a.node, a.callback);
13831         this.fireEvent("load", this, a.node, response);
13832     },
13833
13834     handleFailure : function(response)
13835     {
13836         // should handle failure better..
13837         this.transId = false;
13838         var a = response.argument;
13839         this.fireEvent("loadexception", this, a.node, response);
13840         if(typeof a.callback == "function"){
13841             a.callback(this, a.node);
13842         }
13843     }
13844 });/*
13845  * Based on:
13846  * Ext JS Library 1.1.1
13847  * Copyright(c) 2006-2007, Ext JS, LLC.
13848  *
13849  * Originally Released Under LGPL - original licence link has changed is not relivant.
13850  *
13851  * Fork - LGPL
13852  * <script type="text/javascript">
13853  */
13854
13855 /**
13856 * @class Roo.tree.TreeFilter
13857 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13858 * @param {TreePanel} tree
13859 * @param {Object} config (optional)
13860  */
13861 Roo.tree.TreeFilter = function(tree, config){
13862     this.tree = tree;
13863     this.filtered = {};
13864     Roo.apply(this, config);
13865 };
13866
13867 Roo.tree.TreeFilter.prototype = {
13868     clearBlank:false,
13869     reverse:false,
13870     autoClear:false,
13871     remove:false,
13872
13873      /**
13874      * Filter the data by a specific attribute.
13875      * @param {String/RegExp} value Either string that the attribute value
13876      * should start with or a RegExp to test against the attribute
13877      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13878      * @param {TreeNode} startNode (optional) The node to start the filter at.
13879      */
13880     filter : function(value, attr, startNode){
13881         attr = attr || "text";
13882         var f;
13883         if(typeof value == "string"){
13884             var vlen = value.length;
13885             // auto clear empty filter
13886             if(vlen == 0 && this.clearBlank){
13887                 this.clear();
13888                 return;
13889             }
13890             value = value.toLowerCase();
13891             f = function(n){
13892                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13893             };
13894         }else if(value.exec){ // regex?
13895             f = function(n){
13896                 return value.test(n.attributes[attr]);
13897             };
13898         }else{
13899             throw 'Illegal filter type, must be string or regex';
13900         }
13901         this.filterBy(f, null, startNode);
13902         },
13903
13904     /**
13905      * Filter by a function. The passed function will be called with each
13906      * node in the tree (or from the startNode). If the function returns true, the node is kept
13907      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13908      * @param {Function} fn The filter function
13909      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13910      */
13911     filterBy : function(fn, scope, startNode){
13912         startNode = startNode || this.tree.root;
13913         if(this.autoClear){
13914             this.clear();
13915         }
13916         var af = this.filtered, rv = this.reverse;
13917         var f = function(n){
13918             if(n == startNode){
13919                 return true;
13920             }
13921             if(af[n.id]){
13922                 return false;
13923             }
13924             var m = fn.call(scope || n, n);
13925             if(!m || rv){
13926                 af[n.id] = n;
13927                 n.ui.hide();
13928                 return false;
13929             }
13930             return true;
13931         };
13932         startNode.cascade(f);
13933         if(this.remove){
13934            for(var id in af){
13935                if(typeof id != "function"){
13936                    var n = af[id];
13937                    if(n && n.parentNode){
13938                        n.parentNode.removeChild(n);
13939                    }
13940                }
13941            }
13942         }
13943     },
13944
13945     /**
13946      * Clears the current filter. Note: with the "remove" option
13947      * set a filter cannot be cleared.
13948      */
13949     clear : function(){
13950         var t = this.tree;
13951         var af = this.filtered;
13952         for(var id in af){
13953             if(typeof id != "function"){
13954                 var n = af[id];
13955                 if(n){
13956                     n.ui.show();
13957                 }
13958             }
13959         }
13960         this.filtered = {};
13961     }
13962 };
13963 /*
13964  * Based on:
13965  * Ext JS Library 1.1.1
13966  * Copyright(c) 2006-2007, Ext JS, LLC.
13967  *
13968  * Originally Released Under LGPL - original licence link has changed is not relivant.
13969  *
13970  * Fork - LGPL
13971  * <script type="text/javascript">
13972  */
13973  
13974
13975 /**
13976  * @class Roo.tree.TreeSorter
13977  * Provides sorting of nodes in a TreePanel
13978  * 
13979  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13980  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13981  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13982  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13983  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13984  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13985  * @constructor
13986  * @param {TreePanel} tree
13987  * @param {Object} config
13988  */
13989 Roo.tree.TreeSorter = function(tree, config){
13990     Roo.apply(this, config);
13991     tree.on("beforechildrenrendered", this.doSort, this);
13992     tree.on("append", this.updateSort, this);
13993     tree.on("insert", this.updateSort, this);
13994     
13995     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13996     var p = this.property || "text";
13997     var sortType = this.sortType;
13998     var fs = this.folderSort;
13999     var cs = this.caseSensitive === true;
14000     var leafAttr = this.leafAttr || 'leaf';
14001
14002     this.sortFn = function(n1, n2){
14003         if(fs){
14004             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
14005                 return 1;
14006             }
14007             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
14008                 return -1;
14009             }
14010         }
14011         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
14012         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
14013         if(v1 < v2){
14014                         return dsc ? +1 : -1;
14015                 }else if(v1 > v2){
14016                         return dsc ? -1 : +1;
14017         }else{
14018                 return 0;
14019         }
14020     };
14021 };
14022
14023 Roo.tree.TreeSorter.prototype = {
14024     doSort : function(node){
14025         node.sort(this.sortFn);
14026     },
14027     
14028     compareNodes : function(n1, n2){
14029         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14030     },
14031     
14032     updateSort : function(tree, node){
14033         if(node.childrenRendered){
14034             this.doSort.defer(1, this, [node]);
14035         }
14036     }
14037 };/*
14038  * Based on:
14039  * Ext JS Library 1.1.1
14040  * Copyright(c) 2006-2007, Ext JS, LLC.
14041  *
14042  * Originally Released Under LGPL - original licence link has changed is not relivant.
14043  *
14044  * Fork - LGPL
14045  * <script type="text/javascript">
14046  */
14047
14048 if(Roo.dd.DropZone){
14049     
14050 Roo.tree.TreeDropZone = function(tree, config){
14051     this.allowParentInsert = false;
14052     this.allowContainerDrop = false;
14053     this.appendOnly = false;
14054     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14055     this.tree = tree;
14056     this.lastInsertClass = "x-tree-no-status";
14057     this.dragOverData = {};
14058 };
14059
14060 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14061     ddGroup : "TreeDD",
14062     scroll:  true,
14063     
14064     expandDelay : 1000,
14065     
14066     expandNode : function(node){
14067         if(node.hasChildNodes() && !node.isExpanded()){
14068             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14069         }
14070     },
14071     
14072     queueExpand : function(node){
14073         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14074     },
14075     
14076     cancelExpand : function(){
14077         if(this.expandProcId){
14078             clearTimeout(this.expandProcId);
14079             this.expandProcId = false;
14080         }
14081     },
14082     
14083     isValidDropPoint : function(n, pt, dd, e, data){
14084         if(!n || !data){ return false; }
14085         var targetNode = n.node;
14086         var dropNode = data.node;
14087         // default drop rules
14088         if(!(targetNode && targetNode.isTarget && pt)){
14089             return false;
14090         }
14091         if(pt == "append" && targetNode.allowChildren === false){
14092             return false;
14093         }
14094         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14095             return false;
14096         }
14097         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14098             return false;
14099         }
14100         // reuse the object
14101         var overEvent = this.dragOverData;
14102         overEvent.tree = this.tree;
14103         overEvent.target = targetNode;
14104         overEvent.data = data;
14105         overEvent.point = pt;
14106         overEvent.source = dd;
14107         overEvent.rawEvent = e;
14108         overEvent.dropNode = dropNode;
14109         overEvent.cancel = false;  
14110         var result = this.tree.fireEvent("nodedragover", overEvent);
14111         return overEvent.cancel === false && result !== false;
14112     },
14113     
14114     getDropPoint : function(e, n, dd)
14115     {
14116         var tn = n.node;
14117         if(tn.isRoot){
14118             return tn.allowChildren !== false ? "append" : false; // always append for root
14119         }
14120         var dragEl = n.ddel;
14121         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14122         var y = Roo.lib.Event.getPageY(e);
14123         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14124         
14125         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14126         var noAppend = tn.allowChildren === false;
14127         if(this.appendOnly || tn.parentNode.allowChildren === false){
14128             return noAppend ? false : "append";
14129         }
14130         var noBelow = false;
14131         if(!this.allowParentInsert){
14132             noBelow = tn.hasChildNodes() && tn.isExpanded();
14133         }
14134         var q = (b - t) / (noAppend ? 2 : 3);
14135         if(y >= t && y < (t + q)){
14136             return "above";
14137         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14138             return "below";
14139         }else{
14140             return "append";
14141         }
14142     },
14143     
14144     onNodeEnter : function(n, dd, e, data)
14145     {
14146         this.cancelExpand();
14147     },
14148     
14149     onNodeOver : function(n, dd, e, data)
14150     {
14151        
14152         var pt = this.getDropPoint(e, n, dd);
14153         var node = n.node;
14154         
14155         // auto node expand check
14156         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14157             this.queueExpand(node);
14158         }else if(pt != "append"){
14159             this.cancelExpand();
14160         }
14161         
14162         // set the insert point style on the target node
14163         var returnCls = this.dropNotAllowed;
14164         if(this.isValidDropPoint(n, pt, dd, e, data)){
14165            if(pt){
14166                var el = n.ddel;
14167                var cls;
14168                if(pt == "above"){
14169                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14170                    cls = "x-tree-drag-insert-above";
14171                }else if(pt == "below"){
14172                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14173                    cls = "x-tree-drag-insert-below";
14174                }else{
14175                    returnCls = "x-tree-drop-ok-append";
14176                    cls = "x-tree-drag-append";
14177                }
14178                if(this.lastInsertClass != cls){
14179                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14180                    this.lastInsertClass = cls;
14181                }
14182            }
14183        }
14184        return returnCls;
14185     },
14186     
14187     onNodeOut : function(n, dd, e, data){
14188         
14189         this.cancelExpand();
14190         this.removeDropIndicators(n);
14191     },
14192     
14193     onNodeDrop : function(n, dd, e, data){
14194         var point = this.getDropPoint(e, n, dd);
14195         var targetNode = n.node;
14196         targetNode.ui.startDrop();
14197         if(!this.isValidDropPoint(n, point, dd, e, data)){
14198             targetNode.ui.endDrop();
14199             return false;
14200         }
14201         // first try to find the drop node
14202         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14203         var dropEvent = {
14204             tree : this.tree,
14205             target: targetNode,
14206             data: data,
14207             point: point,
14208             source: dd,
14209             rawEvent: e,
14210             dropNode: dropNode,
14211             cancel: !dropNode   
14212         };
14213         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14214         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14215             targetNode.ui.endDrop();
14216             return false;
14217         }
14218         // allow target changing
14219         targetNode = dropEvent.target;
14220         if(point == "append" && !targetNode.isExpanded()){
14221             targetNode.expand(false, null, function(){
14222                 this.completeDrop(dropEvent);
14223             }.createDelegate(this));
14224         }else{
14225             this.completeDrop(dropEvent);
14226         }
14227         return true;
14228     },
14229     
14230     completeDrop : function(de){
14231         var ns = de.dropNode, p = de.point, t = de.target;
14232         if(!(ns instanceof Array)){
14233             ns = [ns];
14234         }
14235         var n;
14236         for(var i = 0, len = ns.length; i < len; i++){
14237             n = ns[i];
14238             if(p == "above"){
14239                 t.parentNode.insertBefore(n, t);
14240             }else if(p == "below"){
14241                 t.parentNode.insertBefore(n, t.nextSibling);
14242             }else{
14243                 t.appendChild(n);
14244             }
14245         }
14246         n.ui.focus();
14247         if(this.tree.hlDrop){
14248             n.ui.highlight();
14249         }
14250         t.ui.endDrop();
14251         this.tree.fireEvent("nodedrop", de);
14252     },
14253     
14254     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14255         if(this.tree.hlDrop){
14256             dropNode.ui.focus();
14257             dropNode.ui.highlight();
14258         }
14259         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14260     },
14261     
14262     getTree : function(){
14263         return this.tree;
14264     },
14265     
14266     removeDropIndicators : function(n){
14267         if(n && n.ddel){
14268             var el = n.ddel;
14269             Roo.fly(el).removeClass([
14270                     "x-tree-drag-insert-above",
14271                     "x-tree-drag-insert-below",
14272                     "x-tree-drag-append"]);
14273             this.lastInsertClass = "_noclass";
14274         }
14275     },
14276     
14277     beforeDragDrop : function(target, e, id){
14278         this.cancelExpand();
14279         return true;
14280     },
14281     
14282     afterRepair : function(data){
14283         if(data && Roo.enableFx){
14284             data.node.ui.highlight();
14285         }
14286         this.hideProxy();
14287     } 
14288     
14289 });
14290
14291 }
14292 /*
14293  * Based on:
14294  * Ext JS Library 1.1.1
14295  * Copyright(c) 2006-2007, Ext JS, LLC.
14296  *
14297  * Originally Released Under LGPL - original licence link has changed is not relivant.
14298  *
14299  * Fork - LGPL
14300  * <script type="text/javascript">
14301  */
14302  
14303
14304 if(Roo.dd.DragZone){
14305 Roo.tree.TreeDragZone = function(tree, config){
14306     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14307     this.tree = tree;
14308 };
14309
14310 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14311     ddGroup : "TreeDD",
14312    
14313     onBeforeDrag : function(data, e){
14314         var n = data.node;
14315         return n && n.draggable && !n.disabled;
14316     },
14317      
14318     
14319     onInitDrag : function(e){
14320         var data = this.dragData;
14321         this.tree.getSelectionModel().select(data.node);
14322         this.proxy.update("");
14323         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14324         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14325     },
14326     
14327     getRepairXY : function(e, data){
14328         return data.node.ui.getDDRepairXY();
14329     },
14330     
14331     onEndDrag : function(data, e){
14332         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14333         
14334         
14335     },
14336     
14337     onValidDrop : function(dd, e, id){
14338         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14339         this.hideProxy();
14340     },
14341     
14342     beforeInvalidDrop : function(e, id){
14343         // this scrolls the original position back into view
14344         var sm = this.tree.getSelectionModel();
14345         sm.clearSelections();
14346         sm.select(this.dragData.node);
14347     }
14348 });
14349 }/*
14350  * Based on:
14351  * Ext JS Library 1.1.1
14352  * Copyright(c) 2006-2007, Ext JS, LLC.
14353  *
14354  * Originally Released Under LGPL - original licence link has changed is not relivant.
14355  *
14356  * Fork - LGPL
14357  * <script type="text/javascript">
14358  */
14359 /**
14360  * @class Roo.tree.TreeEditor
14361  * @extends Roo.Editor
14362  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14363  * as the editor field.
14364  * @constructor
14365  * @param {Object} config (used to be the tree panel.)
14366  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14367  * 
14368  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14369  * @cfg {Roo.form.TextField|Object} field The field configuration
14370  *
14371  * 
14372  */
14373 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14374     var tree = config;
14375     var field;
14376     if (oldconfig) { // old style..
14377         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14378     } else {
14379         // new style..
14380         tree = config.tree;
14381         config.field = config.field  || {};
14382         config.field.xtype = 'TextField';
14383         field = Roo.factory(config.field, Roo.form);
14384     }
14385     config = config || {};
14386     
14387     
14388     this.addEvents({
14389         /**
14390          * @event beforenodeedit
14391          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14392          * false from the handler of this event.
14393          * @param {Editor} this
14394          * @param {Roo.tree.Node} node 
14395          */
14396         "beforenodeedit" : true
14397     });
14398     
14399     //Roo.log(config);
14400     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14401
14402     this.tree = tree;
14403
14404     tree.on('beforeclick', this.beforeNodeClick, this);
14405     tree.getTreeEl().on('mousedown', this.hide, this);
14406     this.on('complete', this.updateNode, this);
14407     this.on('beforestartedit', this.fitToTree, this);
14408     this.on('startedit', this.bindScroll, this, {delay:10});
14409     this.on('specialkey', this.onSpecialKey, this);
14410 };
14411
14412 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14413     /**
14414      * @cfg {String} alignment
14415      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14416      */
14417     alignment: "l-l",
14418     // inherit
14419     autoSize: false,
14420     /**
14421      * @cfg {Boolean} hideEl
14422      * True to hide the bound element while the editor is displayed (defaults to false)
14423      */
14424     hideEl : false,
14425     /**
14426      * @cfg {String} cls
14427      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14428      */
14429     cls: "x-small-editor x-tree-editor",
14430     /**
14431      * @cfg {Boolean} shim
14432      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14433      */
14434     shim:false,
14435     // inherit
14436     shadow:"frame",
14437     /**
14438      * @cfg {Number} maxWidth
14439      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14440      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14441      * scroll and client offsets into account prior to each edit.
14442      */
14443     maxWidth: 250,
14444
14445     editDelay : 350,
14446
14447     // private
14448     fitToTree : function(ed, el){
14449         var td = this.tree.getTreeEl().dom, nd = el.dom;
14450         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14451             td.scrollLeft = nd.offsetLeft;
14452         }
14453         var w = Math.min(
14454                 this.maxWidth,
14455                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14456         this.setSize(w, '');
14457         
14458         return this.fireEvent('beforenodeedit', this, this.editNode);
14459         
14460     },
14461
14462     // private
14463     triggerEdit : function(node){
14464         this.completeEdit();
14465         this.editNode = node;
14466         this.startEdit(node.ui.textNode, node.text);
14467     },
14468
14469     // private
14470     bindScroll : function(){
14471         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14472     },
14473
14474     // private
14475     beforeNodeClick : function(node, e){
14476         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14477         this.lastClick = new Date();
14478         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14479             e.stopEvent();
14480             this.triggerEdit(node);
14481             return false;
14482         }
14483         return true;
14484     },
14485
14486     // private
14487     updateNode : function(ed, value){
14488         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14489         this.editNode.setText(value);
14490     },
14491
14492     // private
14493     onHide : function(){
14494         Roo.tree.TreeEditor.superclass.onHide.call(this);
14495         if(this.editNode){
14496             this.editNode.ui.focus();
14497         }
14498     },
14499
14500     // private
14501     onSpecialKey : function(field, e){
14502         var k = e.getKey();
14503         if(k == e.ESC){
14504             e.stopEvent();
14505             this.cancelEdit();
14506         }else if(k == e.ENTER && !e.hasModifier()){
14507             e.stopEvent();
14508             this.completeEdit();
14509         }
14510     }
14511 });//<Script type="text/javascript">
14512 /*
14513  * Based on:
14514  * Ext JS Library 1.1.1
14515  * Copyright(c) 2006-2007, Ext JS, LLC.
14516  *
14517  * Originally Released Under LGPL - original licence link has changed is not relivant.
14518  *
14519  * Fork - LGPL
14520  * <script type="text/javascript">
14521  */
14522  
14523 /**
14524  * Not documented??? - probably should be...
14525  */
14526
14527 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14528     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14529     
14530     renderElements : function(n, a, targetNode, bulkRender){
14531         //consel.log("renderElements?");
14532         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14533
14534         var t = n.getOwnerTree();
14535         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14536         
14537         var cols = t.columns;
14538         var bw = t.borderWidth;
14539         var c = cols[0];
14540         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14541          var cb = typeof a.checked == "boolean";
14542         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14543         var colcls = 'x-t-' + tid + '-c0';
14544         var buf = [
14545             '<li class="x-tree-node">',
14546             
14547                 
14548                 '<div class="x-tree-node-el ', a.cls,'">',
14549                     // extran...
14550                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14551                 
14552                 
14553                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14554                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14555                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14556                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14557                            (a.iconCls ? ' '+a.iconCls : ''),
14558                            '" unselectable="on" />',
14559                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14560                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14561                              
14562                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14563                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14564                             '<span unselectable="on" qtip="' + tx + '">',
14565                              tx,
14566                              '</span></a>' ,
14567                     '</div>',
14568                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14569                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14570                  ];
14571         for(var i = 1, len = cols.length; i < len; i++){
14572             c = cols[i];
14573             colcls = 'x-t-' + tid + '-c' +i;
14574             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14575             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14576                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14577                       "</div>");
14578          }
14579          
14580          buf.push(
14581             '</a>',
14582             '<div class="x-clear"></div></div>',
14583             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14584             "</li>");
14585         
14586         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14587             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14588                                 n.nextSibling.ui.getEl(), buf.join(""));
14589         }else{
14590             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14591         }
14592         var el = this.wrap.firstChild;
14593         this.elRow = el;
14594         this.elNode = el.firstChild;
14595         this.ranchor = el.childNodes[1];
14596         this.ctNode = this.wrap.childNodes[1];
14597         var cs = el.firstChild.childNodes;
14598         this.indentNode = cs[0];
14599         this.ecNode = cs[1];
14600         this.iconNode = cs[2];
14601         var index = 3;
14602         if(cb){
14603             this.checkbox = cs[3];
14604             index++;
14605         }
14606         this.anchor = cs[index];
14607         
14608         this.textNode = cs[index].firstChild;
14609         
14610         //el.on("click", this.onClick, this);
14611         //el.on("dblclick", this.onDblClick, this);
14612         
14613         
14614        // console.log(this);
14615     },
14616     initEvents : function(){
14617         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14618         
14619             
14620         var a = this.ranchor;
14621
14622         var el = Roo.get(a);
14623
14624         if(Roo.isOpera){ // opera render bug ignores the CSS
14625             el.setStyle("text-decoration", "none");
14626         }
14627
14628         el.on("click", this.onClick, this);
14629         el.on("dblclick", this.onDblClick, this);
14630         el.on("contextmenu", this.onContextMenu, this);
14631         
14632     },
14633     
14634     /*onSelectedChange : function(state){
14635         if(state){
14636             this.focus();
14637             this.addClass("x-tree-selected");
14638         }else{
14639             //this.blur();
14640             this.removeClass("x-tree-selected");
14641         }
14642     },*/
14643     addClass : function(cls){
14644         if(this.elRow){
14645             Roo.fly(this.elRow).addClass(cls);
14646         }
14647         
14648     },
14649     
14650     
14651     removeClass : function(cls){
14652         if(this.elRow){
14653             Roo.fly(this.elRow).removeClass(cls);
14654         }
14655     }
14656
14657     
14658     
14659 });//<Script type="text/javascript">
14660
14661 /*
14662  * Based on:
14663  * Ext JS Library 1.1.1
14664  * Copyright(c) 2006-2007, Ext JS, LLC.
14665  *
14666  * Originally Released Under LGPL - original licence link has changed is not relivant.
14667  *
14668  * Fork - LGPL
14669  * <script type="text/javascript">
14670  */
14671  
14672
14673 /**
14674  * @class Roo.tree.ColumnTree
14675  * @extends Roo.data.TreePanel
14676  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14677  * @cfg {int} borderWidth  compined right/left border allowance
14678  * @constructor
14679  * @param {String/HTMLElement/Element} el The container element
14680  * @param {Object} config
14681  */
14682 Roo.tree.ColumnTree =  function(el, config)
14683 {
14684    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14685    this.addEvents({
14686         /**
14687         * @event resize
14688         * Fire this event on a container when it resizes
14689         * @param {int} w Width
14690         * @param {int} h Height
14691         */
14692        "resize" : true
14693     });
14694     this.on('resize', this.onResize, this);
14695 };
14696
14697 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14698     //lines:false,
14699     
14700     
14701     borderWidth: Roo.isBorderBox ? 0 : 2, 
14702     headEls : false,
14703     
14704     render : function(){
14705         // add the header.....
14706        
14707         Roo.tree.ColumnTree.superclass.render.apply(this);
14708         
14709         this.el.addClass('x-column-tree');
14710         
14711         this.headers = this.el.createChild(
14712             {cls:'x-tree-headers'},this.innerCt.dom);
14713    
14714         var cols = this.columns, c;
14715         var totalWidth = 0;
14716         this.headEls = [];
14717         var  len = cols.length;
14718         for(var i = 0; i < len; i++){
14719              c = cols[i];
14720              totalWidth += c.width;
14721             this.headEls.push(this.headers.createChild({
14722                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14723                  cn: {
14724                      cls:'x-tree-hd-text',
14725                      html: c.header
14726                  },
14727                  style:'width:'+(c.width-this.borderWidth)+'px;'
14728              }));
14729         }
14730         this.headers.createChild({cls:'x-clear'});
14731         // prevent floats from wrapping when clipped
14732         this.headers.setWidth(totalWidth);
14733         //this.innerCt.setWidth(totalWidth);
14734         this.innerCt.setStyle({ overflow: 'auto' });
14735         this.onResize(this.width, this.height);
14736              
14737         
14738     },
14739     onResize : function(w,h)
14740     {
14741         this.height = h;
14742         this.width = w;
14743         // resize cols..
14744         this.innerCt.setWidth(this.width);
14745         this.innerCt.setHeight(this.height-20);
14746         
14747         // headers...
14748         var cols = this.columns, c;
14749         var totalWidth = 0;
14750         var expEl = false;
14751         var len = cols.length;
14752         for(var i = 0; i < len; i++){
14753             c = cols[i];
14754             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14755                 // it's the expander..
14756                 expEl  = this.headEls[i];
14757                 continue;
14758             }
14759             totalWidth += c.width;
14760             
14761         }
14762         if (expEl) {
14763             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14764         }
14765         this.headers.setWidth(w-20);
14766
14767         
14768         
14769         
14770     }
14771 });
14772 /*
14773  * Based on:
14774  * Ext JS Library 1.1.1
14775  * Copyright(c) 2006-2007, Ext JS, LLC.
14776  *
14777  * Originally Released Under LGPL - original licence link has changed is not relivant.
14778  *
14779  * Fork - LGPL
14780  * <script type="text/javascript">
14781  */
14782  
14783 /**
14784  * @class Roo.menu.Menu
14785  * @extends Roo.util.Observable
14786  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14787  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14788  * @constructor
14789  * Creates a new Menu
14790  * @param {Object} config Configuration options
14791  */
14792 Roo.menu.Menu = function(config){
14793     
14794     Roo.menu.Menu.superclass.constructor.call(this, config);
14795     
14796     this.id = this.id || Roo.id();
14797     this.addEvents({
14798         /**
14799          * @event beforeshow
14800          * Fires before this menu is displayed
14801          * @param {Roo.menu.Menu} this
14802          */
14803         beforeshow : true,
14804         /**
14805          * @event beforehide
14806          * Fires before this menu is hidden
14807          * @param {Roo.menu.Menu} this
14808          */
14809         beforehide : true,
14810         /**
14811          * @event show
14812          * Fires after this menu is displayed
14813          * @param {Roo.menu.Menu} this
14814          */
14815         show : true,
14816         /**
14817          * @event hide
14818          * Fires after this menu is hidden
14819          * @param {Roo.menu.Menu} this
14820          */
14821         hide : true,
14822         /**
14823          * @event click
14824          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14825          * @param {Roo.menu.Menu} this
14826          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14827          * @param {Roo.EventObject} e
14828          */
14829         click : true,
14830         /**
14831          * @event mouseover
14832          * Fires when the mouse is hovering over this menu
14833          * @param {Roo.menu.Menu} this
14834          * @param {Roo.EventObject} e
14835          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14836          */
14837         mouseover : true,
14838         /**
14839          * @event mouseout
14840          * Fires when the mouse exits this menu
14841          * @param {Roo.menu.Menu} this
14842          * @param {Roo.EventObject} e
14843          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14844          */
14845         mouseout : true,
14846         /**
14847          * @event itemclick
14848          * Fires when a menu item contained in this menu is clicked
14849          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14850          * @param {Roo.EventObject} e
14851          */
14852         itemclick: true
14853     });
14854     if (this.registerMenu) {
14855         Roo.menu.MenuMgr.register(this);
14856     }
14857     
14858     var mis = this.items;
14859     this.items = new Roo.util.MixedCollection();
14860     if(mis){
14861         this.add.apply(this, mis);
14862     }
14863 };
14864
14865 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14866     /**
14867      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14868      */
14869     minWidth : 120,
14870     /**
14871      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14872      * for bottom-right shadow (defaults to "sides")
14873      */
14874     shadow : "sides",
14875     /**
14876      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14877      * this menu (defaults to "tl-tr?")
14878      */
14879     subMenuAlign : "tl-tr?",
14880     /**
14881      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14882      * relative to its element of origin (defaults to "tl-bl?")
14883      */
14884     defaultAlign : "tl-bl?",
14885     /**
14886      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14887      */
14888     allowOtherMenus : false,
14889     /**
14890      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14891      */
14892     registerMenu : true,
14893
14894     hidden:true,
14895
14896     // private
14897     render : function(){
14898         if(this.el){
14899             return;
14900         }
14901         var el = this.el = new Roo.Layer({
14902             cls: "x-menu",
14903             shadow:this.shadow,
14904             constrain: false,
14905             parentEl: this.parentEl || document.body,
14906             zindex:15000
14907         });
14908
14909         this.keyNav = new Roo.menu.MenuNav(this);
14910
14911         if(this.plain){
14912             el.addClass("x-menu-plain");
14913         }
14914         if(this.cls){
14915             el.addClass(this.cls);
14916         }
14917         // generic focus element
14918         this.focusEl = el.createChild({
14919             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14920         });
14921         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14922         //disabling touch- as it's causing issues ..
14923         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14924         ul.on('click'   , this.onClick, this);
14925         
14926         
14927         ul.on("mouseover", this.onMouseOver, this);
14928         ul.on("mouseout", this.onMouseOut, this);
14929         this.items.each(function(item){
14930             if (item.hidden) {
14931                 return;
14932             }
14933             
14934             var li = document.createElement("li");
14935             li.className = "x-menu-list-item";
14936             ul.dom.appendChild(li);
14937             item.render(li, this);
14938         }, this);
14939         this.ul = ul;
14940         this.autoWidth();
14941     },
14942
14943     // private
14944     autoWidth : function(){
14945         var el = this.el, ul = this.ul;
14946         if(!el){
14947             return;
14948         }
14949         var w = this.width;
14950         if(w){
14951             el.setWidth(w);
14952         }else if(Roo.isIE){
14953             el.setWidth(this.minWidth);
14954             var t = el.dom.offsetWidth; // force recalc
14955             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14956         }
14957     },
14958
14959     // private
14960     delayAutoWidth : function(){
14961         if(this.rendered){
14962             if(!this.awTask){
14963                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14964             }
14965             this.awTask.delay(20);
14966         }
14967     },
14968
14969     // private
14970     findTargetItem : function(e){
14971         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14972         if(t && t.menuItemId){
14973             return this.items.get(t.menuItemId);
14974         }
14975     },
14976
14977     // private
14978     onClick : function(e){
14979         Roo.log("menu.onClick");
14980         var t = this.findTargetItem(e);
14981         if(!t){
14982             return;
14983         }
14984         Roo.log(e);
14985         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14986             if(t == this.activeItem && t.shouldDeactivate(e)){
14987                 this.activeItem.deactivate();
14988                 delete this.activeItem;
14989                 return;
14990             }
14991             if(t.canActivate){
14992                 this.setActiveItem(t, true);
14993             }
14994             return;
14995             
14996             
14997         }
14998         
14999         t.onClick(e);
15000         this.fireEvent("click", this, t, e);
15001     },
15002
15003     // private
15004     setActiveItem : function(item, autoExpand){
15005         if(item != this.activeItem){
15006             if(this.activeItem){
15007                 this.activeItem.deactivate();
15008             }
15009             this.activeItem = item;
15010             item.activate(autoExpand);
15011         }else if(autoExpand){
15012             item.expandMenu();
15013         }
15014     },
15015
15016     // private
15017     tryActivate : function(start, step){
15018         var items = this.items;
15019         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15020             var item = items.get(i);
15021             if(!item.disabled && item.canActivate){
15022                 this.setActiveItem(item, false);
15023                 return item;
15024             }
15025         }
15026         return false;
15027     },
15028
15029     // private
15030     onMouseOver : function(e){
15031         var t;
15032         if(t = this.findTargetItem(e)){
15033             if(t.canActivate && !t.disabled){
15034                 this.setActiveItem(t, true);
15035             }
15036         }
15037         this.fireEvent("mouseover", this, e, t);
15038     },
15039
15040     // private
15041     onMouseOut : function(e){
15042         var t;
15043         if(t = this.findTargetItem(e)){
15044             if(t == this.activeItem && t.shouldDeactivate(e)){
15045                 this.activeItem.deactivate();
15046                 delete this.activeItem;
15047             }
15048         }
15049         this.fireEvent("mouseout", this, e, t);
15050     },
15051
15052     /**
15053      * Read-only.  Returns true if the menu is currently displayed, else false.
15054      * @type Boolean
15055      */
15056     isVisible : function(){
15057         return this.el && !this.hidden;
15058     },
15059
15060     /**
15061      * Displays this menu relative to another element
15062      * @param {String/HTMLElement/Roo.Element} element The element to align to
15063      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15064      * the element (defaults to this.defaultAlign)
15065      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15066      */
15067     show : function(el, pos, parentMenu){
15068         this.parentMenu = parentMenu;
15069         if(!this.el){
15070             this.render();
15071         }
15072         this.fireEvent("beforeshow", this);
15073         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15074     },
15075
15076     /**
15077      * Displays this menu at a specific xy position
15078      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15079      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15080      */
15081     showAt : function(xy, parentMenu, /* private: */_e){
15082         this.parentMenu = parentMenu;
15083         if(!this.el){
15084             this.render();
15085         }
15086         if(_e !== false){
15087             this.fireEvent("beforeshow", this);
15088             xy = this.el.adjustForConstraints(xy);
15089         }
15090         this.el.setXY(xy);
15091         this.el.show();
15092         this.hidden = false;
15093         this.focus();
15094         this.fireEvent("show", this);
15095     },
15096
15097     focus : function(){
15098         if(!this.hidden){
15099             this.doFocus.defer(50, this);
15100         }
15101     },
15102
15103     doFocus : function(){
15104         if(!this.hidden){
15105             this.focusEl.focus();
15106         }
15107     },
15108
15109     /**
15110      * Hides this menu and optionally all parent menus
15111      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15112      */
15113     hide : function(deep){
15114         if(this.el && this.isVisible()){
15115             this.fireEvent("beforehide", this);
15116             if(this.activeItem){
15117                 this.activeItem.deactivate();
15118                 this.activeItem = null;
15119             }
15120             this.el.hide();
15121             this.hidden = true;
15122             this.fireEvent("hide", this);
15123         }
15124         if(deep === true && this.parentMenu){
15125             this.parentMenu.hide(true);
15126         }
15127     },
15128
15129     /**
15130      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15131      * Any of the following are valid:
15132      * <ul>
15133      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15134      * <li>An HTMLElement object which will be converted to a menu item</li>
15135      * <li>A menu item config object that will be created as a new menu item</li>
15136      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15137      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15138      * </ul>
15139      * Usage:
15140      * <pre><code>
15141 // Create the menu
15142 var menu = new Roo.menu.Menu();
15143
15144 // Create a menu item to add by reference
15145 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15146
15147 // Add a bunch of items at once using different methods.
15148 // Only the last item added will be returned.
15149 var item = menu.add(
15150     menuItem,                // add existing item by ref
15151     'Dynamic Item',          // new TextItem
15152     '-',                     // new separator
15153     { text: 'Config Item' }  // new item by config
15154 );
15155 </code></pre>
15156      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15157      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15158      */
15159     add : function(){
15160         var a = arguments, l = a.length, item;
15161         for(var i = 0; i < l; i++){
15162             var el = a[i];
15163             if ((typeof(el) == "object") && el.xtype && el.xns) {
15164                 el = Roo.factory(el, Roo.menu);
15165             }
15166             
15167             if(el.render){ // some kind of Item
15168                 item = this.addItem(el);
15169             }else if(typeof el == "string"){ // string
15170                 if(el == "separator" || el == "-"){
15171                     item = this.addSeparator();
15172                 }else{
15173                     item = this.addText(el);
15174                 }
15175             }else if(el.tagName || el.el){ // element
15176                 item = this.addElement(el);
15177             }else if(typeof el == "object"){ // must be menu item config?
15178                 item = this.addMenuItem(el);
15179             }
15180         }
15181         return item;
15182     },
15183
15184     /**
15185      * Returns this menu's underlying {@link Roo.Element} object
15186      * @return {Roo.Element} The element
15187      */
15188     getEl : function(){
15189         if(!this.el){
15190             this.render();
15191         }
15192         return this.el;
15193     },
15194
15195     /**
15196      * Adds a separator bar to the menu
15197      * @return {Roo.menu.Item} The menu item that was added
15198      */
15199     addSeparator : function(){
15200         return this.addItem(new Roo.menu.Separator());
15201     },
15202
15203     /**
15204      * Adds an {@link Roo.Element} object to the menu
15205      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15206      * @return {Roo.menu.Item} The menu item that was added
15207      */
15208     addElement : function(el){
15209         return this.addItem(new Roo.menu.BaseItem(el));
15210     },
15211
15212     /**
15213      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15214      * @param {Roo.menu.Item} item The menu item to add
15215      * @return {Roo.menu.Item} The menu item that was added
15216      */
15217     addItem : function(item){
15218         this.items.add(item);
15219         if(this.ul){
15220             var li = document.createElement("li");
15221             li.className = "x-menu-list-item";
15222             this.ul.dom.appendChild(li);
15223             item.render(li, this);
15224             this.delayAutoWidth();
15225         }
15226         return item;
15227     },
15228
15229     /**
15230      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15231      * @param {Object} config A MenuItem config object
15232      * @return {Roo.menu.Item} The menu item that was added
15233      */
15234     addMenuItem : function(config){
15235         if(!(config instanceof Roo.menu.Item)){
15236             if(typeof config.checked == "boolean"){ // must be check menu item config?
15237                 config = new Roo.menu.CheckItem(config);
15238             }else{
15239                 config = new Roo.menu.Item(config);
15240             }
15241         }
15242         return this.addItem(config);
15243     },
15244
15245     /**
15246      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15247      * @param {String} text The text to display in the menu item
15248      * @return {Roo.menu.Item} The menu item that was added
15249      */
15250     addText : function(text){
15251         return this.addItem(new Roo.menu.TextItem({ text : text }));
15252     },
15253
15254     /**
15255      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15256      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15257      * @param {Roo.menu.Item} item The menu item to add
15258      * @return {Roo.menu.Item} The menu item that was added
15259      */
15260     insert : function(index, item){
15261         this.items.insert(index, item);
15262         if(this.ul){
15263             var li = document.createElement("li");
15264             li.className = "x-menu-list-item";
15265             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15266             item.render(li, this);
15267             this.delayAutoWidth();
15268         }
15269         return item;
15270     },
15271
15272     /**
15273      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15274      * @param {Roo.menu.Item} item The menu item to remove
15275      */
15276     remove : function(item){
15277         this.items.removeKey(item.id);
15278         item.destroy();
15279     },
15280
15281     /**
15282      * Removes and destroys all items in the menu
15283      */
15284     removeAll : function(){
15285         var f;
15286         while(f = this.items.first()){
15287             this.remove(f);
15288         }
15289     }
15290 });
15291
15292 // MenuNav is a private utility class used internally by the Menu
15293 Roo.menu.MenuNav = function(menu){
15294     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15295     this.scope = this.menu = menu;
15296 };
15297
15298 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15299     doRelay : function(e, h){
15300         var k = e.getKey();
15301         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15302             this.menu.tryActivate(0, 1);
15303             return false;
15304         }
15305         return h.call(this.scope || this, e, this.menu);
15306     },
15307
15308     up : function(e, m){
15309         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15310             m.tryActivate(m.items.length-1, -1);
15311         }
15312     },
15313
15314     down : function(e, m){
15315         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15316             m.tryActivate(0, 1);
15317         }
15318     },
15319
15320     right : function(e, m){
15321         if(m.activeItem){
15322             m.activeItem.expandMenu(true);
15323         }
15324     },
15325
15326     left : function(e, m){
15327         m.hide();
15328         if(m.parentMenu && m.parentMenu.activeItem){
15329             m.parentMenu.activeItem.activate();
15330         }
15331     },
15332
15333     enter : function(e, m){
15334         if(m.activeItem){
15335             e.stopPropagation();
15336             m.activeItem.onClick(e);
15337             m.fireEvent("click", this, m.activeItem);
15338             return true;
15339         }
15340     }
15341 });/*
15342  * Based on:
15343  * Ext JS Library 1.1.1
15344  * Copyright(c) 2006-2007, Ext JS, LLC.
15345  *
15346  * Originally Released Under LGPL - original licence link has changed is not relivant.
15347  *
15348  * Fork - LGPL
15349  * <script type="text/javascript">
15350  */
15351  
15352 /**
15353  * @class Roo.menu.MenuMgr
15354  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15355  * @singleton
15356  */
15357 Roo.menu.MenuMgr = function(){
15358    var menus, active, groups = {}, attached = false, lastShow = new Date();
15359
15360    // private - called when first menu is created
15361    function init(){
15362        menus = {};
15363        active = new Roo.util.MixedCollection();
15364        Roo.get(document).addKeyListener(27, function(){
15365            if(active.length > 0){
15366                hideAll();
15367            }
15368        });
15369    }
15370
15371    // private
15372    function hideAll(){
15373        if(active && active.length > 0){
15374            var c = active.clone();
15375            c.each(function(m){
15376                m.hide();
15377            });
15378        }
15379    }
15380
15381    // private
15382    function onHide(m){
15383        active.remove(m);
15384        if(active.length < 1){
15385            Roo.get(document).un("mousedown", onMouseDown);
15386            attached = false;
15387        }
15388    }
15389
15390    // private
15391    function onShow(m){
15392        var last = active.last();
15393        lastShow = new Date();
15394        active.add(m);
15395        if(!attached){
15396            Roo.get(document).on("mousedown", onMouseDown);
15397            attached = true;
15398        }
15399        if(m.parentMenu){
15400           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15401           m.parentMenu.activeChild = m;
15402        }else if(last && last.isVisible()){
15403           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15404        }
15405    }
15406
15407    // private
15408    function onBeforeHide(m){
15409        if(m.activeChild){
15410            m.activeChild.hide();
15411        }
15412        if(m.autoHideTimer){
15413            clearTimeout(m.autoHideTimer);
15414            delete m.autoHideTimer;
15415        }
15416    }
15417
15418    // private
15419    function onBeforeShow(m){
15420        var pm = m.parentMenu;
15421        if(!pm && !m.allowOtherMenus){
15422            hideAll();
15423        }else if(pm && pm.activeChild && active != m){
15424            pm.activeChild.hide();
15425        }
15426    }
15427
15428    // private
15429    function onMouseDown(e){
15430        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15431            hideAll();
15432        }
15433    }
15434
15435    // private
15436    function onBeforeCheck(mi, state){
15437        if(state){
15438            var g = groups[mi.group];
15439            for(var i = 0, l = g.length; i < l; i++){
15440                if(g[i] != mi){
15441                    g[i].setChecked(false);
15442                }
15443            }
15444        }
15445    }
15446
15447    return {
15448
15449        /**
15450         * Hides all menus that are currently visible
15451         */
15452        hideAll : function(){
15453             hideAll();  
15454        },
15455
15456        // private
15457        register : function(menu){
15458            if(!menus){
15459                init();
15460            }
15461            menus[menu.id] = menu;
15462            menu.on("beforehide", onBeforeHide);
15463            menu.on("hide", onHide);
15464            menu.on("beforeshow", onBeforeShow);
15465            menu.on("show", onShow);
15466            var g = menu.group;
15467            if(g && menu.events["checkchange"]){
15468                if(!groups[g]){
15469                    groups[g] = [];
15470                }
15471                groups[g].push(menu);
15472                menu.on("checkchange", onCheck);
15473            }
15474        },
15475
15476         /**
15477          * Returns a {@link Roo.menu.Menu} object
15478          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15479          * be used to generate and return a new Menu instance.
15480          */
15481        get : function(menu){
15482            if(typeof menu == "string"){ // menu id
15483                return menus[menu];
15484            }else if(menu.events){  // menu instance
15485                return menu;
15486            }else if(typeof menu.length == 'number'){ // array of menu items?
15487                return new Roo.menu.Menu({items:menu});
15488            }else{ // otherwise, must be a config
15489                return new Roo.menu.Menu(menu);
15490            }
15491        },
15492
15493        // private
15494        unregister : function(menu){
15495            delete menus[menu.id];
15496            menu.un("beforehide", onBeforeHide);
15497            menu.un("hide", onHide);
15498            menu.un("beforeshow", onBeforeShow);
15499            menu.un("show", onShow);
15500            var g = menu.group;
15501            if(g && menu.events["checkchange"]){
15502                groups[g].remove(menu);
15503                menu.un("checkchange", onCheck);
15504            }
15505        },
15506
15507        // private
15508        registerCheckable : function(menuItem){
15509            var g = menuItem.group;
15510            if(g){
15511                if(!groups[g]){
15512                    groups[g] = [];
15513                }
15514                groups[g].push(menuItem);
15515                menuItem.on("beforecheckchange", onBeforeCheck);
15516            }
15517        },
15518
15519        // private
15520        unregisterCheckable : function(menuItem){
15521            var g = menuItem.group;
15522            if(g){
15523                groups[g].remove(menuItem);
15524                menuItem.un("beforecheckchange", onBeforeCheck);
15525            }
15526        }
15527    };
15528 }();/*
15529  * Based on:
15530  * Ext JS Library 1.1.1
15531  * Copyright(c) 2006-2007, Ext JS, LLC.
15532  *
15533  * Originally Released Under LGPL - original licence link has changed is not relivant.
15534  *
15535  * Fork - LGPL
15536  * <script type="text/javascript">
15537  */
15538  
15539
15540 /**
15541  * @class Roo.menu.BaseItem
15542  * @extends Roo.Component
15543  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15544  * management and base configuration options shared by all menu components.
15545  * @constructor
15546  * Creates a new BaseItem
15547  * @param {Object} config Configuration options
15548  */
15549 Roo.menu.BaseItem = function(config){
15550     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15551
15552     this.addEvents({
15553         /**
15554          * @event click
15555          * Fires when this item is clicked
15556          * @param {Roo.menu.BaseItem} this
15557          * @param {Roo.EventObject} e
15558          */
15559         click: true,
15560         /**
15561          * @event activate
15562          * Fires when this item is activated
15563          * @param {Roo.menu.BaseItem} this
15564          */
15565         activate : true,
15566         /**
15567          * @event deactivate
15568          * Fires when this item is deactivated
15569          * @param {Roo.menu.BaseItem} this
15570          */
15571         deactivate : true
15572     });
15573
15574     if(this.handler){
15575         this.on("click", this.handler, this.scope, true);
15576     }
15577 };
15578
15579 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15580     /**
15581      * @cfg {Function} handler
15582      * A function that will handle the click event of this menu item (defaults to undefined)
15583      */
15584     /**
15585      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15586      */
15587     canActivate : false,
15588     
15589      /**
15590      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15591      */
15592     hidden: false,
15593     
15594     /**
15595      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15596      */
15597     activeClass : "x-menu-item-active",
15598     /**
15599      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15600      */
15601     hideOnClick : true,
15602     /**
15603      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15604      */
15605     hideDelay : 100,
15606
15607     // private
15608     ctype: "Roo.menu.BaseItem",
15609
15610     // private
15611     actionMode : "container",
15612
15613     // private
15614     render : function(container, parentMenu){
15615         this.parentMenu = parentMenu;
15616         Roo.menu.BaseItem.superclass.render.call(this, container);
15617         this.container.menuItemId = this.id;
15618     },
15619
15620     // private
15621     onRender : function(container, position){
15622         this.el = Roo.get(this.el);
15623         container.dom.appendChild(this.el.dom);
15624     },
15625
15626     // private
15627     onClick : function(e){
15628         if(!this.disabled && this.fireEvent("click", this, e) !== false
15629                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15630             this.handleClick(e);
15631         }else{
15632             e.stopEvent();
15633         }
15634     },
15635
15636     // private
15637     activate : function(){
15638         if(this.disabled){
15639             return false;
15640         }
15641         var li = this.container;
15642         li.addClass(this.activeClass);
15643         this.region = li.getRegion().adjust(2, 2, -2, -2);
15644         this.fireEvent("activate", this);
15645         return true;
15646     },
15647
15648     // private
15649     deactivate : function(){
15650         this.container.removeClass(this.activeClass);
15651         this.fireEvent("deactivate", this);
15652     },
15653
15654     // private
15655     shouldDeactivate : function(e){
15656         return !this.region || !this.region.contains(e.getPoint());
15657     },
15658
15659     // private
15660     handleClick : function(e){
15661         if(this.hideOnClick){
15662             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15663         }
15664     },
15665
15666     // private
15667     expandMenu : function(autoActivate){
15668         // do nothing
15669     },
15670
15671     // private
15672     hideMenu : function(){
15673         // do nothing
15674     }
15675 });/*
15676  * Based on:
15677  * Ext JS Library 1.1.1
15678  * Copyright(c) 2006-2007, Ext JS, LLC.
15679  *
15680  * Originally Released Under LGPL - original licence link has changed is not relivant.
15681  *
15682  * Fork - LGPL
15683  * <script type="text/javascript">
15684  */
15685  
15686 /**
15687  * @class Roo.menu.Adapter
15688  * @extends Roo.menu.BaseItem
15689  * 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.
15690  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15691  * @constructor
15692  * Creates a new Adapter
15693  * @param {Object} config Configuration options
15694  */
15695 Roo.menu.Adapter = function(component, config){
15696     Roo.menu.Adapter.superclass.constructor.call(this, config);
15697     this.component = component;
15698 };
15699 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15700     // private
15701     canActivate : true,
15702
15703     // private
15704     onRender : function(container, position){
15705         this.component.render(container);
15706         this.el = this.component.getEl();
15707     },
15708
15709     // private
15710     activate : function(){
15711         if(this.disabled){
15712             return false;
15713         }
15714         this.component.focus();
15715         this.fireEvent("activate", this);
15716         return true;
15717     },
15718
15719     // private
15720     deactivate : function(){
15721         this.fireEvent("deactivate", this);
15722     },
15723
15724     // private
15725     disable : function(){
15726         this.component.disable();
15727         Roo.menu.Adapter.superclass.disable.call(this);
15728     },
15729
15730     // private
15731     enable : function(){
15732         this.component.enable();
15733         Roo.menu.Adapter.superclass.enable.call(this);
15734     }
15735 });/*
15736  * Based on:
15737  * Ext JS Library 1.1.1
15738  * Copyright(c) 2006-2007, Ext JS, LLC.
15739  *
15740  * Originally Released Under LGPL - original licence link has changed is not relivant.
15741  *
15742  * Fork - LGPL
15743  * <script type="text/javascript">
15744  */
15745
15746 /**
15747  * @class Roo.menu.TextItem
15748  * @extends Roo.menu.BaseItem
15749  * Adds a static text string to a menu, usually used as either a heading or group separator.
15750  * Note: old style constructor with text is still supported.
15751  * 
15752  * @constructor
15753  * Creates a new TextItem
15754  * @param {Object} cfg Configuration
15755  */
15756 Roo.menu.TextItem = function(cfg){
15757     if (typeof(cfg) == 'string') {
15758         this.text = cfg;
15759     } else {
15760         Roo.apply(this,cfg);
15761     }
15762     
15763     Roo.menu.TextItem.superclass.constructor.call(this);
15764 };
15765
15766 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15767     /**
15768      * @cfg {Boolean} text Text to show on item.
15769      */
15770     text : '',
15771     
15772     /**
15773      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15774      */
15775     hideOnClick : false,
15776     /**
15777      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15778      */
15779     itemCls : "x-menu-text",
15780
15781     // private
15782     onRender : function(){
15783         var s = document.createElement("span");
15784         s.className = this.itemCls;
15785         s.innerHTML = this.text;
15786         this.el = s;
15787         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15788     }
15789 });/*
15790  * Based on:
15791  * Ext JS Library 1.1.1
15792  * Copyright(c) 2006-2007, Ext JS, LLC.
15793  *
15794  * Originally Released Under LGPL - original licence link has changed is not relivant.
15795  *
15796  * Fork - LGPL
15797  * <script type="text/javascript">
15798  */
15799
15800 /**
15801  * @class Roo.menu.Separator
15802  * @extends Roo.menu.BaseItem
15803  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15804  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15805  * @constructor
15806  * @param {Object} config Configuration options
15807  */
15808 Roo.menu.Separator = function(config){
15809     Roo.menu.Separator.superclass.constructor.call(this, config);
15810 };
15811
15812 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15813     /**
15814      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15815      */
15816     itemCls : "x-menu-sep",
15817     /**
15818      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15819      */
15820     hideOnClick : false,
15821
15822     // private
15823     onRender : function(li){
15824         var s = document.createElement("span");
15825         s.className = this.itemCls;
15826         s.innerHTML = "&#160;";
15827         this.el = s;
15828         li.addClass("x-menu-sep-li");
15829         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15830     }
15831 });/*
15832  * Based on:
15833  * Ext JS Library 1.1.1
15834  * Copyright(c) 2006-2007, Ext JS, LLC.
15835  *
15836  * Originally Released Under LGPL - original licence link has changed is not relivant.
15837  *
15838  * Fork - LGPL
15839  * <script type="text/javascript">
15840  */
15841 /**
15842  * @class Roo.menu.Item
15843  * @extends Roo.menu.BaseItem
15844  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15845  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15846  * activation and click handling.
15847  * @constructor
15848  * Creates a new Item
15849  * @param {Object} config Configuration options
15850  */
15851 Roo.menu.Item = function(config){
15852     Roo.menu.Item.superclass.constructor.call(this, config);
15853     if(this.menu){
15854         this.menu = Roo.menu.MenuMgr.get(this.menu);
15855     }
15856 };
15857 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15858     
15859     /**
15860      * @cfg {String} text
15861      * The text to show on the menu item.
15862      */
15863     text: '',
15864      /**
15865      * @cfg {String} HTML to render in menu
15866      * The text to show on the menu item (HTML version).
15867      */
15868     html: '',
15869     /**
15870      * @cfg {String} icon
15871      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15872      */
15873     icon: undefined,
15874     /**
15875      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15876      */
15877     itemCls : "x-menu-item",
15878     /**
15879      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15880      */
15881     canActivate : true,
15882     /**
15883      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15884      */
15885     showDelay: 200,
15886     // doc'd in BaseItem
15887     hideDelay: 200,
15888
15889     // private
15890     ctype: "Roo.menu.Item",
15891     
15892     // private
15893     onRender : function(container, position){
15894         var el = document.createElement("a");
15895         el.hideFocus = true;
15896         el.unselectable = "on";
15897         el.href = this.href || "#";
15898         if(this.hrefTarget){
15899             el.target = this.hrefTarget;
15900         }
15901         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15902         
15903         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15904         
15905         el.innerHTML = String.format(
15906                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15907                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15908         this.el = el;
15909         Roo.menu.Item.superclass.onRender.call(this, container, position);
15910     },
15911
15912     /**
15913      * Sets the text to display in this menu item
15914      * @param {String} text The text to display
15915      * @param {Boolean} isHTML true to indicate text is pure html.
15916      */
15917     setText : function(text, isHTML){
15918         if (isHTML) {
15919             this.html = text;
15920         } else {
15921             this.text = text;
15922             this.html = '';
15923         }
15924         if(this.rendered){
15925             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15926      
15927             this.el.update(String.format(
15928                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15929                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15930             this.parentMenu.autoWidth();
15931         }
15932     },
15933
15934     // private
15935     handleClick : function(e){
15936         if(!this.href){ // if no link defined, stop the event automatically
15937             e.stopEvent();
15938         }
15939         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15940     },
15941
15942     // private
15943     activate : function(autoExpand){
15944         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15945             this.focus();
15946             if(autoExpand){
15947                 this.expandMenu();
15948             }
15949         }
15950         return true;
15951     },
15952
15953     // private
15954     shouldDeactivate : function(e){
15955         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15956             if(this.menu && this.menu.isVisible()){
15957                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15958             }
15959             return true;
15960         }
15961         return false;
15962     },
15963
15964     // private
15965     deactivate : function(){
15966         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15967         this.hideMenu();
15968     },
15969
15970     // private
15971     expandMenu : function(autoActivate){
15972         if(!this.disabled && this.menu){
15973             clearTimeout(this.hideTimer);
15974             delete this.hideTimer;
15975             if(!this.menu.isVisible() && !this.showTimer){
15976                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15977             }else if (this.menu.isVisible() && autoActivate){
15978                 this.menu.tryActivate(0, 1);
15979             }
15980         }
15981     },
15982
15983     // private
15984     deferExpand : function(autoActivate){
15985         delete this.showTimer;
15986         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15987         if(autoActivate){
15988             this.menu.tryActivate(0, 1);
15989         }
15990     },
15991
15992     // private
15993     hideMenu : function(){
15994         clearTimeout(this.showTimer);
15995         delete this.showTimer;
15996         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15997             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15998         }
15999     },
16000
16001     // private
16002     deferHide : function(){
16003         delete this.hideTimer;
16004         this.menu.hide();
16005     }
16006 });/*
16007  * Based on:
16008  * Ext JS Library 1.1.1
16009  * Copyright(c) 2006-2007, Ext JS, LLC.
16010  *
16011  * Originally Released Under LGPL - original licence link has changed is not relivant.
16012  *
16013  * Fork - LGPL
16014  * <script type="text/javascript">
16015  */
16016  
16017 /**
16018  * @class Roo.menu.CheckItem
16019  * @extends Roo.menu.Item
16020  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16021  * @constructor
16022  * Creates a new CheckItem
16023  * @param {Object} config Configuration options
16024  */
16025 Roo.menu.CheckItem = function(config){
16026     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16027     this.addEvents({
16028         /**
16029          * @event beforecheckchange
16030          * Fires before the checked value is set, providing an opportunity to cancel if needed
16031          * @param {Roo.menu.CheckItem} this
16032          * @param {Boolean} checked The new checked value that will be set
16033          */
16034         "beforecheckchange" : true,
16035         /**
16036          * @event checkchange
16037          * Fires after the checked value has been set
16038          * @param {Roo.menu.CheckItem} this
16039          * @param {Boolean} checked The checked value that was set
16040          */
16041         "checkchange" : true
16042     });
16043     if(this.checkHandler){
16044         this.on('checkchange', this.checkHandler, this.scope);
16045     }
16046 };
16047 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16048     /**
16049      * @cfg {String} group
16050      * All check items with the same group name will automatically be grouped into a single-select
16051      * radio button group (defaults to '')
16052      */
16053     /**
16054      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16055      */
16056     itemCls : "x-menu-item x-menu-check-item",
16057     /**
16058      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16059      */
16060     groupClass : "x-menu-group-item",
16061
16062     /**
16063      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16064      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16065      * initialized with checked = true will be rendered as checked.
16066      */
16067     checked: false,
16068
16069     // private
16070     ctype: "Roo.menu.CheckItem",
16071
16072     // private
16073     onRender : function(c){
16074         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16075         if(this.group){
16076             this.el.addClass(this.groupClass);
16077         }
16078         Roo.menu.MenuMgr.registerCheckable(this);
16079         if(this.checked){
16080             this.checked = false;
16081             this.setChecked(true, true);
16082         }
16083     },
16084
16085     // private
16086     destroy : function(){
16087         if(this.rendered){
16088             Roo.menu.MenuMgr.unregisterCheckable(this);
16089         }
16090         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16091     },
16092
16093     /**
16094      * Set the checked state of this item
16095      * @param {Boolean} checked The new checked value
16096      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16097      */
16098     setChecked : function(state, suppressEvent){
16099         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16100             if(this.container){
16101                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16102             }
16103             this.checked = state;
16104             if(suppressEvent !== true){
16105                 this.fireEvent("checkchange", this, state);
16106             }
16107         }
16108     },
16109
16110     // private
16111     handleClick : function(e){
16112        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16113            this.setChecked(!this.checked);
16114        }
16115        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16116     }
16117 });/*
16118  * Based on:
16119  * Ext JS Library 1.1.1
16120  * Copyright(c) 2006-2007, Ext JS, LLC.
16121  *
16122  * Originally Released Under LGPL - original licence link has changed is not relivant.
16123  *
16124  * Fork - LGPL
16125  * <script type="text/javascript">
16126  */
16127  
16128 /**
16129  * @class Roo.menu.DateItem
16130  * @extends Roo.menu.Adapter
16131  * A menu item that wraps the {@link Roo.DatPicker} component.
16132  * @constructor
16133  * Creates a new DateItem
16134  * @param {Object} config Configuration options
16135  */
16136 Roo.menu.DateItem = function(config){
16137     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16138     /** The Roo.DatePicker object @type Roo.DatePicker */
16139     this.picker = this.component;
16140     this.addEvents({select: true});
16141     
16142     this.picker.on("render", function(picker){
16143         picker.getEl().swallowEvent("click");
16144         picker.container.addClass("x-menu-date-item");
16145     });
16146
16147     this.picker.on("select", this.onSelect, this);
16148 };
16149
16150 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16151     // private
16152     onSelect : function(picker, date){
16153         this.fireEvent("select", this, date, picker);
16154         Roo.menu.DateItem.superclass.handleClick.call(this);
16155     }
16156 });/*
16157  * Based on:
16158  * Ext JS Library 1.1.1
16159  * Copyright(c) 2006-2007, Ext JS, LLC.
16160  *
16161  * Originally Released Under LGPL - original licence link has changed is not relivant.
16162  *
16163  * Fork - LGPL
16164  * <script type="text/javascript">
16165  */
16166  
16167 /**
16168  * @class Roo.menu.ColorItem
16169  * @extends Roo.menu.Adapter
16170  * A menu item that wraps the {@link Roo.ColorPalette} component.
16171  * @constructor
16172  * Creates a new ColorItem
16173  * @param {Object} config Configuration options
16174  */
16175 Roo.menu.ColorItem = function(config){
16176     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16177     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16178     this.palette = this.component;
16179     this.relayEvents(this.palette, ["select"]);
16180     if(this.selectHandler){
16181         this.on('select', this.selectHandler, this.scope);
16182     }
16183 };
16184 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16185  * Based on:
16186  * Ext JS Library 1.1.1
16187  * Copyright(c) 2006-2007, Ext JS, LLC.
16188  *
16189  * Originally Released Under LGPL - original licence link has changed is not relivant.
16190  *
16191  * Fork - LGPL
16192  * <script type="text/javascript">
16193  */
16194  
16195
16196 /**
16197  * @class Roo.menu.DateMenu
16198  * @extends Roo.menu.Menu
16199  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16200  * @constructor
16201  * Creates a new DateMenu
16202  * @param {Object} config Configuration options
16203  */
16204 Roo.menu.DateMenu = function(config){
16205     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16206     this.plain = true;
16207     var di = new Roo.menu.DateItem(config);
16208     this.add(di);
16209     /**
16210      * The {@link Roo.DatePicker} instance for this DateMenu
16211      * @type DatePicker
16212      */
16213     this.picker = di.picker;
16214     /**
16215      * @event select
16216      * @param {DatePicker} picker
16217      * @param {Date} date
16218      */
16219     this.relayEvents(di, ["select"]);
16220     this.on('beforeshow', function(){
16221         if(this.picker){
16222             this.picker.hideMonthPicker(false);
16223         }
16224     }, this);
16225 };
16226 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16227     cls:'x-date-menu'
16228 });/*
16229  * Based on:
16230  * Ext JS Library 1.1.1
16231  * Copyright(c) 2006-2007, Ext JS, LLC.
16232  *
16233  * Originally Released Under LGPL - original licence link has changed is not relivant.
16234  *
16235  * Fork - LGPL
16236  * <script type="text/javascript">
16237  */
16238  
16239
16240 /**
16241  * @class Roo.menu.ColorMenu
16242  * @extends Roo.menu.Menu
16243  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16244  * @constructor
16245  * Creates a new ColorMenu
16246  * @param {Object} config Configuration options
16247  */
16248 Roo.menu.ColorMenu = function(config){
16249     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16250     this.plain = true;
16251     var ci = new Roo.menu.ColorItem(config);
16252     this.add(ci);
16253     /**
16254      * The {@link Roo.ColorPalette} instance for this ColorMenu
16255      * @type ColorPalette
16256      */
16257     this.palette = ci.palette;
16258     /**
16259      * @event select
16260      * @param {ColorPalette} palette
16261      * @param {String} color
16262      */
16263     this.relayEvents(ci, ["select"]);
16264 };
16265 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16266  * Based on:
16267  * Ext JS Library 1.1.1
16268  * Copyright(c) 2006-2007, Ext JS, LLC.
16269  *
16270  * Originally Released Under LGPL - original licence link has changed is not relivant.
16271  *
16272  * Fork - LGPL
16273  * <script type="text/javascript">
16274  */
16275  
16276 /**
16277  * @class Roo.form.TextItem
16278  * @extends Roo.BoxComponent
16279  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16280  * @constructor
16281  * Creates a new TextItem
16282  * @param {Object} config Configuration options
16283  */
16284 Roo.form.TextItem = function(config){
16285     Roo.form.TextItem.superclass.constructor.call(this, config);
16286 };
16287
16288 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
16289     
16290     /**
16291      * @cfg {String} tag the tag for this item (default div)
16292      */
16293     tag : 'div',
16294     /**
16295      * @cfg {String} html the content for this item
16296      */
16297     html : '',
16298     
16299     getAutoCreate : function()
16300     {
16301         var cfg = {
16302             id: this.id,
16303             tag: this.tag,
16304             html: this.html,
16305             cls: 'x-form-item'
16306         };
16307         
16308         return cfg;
16309         
16310     },
16311     
16312     onRender : function(ct, position)
16313     {
16314         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16315         
16316         if(!this.el){
16317             var cfg = this.getAutoCreate();
16318             if(!cfg.name){
16319                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16320             }
16321             if (!cfg.name.length) {
16322                 delete cfg.name;
16323             }
16324             this.el = ct.createChild(cfg, position);
16325         }
16326     }
16327     
16328 });/*
16329  * Based on:
16330  * Ext JS Library 1.1.1
16331  * Copyright(c) 2006-2007, Ext JS, LLC.
16332  *
16333  * Originally Released Under LGPL - original licence link has changed is not relivant.
16334  *
16335  * Fork - LGPL
16336  * <script type="text/javascript">
16337  */
16338  
16339 /**
16340  * @class Roo.form.Field
16341  * @extends Roo.BoxComponent
16342  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16343  * @constructor
16344  * Creates a new Field
16345  * @param {Object} config Configuration options
16346  */
16347 Roo.form.Field = function(config){
16348     Roo.form.Field.superclass.constructor.call(this, config);
16349 };
16350
16351 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16352     /**
16353      * @cfg {String} fieldLabel Label to use when rendering a form.
16354      */
16355        /**
16356      * @cfg {String} qtip Mouse over tip
16357      */
16358      
16359     /**
16360      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16361      */
16362     invalidClass : "x-form-invalid",
16363     /**
16364      * @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")
16365      */
16366     invalidText : "The value in this field is invalid",
16367     /**
16368      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16369      */
16370     focusClass : "x-form-focus",
16371     /**
16372      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16373       automatic validation (defaults to "keyup").
16374      */
16375     validationEvent : "keyup",
16376     /**
16377      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16378      */
16379     validateOnBlur : true,
16380     /**
16381      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16382      */
16383     validationDelay : 250,
16384     /**
16385      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16386      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16387      */
16388     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16389     /**
16390      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16391      */
16392     fieldClass : "x-form-field",
16393     /**
16394      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16395      *<pre>
16396 Value         Description
16397 -----------   ----------------------------------------------------------------------
16398 qtip          Display a quick tip when the user hovers over the field
16399 title         Display a default browser title attribute popup
16400 under         Add a block div beneath the field containing the error text
16401 side          Add an error icon to the right of the field with a popup on hover
16402 [element id]  Add the error text directly to the innerHTML of the specified element
16403 </pre>
16404      */
16405     msgTarget : 'qtip',
16406     /**
16407      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16408      */
16409     msgFx : 'normal',
16410
16411     /**
16412      * @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.
16413      */
16414     readOnly : false,
16415
16416     /**
16417      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16418      */
16419     disabled : false,
16420
16421     /**
16422      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16423      */
16424     inputType : undefined,
16425     
16426     /**
16427      * @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).
16428          */
16429         tabIndex : undefined,
16430         
16431     // private
16432     isFormField : true,
16433
16434     // private
16435     hasFocus : false,
16436     /**
16437      * @property {Roo.Element} fieldEl
16438      * Element Containing the rendered Field (with label etc.)
16439      */
16440     /**
16441      * @cfg {Mixed} value A value to initialize this field with.
16442      */
16443     value : undefined,
16444
16445     /**
16446      * @cfg {String} name The field's HTML name attribute.
16447      */
16448     /**
16449      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16450      */
16451     // private
16452     loadedValue : false,
16453      
16454      
16455         // private ??
16456         initComponent : function(){
16457         Roo.form.Field.superclass.initComponent.call(this);
16458         this.addEvents({
16459             /**
16460              * @event focus
16461              * Fires when this field receives input focus.
16462              * @param {Roo.form.Field} this
16463              */
16464             focus : true,
16465             /**
16466              * @event blur
16467              * Fires when this field loses input focus.
16468              * @param {Roo.form.Field} this
16469              */
16470             blur : true,
16471             /**
16472              * @event specialkey
16473              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16474              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16475              * @param {Roo.form.Field} this
16476              * @param {Roo.EventObject} e The event object
16477              */
16478             specialkey : true,
16479             /**
16480              * @event change
16481              * Fires just before the field blurs if the field value has changed.
16482              * @param {Roo.form.Field} this
16483              * @param {Mixed} newValue The new value
16484              * @param {Mixed} oldValue The original value
16485              */
16486             change : true,
16487             /**
16488              * @event invalid
16489              * Fires after the field has been marked as invalid.
16490              * @param {Roo.form.Field} this
16491              * @param {String} msg The validation message
16492              */
16493             invalid : true,
16494             /**
16495              * @event valid
16496              * Fires after the field has been validated with no errors.
16497              * @param {Roo.form.Field} this
16498              */
16499             valid : true,
16500              /**
16501              * @event keyup
16502              * Fires after the key up
16503              * @param {Roo.form.Field} this
16504              * @param {Roo.EventObject}  e The event Object
16505              */
16506             keyup : true
16507         });
16508     },
16509
16510     /**
16511      * Returns the name attribute of the field if available
16512      * @return {String} name The field name
16513      */
16514     getName: function(){
16515          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16516     },
16517
16518     // private
16519     onRender : function(ct, position){
16520         Roo.form.Field.superclass.onRender.call(this, ct, position);
16521         if(!this.el){
16522             var cfg = this.getAutoCreate();
16523             if(!cfg.name){
16524                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16525             }
16526             if (!cfg.name.length) {
16527                 delete cfg.name;
16528             }
16529             if(this.inputType){
16530                 cfg.type = this.inputType;
16531             }
16532             this.el = ct.createChild(cfg, position);
16533         }
16534         var type = this.el.dom.type;
16535         if(type){
16536             if(type == 'password'){
16537                 type = 'text';
16538             }
16539             this.el.addClass('x-form-'+type);
16540         }
16541         if(this.readOnly){
16542             this.el.dom.readOnly = true;
16543         }
16544         if(this.tabIndex !== undefined){
16545             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16546         }
16547
16548         this.el.addClass([this.fieldClass, this.cls]);
16549         this.initValue();
16550     },
16551
16552     /**
16553      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16554      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16555      * @return {Roo.form.Field} this
16556      */
16557     applyTo : function(target){
16558         this.allowDomMove = false;
16559         this.el = Roo.get(target);
16560         this.render(this.el.dom.parentNode);
16561         return this;
16562     },
16563
16564     // private
16565     initValue : function(){
16566         if(this.value !== undefined){
16567             this.setValue(this.value);
16568         }else if(this.el.dom.value.length > 0){
16569             this.setValue(this.el.dom.value);
16570         }
16571     },
16572
16573     /**
16574      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16575      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16576      */
16577     isDirty : function() {
16578         if(this.disabled) {
16579             return false;
16580         }
16581         return String(this.getValue()) !== String(this.originalValue);
16582     },
16583
16584     /**
16585      * stores the current value in loadedValue
16586      */
16587     resetHasChanged : function()
16588     {
16589         this.loadedValue = String(this.getValue());
16590     },
16591     /**
16592      * checks the current value against the 'loaded' value.
16593      * Note - will return false if 'resetHasChanged' has not been called first.
16594      */
16595     hasChanged : function()
16596     {
16597         if(this.disabled || this.readOnly) {
16598             return false;
16599         }
16600         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16601     },
16602     
16603     
16604     
16605     // private
16606     afterRender : function(){
16607         Roo.form.Field.superclass.afterRender.call(this);
16608         this.initEvents();
16609     },
16610
16611     // private
16612     fireKey : function(e){
16613         //Roo.log('field ' + e.getKey());
16614         if(e.isNavKeyPress()){
16615             this.fireEvent("specialkey", this, e);
16616         }
16617     },
16618
16619     /**
16620      * Resets the current field value to the originally loaded value and clears any validation messages
16621      */
16622     reset : function(){
16623         this.setValue(this.resetValue);
16624         this.originalValue = this.getValue();
16625         this.clearInvalid();
16626     },
16627
16628     // private
16629     initEvents : function(){
16630         // safari killled keypress - so keydown is now used..
16631         this.el.on("keydown" , this.fireKey,  this);
16632         this.el.on("focus", this.onFocus,  this);
16633         this.el.on("blur", this.onBlur,  this);
16634         this.el.relayEvent('keyup', this);
16635
16636         // reference to original value for reset
16637         this.originalValue = this.getValue();
16638         this.resetValue =  this.getValue();
16639     },
16640
16641     // private
16642     onFocus : function(){
16643         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16644             this.el.addClass(this.focusClass);
16645         }
16646         if(!this.hasFocus){
16647             this.hasFocus = true;
16648             this.startValue = this.getValue();
16649             this.fireEvent("focus", this);
16650         }
16651     },
16652
16653     beforeBlur : Roo.emptyFn,
16654
16655     // private
16656     onBlur : function(){
16657         this.beforeBlur();
16658         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16659             this.el.removeClass(this.focusClass);
16660         }
16661         this.hasFocus = false;
16662         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16663             this.validate();
16664         }
16665         var v = this.getValue();
16666         if(String(v) !== String(this.startValue)){
16667             this.fireEvent('change', this, v, this.startValue);
16668         }
16669         this.fireEvent("blur", this);
16670     },
16671
16672     /**
16673      * Returns whether or not the field value is currently valid
16674      * @param {Boolean} preventMark True to disable marking the field invalid
16675      * @return {Boolean} True if the value is valid, else false
16676      */
16677     isValid : function(preventMark){
16678         if(this.disabled){
16679             return true;
16680         }
16681         var restore = this.preventMark;
16682         this.preventMark = preventMark === true;
16683         var v = this.validateValue(this.processValue(this.getRawValue()));
16684         this.preventMark = restore;
16685         return v;
16686     },
16687
16688     /**
16689      * Validates the field value
16690      * @return {Boolean} True if the value is valid, else false
16691      */
16692     validate : function(){
16693         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16694             this.clearInvalid();
16695             return true;
16696         }
16697         return false;
16698     },
16699
16700     processValue : function(value){
16701         return value;
16702     },
16703
16704     // private
16705     // Subclasses should provide the validation implementation by overriding this
16706     validateValue : function(value){
16707         return true;
16708     },
16709
16710     /**
16711      * Mark this field as invalid
16712      * @param {String} msg The validation message
16713      */
16714     markInvalid : function(msg){
16715         if(!this.rendered || this.preventMark){ // not rendered
16716             return;
16717         }
16718         
16719         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16720         
16721         obj.el.addClass(this.invalidClass);
16722         msg = msg || this.invalidText;
16723         switch(this.msgTarget){
16724             case 'qtip':
16725                 obj.el.dom.qtip = msg;
16726                 obj.el.dom.qclass = 'x-form-invalid-tip';
16727                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16728                     Roo.QuickTips.enable();
16729                 }
16730                 break;
16731             case 'title':
16732                 this.el.dom.title = msg;
16733                 break;
16734             case 'under':
16735                 if(!this.errorEl){
16736                     var elp = this.el.findParent('.x-form-element', 5, true);
16737                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16738                     this.errorEl.setWidth(elp.getWidth(true)-20);
16739                 }
16740                 this.errorEl.update(msg);
16741                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16742                 break;
16743             case 'side':
16744                 if(!this.errorIcon){
16745                     var elp = this.el.findParent('.x-form-element', 5, true);
16746                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16747                 }
16748                 this.alignErrorIcon();
16749                 this.errorIcon.dom.qtip = msg;
16750                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16751                 this.errorIcon.show();
16752                 this.on('resize', this.alignErrorIcon, this);
16753                 break;
16754             default:
16755                 var t = Roo.getDom(this.msgTarget);
16756                 t.innerHTML = msg;
16757                 t.style.display = this.msgDisplay;
16758                 break;
16759         }
16760         this.fireEvent('invalid', this, msg);
16761     },
16762
16763     // private
16764     alignErrorIcon : function(){
16765         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16766     },
16767
16768     /**
16769      * Clear any invalid styles/messages for this field
16770      */
16771     clearInvalid : function(){
16772         if(!this.rendered || this.preventMark){ // not rendered
16773             return;
16774         }
16775         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16776         
16777         obj.el.removeClass(this.invalidClass);
16778         switch(this.msgTarget){
16779             case 'qtip':
16780                 obj.el.dom.qtip = '';
16781                 break;
16782             case 'title':
16783                 this.el.dom.title = '';
16784                 break;
16785             case 'under':
16786                 if(this.errorEl){
16787                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16788                 }
16789                 break;
16790             case 'side':
16791                 if(this.errorIcon){
16792                     this.errorIcon.dom.qtip = '';
16793                     this.errorIcon.hide();
16794                     this.un('resize', this.alignErrorIcon, this);
16795                 }
16796                 break;
16797             default:
16798                 var t = Roo.getDom(this.msgTarget);
16799                 t.innerHTML = '';
16800                 t.style.display = 'none';
16801                 break;
16802         }
16803         this.fireEvent('valid', this);
16804     },
16805
16806     /**
16807      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16808      * @return {Mixed} value The field value
16809      */
16810     getRawValue : function(){
16811         var v = this.el.getValue();
16812         
16813         return v;
16814     },
16815
16816     /**
16817      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16818      * @return {Mixed} value The field value
16819      */
16820     getValue : function(){
16821         var v = this.el.getValue();
16822          
16823         return v;
16824     },
16825
16826     /**
16827      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16828      * @param {Mixed} value The value to set
16829      */
16830     setRawValue : function(v){
16831         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16832     },
16833
16834     /**
16835      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16836      * @param {Mixed} value The value to set
16837      */
16838     setValue : function(v){
16839         this.value = v;
16840         if(this.rendered){
16841             this.el.dom.value = (v === null || v === undefined ? '' : v);
16842              this.validate();
16843         }
16844     },
16845
16846     adjustSize : function(w, h){
16847         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16848         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16849         return s;
16850     },
16851
16852     adjustWidth : function(tag, w){
16853         tag = tag.toLowerCase();
16854         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16855             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16856                 if(tag == 'input'){
16857                     return w + 2;
16858                 }
16859                 if(tag == 'textarea'){
16860                     return w-2;
16861                 }
16862             }else if(Roo.isOpera){
16863                 if(tag == 'input'){
16864                     return w + 2;
16865                 }
16866                 if(tag == 'textarea'){
16867                     return w-2;
16868                 }
16869             }
16870         }
16871         return w;
16872     }
16873 });
16874
16875
16876 // anything other than normal should be considered experimental
16877 Roo.form.Field.msgFx = {
16878     normal : {
16879         show: function(msgEl, f){
16880             msgEl.setDisplayed('block');
16881         },
16882
16883         hide : function(msgEl, f){
16884             msgEl.setDisplayed(false).update('');
16885         }
16886     },
16887
16888     slide : {
16889         show: function(msgEl, f){
16890             msgEl.slideIn('t', {stopFx:true});
16891         },
16892
16893         hide : function(msgEl, f){
16894             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16895         }
16896     },
16897
16898     slideRight : {
16899         show: function(msgEl, f){
16900             msgEl.fixDisplay();
16901             msgEl.alignTo(f.el, 'tl-tr');
16902             msgEl.slideIn('l', {stopFx:true});
16903         },
16904
16905         hide : function(msgEl, f){
16906             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16907         }
16908     }
16909 };/*
16910  * Based on:
16911  * Ext JS Library 1.1.1
16912  * Copyright(c) 2006-2007, Ext JS, LLC.
16913  *
16914  * Originally Released Under LGPL - original licence link has changed is not relivant.
16915  *
16916  * Fork - LGPL
16917  * <script type="text/javascript">
16918  */
16919  
16920
16921 /**
16922  * @class Roo.form.TextField
16923  * @extends Roo.form.Field
16924  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16925  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16926  * @constructor
16927  * Creates a new TextField
16928  * @param {Object} config Configuration options
16929  */
16930 Roo.form.TextField = function(config){
16931     Roo.form.TextField.superclass.constructor.call(this, config);
16932     this.addEvents({
16933         /**
16934          * @event autosize
16935          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16936          * according to the default logic, but this event provides a hook for the developer to apply additional
16937          * logic at runtime to resize the field if needed.
16938              * @param {Roo.form.Field} this This text field
16939              * @param {Number} width The new field width
16940              */
16941         autosize : true
16942     });
16943 };
16944
16945 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16946     /**
16947      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16948      */
16949     grow : false,
16950     /**
16951      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16952      */
16953     growMin : 30,
16954     /**
16955      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16956      */
16957     growMax : 800,
16958     /**
16959      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16960      */
16961     vtype : null,
16962     /**
16963      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16964      */
16965     maskRe : null,
16966     /**
16967      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16968      */
16969     disableKeyFilter : false,
16970     /**
16971      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16972      */
16973     allowBlank : true,
16974     /**
16975      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16976      */
16977     minLength : 0,
16978     /**
16979      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16980      */
16981     maxLength : Number.MAX_VALUE,
16982     /**
16983      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16984      */
16985     minLengthText : "The minimum length for this field is {0}",
16986     /**
16987      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16988      */
16989     maxLengthText : "The maximum length for this field is {0}",
16990     /**
16991      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16992      */
16993     selectOnFocus : false,
16994     /**
16995      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16996      */    
16997     allowLeadingSpace : false,
16998     /**
16999      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
17000      */
17001     blankText : "This field is required",
17002     /**
17003      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
17004      * If available, this function will be called only after the basic validators all return true, and will be passed the
17005      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
17006      */
17007     validator : null,
17008     /**
17009      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
17010      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
17011      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
17012      */
17013     regex : null,
17014     /**
17015      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
17016      */
17017     regexText : "",
17018     /**
17019      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
17020      */
17021     emptyText : null,
17022    
17023
17024     // private
17025     initEvents : function()
17026     {
17027         if (this.emptyText) {
17028             this.el.attr('placeholder', this.emptyText);
17029         }
17030         
17031         Roo.form.TextField.superclass.initEvents.call(this);
17032         if(this.validationEvent == 'keyup'){
17033             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
17034             this.el.on('keyup', this.filterValidation, this);
17035         }
17036         else if(this.validationEvent !== false){
17037             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
17038         }
17039         
17040         if(this.selectOnFocus){
17041             this.on("focus", this.preFocus, this);
17042         }
17043         if (!this.allowLeadingSpace) {
17044             this.on('blur', this.cleanLeadingSpace, this);
17045         }
17046         
17047         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
17048             this.el.on("keypress", this.filterKeys, this);
17049         }
17050         if(this.grow){
17051             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
17052             this.el.on("click", this.autoSize,  this);
17053         }
17054         if(this.el.is('input[type=password]') && Roo.isSafari){
17055             this.el.on('keydown', this.SafariOnKeyDown, this);
17056         }
17057     },
17058
17059     processValue : function(value){
17060         if(this.stripCharsRe){
17061             var newValue = value.replace(this.stripCharsRe, '');
17062             if(newValue !== value){
17063                 this.setRawValue(newValue);
17064                 return newValue;
17065             }
17066         }
17067         return value;
17068     },
17069
17070     filterValidation : function(e){
17071         if(!e.isNavKeyPress()){
17072             this.validationTask.delay(this.validationDelay);
17073         }
17074     },
17075
17076     // private
17077     onKeyUp : function(e){
17078         if(!e.isNavKeyPress()){
17079             this.autoSize();
17080         }
17081     },
17082     // private - clean the leading white space
17083     cleanLeadingSpace : function(e)
17084     {
17085         if ( this.inputType == 'file') {
17086             return;
17087         }
17088         
17089         this.setValue((this.getValue() + '').replace(/^\s+/,''));
17090     },
17091     /**
17092      * Resets the current field value to the originally-loaded value and clears any validation messages.
17093      *  
17094      */
17095     reset : function(){
17096         Roo.form.TextField.superclass.reset.call(this);
17097        
17098     }, 
17099     // private
17100     preFocus : function(){
17101         
17102         if(this.selectOnFocus){
17103             this.el.dom.select();
17104         }
17105     },
17106
17107     
17108     // private
17109     filterKeys : function(e){
17110         var k = e.getKey();
17111         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17112             return;
17113         }
17114         var c = e.getCharCode(), cc = String.fromCharCode(c);
17115         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17116             return;
17117         }
17118         if(!this.maskRe.test(cc)){
17119             e.stopEvent();
17120         }
17121     },
17122
17123     setValue : function(v){
17124         
17125         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17126         
17127         this.autoSize();
17128     },
17129
17130     /**
17131      * Validates a value according to the field's validation rules and marks the field as invalid
17132      * if the validation fails
17133      * @param {Mixed} value The value to validate
17134      * @return {Boolean} True if the value is valid, else false
17135      */
17136     validateValue : function(value){
17137         if(value.length < 1)  { // if it's blank
17138              if(this.allowBlank){
17139                 this.clearInvalid();
17140                 return true;
17141              }else{
17142                 this.markInvalid(this.blankText);
17143                 return false;
17144              }
17145         }
17146         if(value.length < this.minLength){
17147             this.markInvalid(String.format(this.minLengthText, this.minLength));
17148             return false;
17149         }
17150         if(value.length > this.maxLength){
17151             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17152             return false;
17153         }
17154         if(this.vtype){
17155             var vt = Roo.form.VTypes;
17156             if(!vt[this.vtype](value, this)){
17157                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17158                 return false;
17159             }
17160         }
17161         if(typeof this.validator == "function"){
17162             var msg = this.validator(value);
17163             if(msg !== true){
17164                 this.markInvalid(msg);
17165                 return false;
17166             }
17167         }
17168         if(this.regex && !this.regex.test(value)){
17169             this.markInvalid(this.regexText);
17170             return false;
17171         }
17172         return true;
17173     },
17174
17175     /**
17176      * Selects text in this field
17177      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17178      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17179      */
17180     selectText : function(start, end){
17181         var v = this.getRawValue();
17182         if(v.length > 0){
17183             start = start === undefined ? 0 : start;
17184             end = end === undefined ? v.length : end;
17185             var d = this.el.dom;
17186             if(d.setSelectionRange){
17187                 d.setSelectionRange(start, end);
17188             }else if(d.createTextRange){
17189                 var range = d.createTextRange();
17190                 range.moveStart("character", start);
17191                 range.moveEnd("character", v.length-end);
17192                 range.select();
17193             }
17194         }
17195     },
17196
17197     /**
17198      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17199      * This only takes effect if grow = true, and fires the autosize event.
17200      */
17201     autoSize : function(){
17202         if(!this.grow || !this.rendered){
17203             return;
17204         }
17205         if(!this.metrics){
17206             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17207         }
17208         var el = this.el;
17209         var v = el.dom.value;
17210         var d = document.createElement('div');
17211         d.appendChild(document.createTextNode(v));
17212         v = d.innerHTML;
17213         d = null;
17214         v += "&#160;";
17215         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17216         this.el.setWidth(w);
17217         this.fireEvent("autosize", this, w);
17218     },
17219     
17220     // private
17221     SafariOnKeyDown : function(event)
17222     {
17223         // this is a workaround for a password hang bug on chrome/ webkit.
17224         
17225         var isSelectAll = false;
17226         
17227         if(this.el.dom.selectionEnd > 0){
17228             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17229         }
17230         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17231             event.preventDefault();
17232             this.setValue('');
17233             return;
17234         }
17235         
17236         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17237             
17238             event.preventDefault();
17239             // this is very hacky as keydown always get's upper case.
17240             
17241             var cc = String.fromCharCode(event.getCharCode());
17242             
17243             
17244             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17245             
17246         }
17247         
17248         
17249     }
17250 });/*
17251  * Based on:
17252  * Ext JS Library 1.1.1
17253  * Copyright(c) 2006-2007, Ext JS, LLC.
17254  *
17255  * Originally Released Under LGPL - original licence link has changed is not relivant.
17256  *
17257  * Fork - LGPL
17258  * <script type="text/javascript">
17259  */
17260  
17261 /**
17262  * @class Roo.form.Hidden
17263  * @extends Roo.form.TextField
17264  * Simple Hidden element used on forms 
17265  * 
17266  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17267  * 
17268  * @constructor
17269  * Creates a new Hidden form element.
17270  * @param {Object} config Configuration options
17271  */
17272
17273
17274
17275 // easy hidden field...
17276 Roo.form.Hidden = function(config){
17277     Roo.form.Hidden.superclass.constructor.call(this, config);
17278 };
17279   
17280 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17281     fieldLabel:      '',
17282     inputType:      'hidden',
17283     width:          50,
17284     allowBlank:     true,
17285     labelSeparator: '',
17286     hidden:         true,
17287     itemCls :       'x-form-item-display-none'
17288
17289
17290 });
17291
17292
17293 /*
17294  * Based on:
17295  * Ext JS Library 1.1.1
17296  * Copyright(c) 2006-2007, Ext JS, LLC.
17297  *
17298  * Originally Released Under LGPL - original licence link has changed is not relivant.
17299  *
17300  * Fork - LGPL
17301  * <script type="text/javascript">
17302  */
17303  
17304 /**
17305  * @class Roo.form.TriggerField
17306  * @extends Roo.form.TextField
17307  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17308  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17309  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17310  * for which you can provide a custom implementation.  For example:
17311  * <pre><code>
17312 var trigger = new Roo.form.TriggerField();
17313 trigger.onTriggerClick = myTriggerFn;
17314 trigger.applyTo('my-field');
17315 </code></pre>
17316  *
17317  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17318  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17319  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17320  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17321  * @constructor
17322  * Create a new TriggerField.
17323  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17324  * to the base TextField)
17325  */
17326 Roo.form.TriggerField = function(config){
17327     this.mimicing = false;
17328     Roo.form.TriggerField.superclass.constructor.call(this, config);
17329 };
17330
17331 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17332     /**
17333      * @cfg {String} triggerClass A CSS class to apply to the trigger
17334      */
17335     /**
17336      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17337      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17338      */
17339     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17340     /**
17341      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17342      */
17343     hideTrigger:false,
17344
17345     /** @cfg {Boolean} grow @hide */
17346     /** @cfg {Number} growMin @hide */
17347     /** @cfg {Number} growMax @hide */
17348
17349     /**
17350      * @hide 
17351      * @method
17352      */
17353     autoSize: Roo.emptyFn,
17354     // private
17355     monitorTab : true,
17356     // private
17357     deferHeight : true,
17358
17359     
17360     actionMode : 'wrap',
17361     // private
17362     onResize : function(w, h){
17363         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17364         if(typeof w == 'number'){
17365             var x = w - this.trigger.getWidth();
17366             this.el.setWidth(this.adjustWidth('input', x));
17367             this.trigger.setStyle('left', x+'px');
17368         }
17369     },
17370
17371     // private
17372     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17373
17374     // private
17375     getResizeEl : function(){
17376         return this.wrap;
17377     },
17378
17379     // private
17380     getPositionEl : function(){
17381         return this.wrap;
17382     },
17383
17384     // private
17385     alignErrorIcon : function(){
17386         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17387     },
17388
17389     // private
17390     onRender : function(ct, position){
17391         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17392         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17393         this.trigger = this.wrap.createChild(this.triggerConfig ||
17394                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17395         if(this.hideTrigger){
17396             this.trigger.setDisplayed(false);
17397         }
17398         this.initTrigger();
17399         if(!this.width){
17400             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17401         }
17402     },
17403
17404     // private
17405     initTrigger : function(){
17406         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17407         this.trigger.addClassOnOver('x-form-trigger-over');
17408         this.trigger.addClassOnClick('x-form-trigger-click');
17409     },
17410
17411     // private
17412     onDestroy : function(){
17413         if(this.trigger){
17414             this.trigger.removeAllListeners();
17415             this.trigger.remove();
17416         }
17417         if(this.wrap){
17418             this.wrap.remove();
17419         }
17420         Roo.form.TriggerField.superclass.onDestroy.call(this);
17421     },
17422
17423     // private
17424     onFocus : function(){
17425         Roo.form.TriggerField.superclass.onFocus.call(this);
17426         if(!this.mimicing){
17427             this.wrap.addClass('x-trigger-wrap-focus');
17428             this.mimicing = true;
17429             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17430             if(this.monitorTab){
17431                 this.el.on("keydown", this.checkTab, this);
17432             }
17433         }
17434     },
17435
17436     // private
17437     checkTab : function(e){
17438         if(e.getKey() == e.TAB){
17439             this.triggerBlur();
17440         }
17441     },
17442
17443     // private
17444     onBlur : function(){
17445         // do nothing
17446     },
17447
17448     // private
17449     mimicBlur : function(e, t){
17450         if(!this.wrap.contains(t) && this.validateBlur()){
17451             this.triggerBlur();
17452         }
17453     },
17454
17455     // private
17456     triggerBlur : function(){
17457         this.mimicing = false;
17458         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17459         if(this.monitorTab){
17460             this.el.un("keydown", this.checkTab, this);
17461         }
17462         this.wrap.removeClass('x-trigger-wrap-focus');
17463         Roo.form.TriggerField.superclass.onBlur.call(this);
17464     },
17465
17466     // private
17467     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17468     validateBlur : function(e, t){
17469         return true;
17470     },
17471
17472     // private
17473     onDisable : function(){
17474         Roo.form.TriggerField.superclass.onDisable.call(this);
17475         if(this.wrap){
17476             this.wrap.addClass('x-item-disabled');
17477         }
17478     },
17479
17480     // private
17481     onEnable : function(){
17482         Roo.form.TriggerField.superclass.onEnable.call(this);
17483         if(this.wrap){
17484             this.wrap.removeClass('x-item-disabled');
17485         }
17486     },
17487
17488     // private
17489     onShow : function(){
17490         var ae = this.getActionEl();
17491         
17492         if(ae){
17493             ae.dom.style.display = '';
17494             ae.dom.style.visibility = 'visible';
17495         }
17496     },
17497
17498     // private
17499     
17500     onHide : function(){
17501         var ae = this.getActionEl();
17502         ae.dom.style.display = 'none';
17503     },
17504
17505     /**
17506      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17507      * by an implementing function.
17508      * @method
17509      * @param {EventObject} e
17510      */
17511     onTriggerClick : Roo.emptyFn
17512 });
17513
17514 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17515 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17516 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17517 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17518     initComponent : function(){
17519         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17520
17521         this.triggerConfig = {
17522             tag:'span', cls:'x-form-twin-triggers', cn:[
17523             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17524             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17525         ]};
17526     },
17527
17528     getTrigger : function(index){
17529         return this.triggers[index];
17530     },
17531
17532     initTrigger : function(){
17533         var ts = this.trigger.select('.x-form-trigger', true);
17534         this.wrap.setStyle('overflow', 'hidden');
17535         var triggerField = this;
17536         ts.each(function(t, all, index){
17537             t.hide = function(){
17538                 var w = triggerField.wrap.getWidth();
17539                 this.dom.style.display = 'none';
17540                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17541             };
17542             t.show = function(){
17543                 var w = triggerField.wrap.getWidth();
17544                 this.dom.style.display = '';
17545                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17546             };
17547             var triggerIndex = 'Trigger'+(index+1);
17548
17549             if(this['hide'+triggerIndex]){
17550                 t.dom.style.display = 'none';
17551             }
17552             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17553             t.addClassOnOver('x-form-trigger-over');
17554             t.addClassOnClick('x-form-trigger-click');
17555         }, this);
17556         this.triggers = ts.elements;
17557     },
17558
17559     onTrigger1Click : Roo.emptyFn,
17560     onTrigger2Click : Roo.emptyFn
17561 });/*
17562  * Based on:
17563  * Ext JS Library 1.1.1
17564  * Copyright(c) 2006-2007, Ext JS, LLC.
17565  *
17566  * Originally Released Under LGPL - original licence link has changed is not relivant.
17567  *
17568  * Fork - LGPL
17569  * <script type="text/javascript">
17570  */
17571  
17572 /**
17573  * @class Roo.form.TextArea
17574  * @extends Roo.form.TextField
17575  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17576  * support for auto-sizing.
17577  * @constructor
17578  * Creates a new TextArea
17579  * @param {Object} config Configuration options
17580  */
17581 Roo.form.TextArea = function(config){
17582     Roo.form.TextArea.superclass.constructor.call(this, config);
17583     // these are provided exchanges for backwards compat
17584     // minHeight/maxHeight were replaced by growMin/growMax to be
17585     // compatible with TextField growing config values
17586     if(this.minHeight !== undefined){
17587         this.growMin = this.minHeight;
17588     }
17589     if(this.maxHeight !== undefined){
17590         this.growMax = this.maxHeight;
17591     }
17592 };
17593
17594 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17595     /**
17596      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17597      */
17598     growMin : 60,
17599     /**
17600      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17601      */
17602     growMax: 1000,
17603     /**
17604      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17605      * in the field (equivalent to setting overflow: hidden, defaults to false)
17606      */
17607     preventScrollbars: false,
17608     /**
17609      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17610      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17611      */
17612
17613     // private
17614     onRender : function(ct, position){
17615         if(!this.el){
17616             this.defaultAutoCreate = {
17617                 tag: "textarea",
17618                 style:"width:300px;height:60px;",
17619                 autocomplete: "new-password"
17620             };
17621         }
17622         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17623         if(this.grow){
17624             this.textSizeEl = Roo.DomHelper.append(document.body, {
17625                 tag: "pre", cls: "x-form-grow-sizer"
17626             });
17627             if(this.preventScrollbars){
17628                 this.el.setStyle("overflow", "hidden");
17629             }
17630             this.el.setHeight(this.growMin);
17631         }
17632     },
17633
17634     onDestroy : function(){
17635         if(this.textSizeEl){
17636             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17637         }
17638         Roo.form.TextArea.superclass.onDestroy.call(this);
17639     },
17640
17641     // private
17642     onKeyUp : function(e){
17643         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17644             this.autoSize();
17645         }
17646     },
17647
17648     /**
17649      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17650      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17651      */
17652     autoSize : function(){
17653         if(!this.grow || !this.textSizeEl){
17654             return;
17655         }
17656         var el = this.el;
17657         var v = el.dom.value;
17658         var ts = this.textSizeEl;
17659
17660         ts.innerHTML = '';
17661         ts.appendChild(document.createTextNode(v));
17662         v = ts.innerHTML;
17663
17664         Roo.fly(ts).setWidth(this.el.getWidth());
17665         if(v.length < 1){
17666             v = "&#160;&#160;";
17667         }else{
17668             if(Roo.isIE){
17669                 v = v.replace(/\n/g, '<p>&#160;</p>');
17670             }
17671             v += "&#160;\n&#160;";
17672         }
17673         ts.innerHTML = v;
17674         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17675         if(h != this.lastHeight){
17676             this.lastHeight = h;
17677             this.el.setHeight(h);
17678             this.fireEvent("autosize", this, h);
17679         }
17680     }
17681 });/*
17682  * Based on:
17683  * Ext JS Library 1.1.1
17684  * Copyright(c) 2006-2007, Ext JS, LLC.
17685  *
17686  * Originally Released Under LGPL - original licence link has changed is not relivant.
17687  *
17688  * Fork - LGPL
17689  * <script type="text/javascript">
17690  */
17691  
17692
17693 /**
17694  * @class Roo.form.NumberField
17695  * @extends Roo.form.TextField
17696  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17697  * @constructor
17698  * Creates a new NumberField
17699  * @param {Object} config Configuration options
17700  */
17701 Roo.form.NumberField = function(config){
17702     Roo.form.NumberField.superclass.constructor.call(this, config);
17703 };
17704
17705 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17706     /**
17707      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17708      */
17709     fieldClass: "x-form-field x-form-num-field",
17710     /**
17711      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17712      */
17713     allowDecimals : true,
17714     /**
17715      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17716      */
17717     decimalSeparator : ".",
17718     /**
17719      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17720      */
17721     decimalPrecision : 2,
17722     /**
17723      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17724      */
17725     allowNegative : true,
17726     /**
17727      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17728      */
17729     minValue : Number.NEGATIVE_INFINITY,
17730     /**
17731      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17732      */
17733     maxValue : Number.MAX_VALUE,
17734     /**
17735      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17736      */
17737     minText : "The minimum value for this field is {0}",
17738     /**
17739      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17740      */
17741     maxText : "The maximum value for this field is {0}",
17742     /**
17743      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17744      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17745      */
17746     nanText : "{0} is not a valid number",
17747
17748     // private
17749     initEvents : function(){
17750         Roo.form.NumberField.superclass.initEvents.call(this);
17751         var allowed = "0123456789";
17752         if(this.allowDecimals){
17753             allowed += this.decimalSeparator;
17754         }
17755         if(this.allowNegative){
17756             allowed += "-";
17757         }
17758         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17759         var keyPress = function(e){
17760             var k = e.getKey();
17761             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17762                 return;
17763             }
17764             var c = e.getCharCode();
17765             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17766                 e.stopEvent();
17767             }
17768         };
17769         this.el.on("keypress", keyPress, this);
17770     },
17771
17772     // private
17773     validateValue : function(value){
17774         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17775             return false;
17776         }
17777         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17778              return true;
17779         }
17780         var num = this.parseValue(value);
17781         if(isNaN(num)){
17782             this.markInvalid(String.format(this.nanText, value));
17783             return false;
17784         }
17785         if(num < this.minValue){
17786             this.markInvalid(String.format(this.minText, this.minValue));
17787             return false;
17788         }
17789         if(num > this.maxValue){
17790             this.markInvalid(String.format(this.maxText, this.maxValue));
17791             return false;
17792         }
17793         return true;
17794     },
17795
17796     getValue : function(){
17797         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17798     },
17799
17800     // private
17801     parseValue : function(value){
17802         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17803         return isNaN(value) ? '' : value;
17804     },
17805
17806     // private
17807     fixPrecision : function(value){
17808         var nan = isNaN(value);
17809         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17810             return nan ? '' : value;
17811         }
17812         return parseFloat(value).toFixed(this.decimalPrecision);
17813     },
17814
17815     setValue : function(v){
17816         v = this.fixPrecision(v);
17817         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17818     },
17819
17820     // private
17821     decimalPrecisionFcn : function(v){
17822         return Math.floor(v);
17823     },
17824
17825     beforeBlur : function(){
17826         var v = this.parseValue(this.getRawValue());
17827         if(v){
17828             this.setValue(v);
17829         }
17830     }
17831 });/*
17832  * Based on:
17833  * Ext JS Library 1.1.1
17834  * Copyright(c) 2006-2007, Ext JS, LLC.
17835  *
17836  * Originally Released Under LGPL - original licence link has changed is not relivant.
17837  *
17838  * Fork - LGPL
17839  * <script type="text/javascript">
17840  */
17841  
17842 /**
17843  * @class Roo.form.DateField
17844  * @extends Roo.form.TriggerField
17845  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17846 * @constructor
17847 * Create a new DateField
17848 * @param {Object} config
17849  */
17850 Roo.form.DateField = function(config)
17851 {
17852     Roo.form.DateField.superclass.constructor.call(this, config);
17853     
17854       this.addEvents({
17855          
17856         /**
17857          * @event select
17858          * Fires when a date is selected
17859              * @param {Roo.form.DateField} combo This combo box
17860              * @param {Date} date The date selected
17861              */
17862         'select' : true
17863          
17864     });
17865     
17866     
17867     if(typeof this.minValue == "string") {
17868         this.minValue = this.parseDate(this.minValue);
17869     }
17870     if(typeof this.maxValue == "string") {
17871         this.maxValue = this.parseDate(this.maxValue);
17872     }
17873     this.ddMatch = null;
17874     if(this.disabledDates){
17875         var dd = this.disabledDates;
17876         var re = "(?:";
17877         for(var i = 0; i < dd.length; i++){
17878             re += dd[i];
17879             if(i != dd.length-1) {
17880                 re += "|";
17881             }
17882         }
17883         this.ddMatch = new RegExp(re + ")");
17884     }
17885 };
17886
17887 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17888     /**
17889      * @cfg {String} format
17890      * The default date format string which can be overriden for localization support.  The format must be
17891      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17892      */
17893     format : "m/d/y",
17894     /**
17895      * @cfg {String} altFormats
17896      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17897      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17898      */
17899     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17900     /**
17901      * @cfg {Array} disabledDays
17902      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17903      */
17904     disabledDays : null,
17905     /**
17906      * @cfg {String} disabledDaysText
17907      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17908      */
17909     disabledDaysText : "Disabled",
17910     /**
17911      * @cfg {Array} disabledDates
17912      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17913      * expression so they are very powerful. Some examples:
17914      * <ul>
17915      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17916      * <li>["03/08", "09/16"] would disable those days for every year</li>
17917      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17918      * <li>["03/../2006"] would disable every day in March 2006</li>
17919      * <li>["^03"] would disable every day in every March</li>
17920      * </ul>
17921      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17922      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17923      */
17924     disabledDates : null,
17925     /**
17926      * @cfg {String} disabledDatesText
17927      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17928      */
17929     disabledDatesText : "Disabled",
17930     /**
17931      * @cfg {Date/String} minValue
17932      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17933      * valid format (defaults to null).
17934      */
17935     minValue : null,
17936     /**
17937      * @cfg {Date/String} maxValue
17938      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17939      * valid format (defaults to null).
17940      */
17941     maxValue : null,
17942     /**
17943      * @cfg {String} minText
17944      * The error text to display when the date in the cell is before minValue (defaults to
17945      * 'The date in this field must be after {minValue}').
17946      */
17947     minText : "The date in this field must be equal to or after {0}",
17948     /**
17949      * @cfg {String} maxText
17950      * The error text to display when the date in the cell is after maxValue (defaults to
17951      * 'The date in this field must be before {maxValue}').
17952      */
17953     maxText : "The date in this field must be equal to or before {0}",
17954     /**
17955      * @cfg {String} invalidText
17956      * The error text to display when the date in the field is invalid (defaults to
17957      * '{value} is not a valid date - it must be in the format {format}').
17958      */
17959     invalidText : "{0} is not a valid date - it must be in the format {1}",
17960     /**
17961      * @cfg {String} triggerClass
17962      * An additional CSS class used to style the trigger button.  The trigger will always get the
17963      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17964      * which displays a calendar icon).
17965      */
17966     triggerClass : 'x-form-date-trigger',
17967     
17968
17969     /**
17970      * @cfg {Boolean} useIso
17971      * if enabled, then the date field will use a hidden field to store the 
17972      * real value as iso formated date. default (false)
17973      */ 
17974     useIso : false,
17975     /**
17976      * @cfg {String/Object} autoCreate
17977      * A DomHelper element spec, or true for a default element spec (defaults to
17978      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17979      */ 
17980     // private
17981     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17982     
17983     // private
17984     hiddenField: false,
17985     
17986     onRender : function(ct, position)
17987     {
17988         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17989         if (this.useIso) {
17990             //this.el.dom.removeAttribute('name'); 
17991             Roo.log("Changing name?");
17992             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17993             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17994                     'before', true);
17995             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17996             // prevent input submission
17997             this.hiddenName = this.name;
17998         }
17999             
18000             
18001     },
18002     
18003     // private
18004     validateValue : function(value)
18005     {
18006         value = this.formatDate(value);
18007         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
18008             Roo.log('super failed');
18009             return false;
18010         }
18011         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18012              return true;
18013         }
18014         var svalue = value;
18015         value = this.parseDate(value);
18016         if(!value){
18017             Roo.log('parse date failed' + svalue);
18018             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18019             return false;
18020         }
18021         var time = value.getTime();
18022         if(this.minValue && time < this.minValue.getTime()){
18023             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18024             return false;
18025         }
18026         if(this.maxValue && time > this.maxValue.getTime()){
18027             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18028             return false;
18029         }
18030         if(this.disabledDays){
18031             var day = value.getDay();
18032             for(var i = 0; i < this.disabledDays.length; i++) {
18033                 if(day === this.disabledDays[i]){
18034                     this.markInvalid(this.disabledDaysText);
18035                     return false;
18036                 }
18037             }
18038         }
18039         var fvalue = this.formatDate(value);
18040         if(this.ddMatch && this.ddMatch.test(fvalue)){
18041             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18042             return false;
18043         }
18044         return true;
18045     },
18046
18047     // private
18048     // Provides logic to override the default TriggerField.validateBlur which just returns true
18049     validateBlur : function(){
18050         return !this.menu || !this.menu.isVisible();
18051     },
18052     
18053     getName: function()
18054     {
18055         // returns hidden if it's set..
18056         if (!this.rendered) {return ''};
18057         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18058         
18059     },
18060
18061     /**
18062      * Returns the current date value of the date field.
18063      * @return {Date} The date value
18064      */
18065     getValue : function(){
18066         
18067         return  this.hiddenField ?
18068                 this.hiddenField.value :
18069                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
18070     },
18071
18072     /**
18073      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18074      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
18075      * (the default format used is "m/d/y").
18076      * <br />Usage:
18077      * <pre><code>
18078 //All of these calls set the same date value (May 4, 2006)
18079
18080 //Pass a date object:
18081 var dt = new Date('5/4/06');
18082 dateField.setValue(dt);
18083
18084 //Pass a date string (default format):
18085 dateField.setValue('5/4/06');
18086
18087 //Pass a date string (custom format):
18088 dateField.format = 'Y-m-d';
18089 dateField.setValue('2006-5-4');
18090 </code></pre>
18091      * @param {String/Date} date The date or valid date string
18092      */
18093     setValue : function(date){
18094         if (this.hiddenField) {
18095             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18096         }
18097         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18098         // make sure the value field is always stored as a date..
18099         this.value = this.parseDate(date);
18100         
18101         
18102     },
18103
18104     // private
18105     parseDate : function(value){
18106         if(!value || value instanceof Date){
18107             return value;
18108         }
18109         var v = Date.parseDate(value, this.format);
18110          if (!v && this.useIso) {
18111             v = Date.parseDate(value, 'Y-m-d');
18112         }
18113         if(!v && this.altFormats){
18114             if(!this.altFormatsArray){
18115                 this.altFormatsArray = this.altFormats.split("|");
18116             }
18117             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18118                 v = Date.parseDate(value, this.altFormatsArray[i]);
18119             }
18120         }
18121         return v;
18122     },
18123
18124     // private
18125     formatDate : function(date, fmt){
18126         return (!date || !(date instanceof Date)) ?
18127                date : date.dateFormat(fmt || this.format);
18128     },
18129
18130     // private
18131     menuListeners : {
18132         select: function(m, d){
18133             
18134             this.setValue(d);
18135             this.fireEvent('select', this, d);
18136         },
18137         show : function(){ // retain focus styling
18138             this.onFocus();
18139         },
18140         hide : function(){
18141             this.focus.defer(10, this);
18142             var ml = this.menuListeners;
18143             this.menu.un("select", ml.select,  this);
18144             this.menu.un("show", ml.show,  this);
18145             this.menu.un("hide", ml.hide,  this);
18146         }
18147     },
18148
18149     // private
18150     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18151     onTriggerClick : function(){
18152         if(this.disabled){
18153             return;
18154         }
18155         if(this.menu == null){
18156             this.menu = new Roo.menu.DateMenu();
18157         }
18158         Roo.apply(this.menu.picker,  {
18159             showClear: this.allowBlank,
18160             minDate : this.minValue,
18161             maxDate : this.maxValue,
18162             disabledDatesRE : this.ddMatch,
18163             disabledDatesText : this.disabledDatesText,
18164             disabledDays : this.disabledDays,
18165             disabledDaysText : this.disabledDaysText,
18166             format : this.useIso ? 'Y-m-d' : this.format,
18167             minText : String.format(this.minText, this.formatDate(this.minValue)),
18168             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18169         });
18170         this.menu.on(Roo.apply({}, this.menuListeners, {
18171             scope:this
18172         }));
18173         this.menu.picker.setValue(this.getValue() || new Date());
18174         this.menu.show(this.el, "tl-bl?");
18175     },
18176
18177     beforeBlur : function(){
18178         var v = this.parseDate(this.getRawValue());
18179         if(v){
18180             this.setValue(v);
18181         }
18182     },
18183
18184     /*@
18185      * overide
18186      * 
18187      */
18188     isDirty : function() {
18189         if(this.disabled) {
18190             return false;
18191         }
18192         
18193         if(typeof(this.startValue) === 'undefined'){
18194             return false;
18195         }
18196         
18197         return String(this.getValue()) !== String(this.startValue);
18198         
18199     },
18200     // @overide
18201     cleanLeadingSpace : function(e)
18202     {
18203        return;
18204     }
18205     
18206 });/*
18207  * Based on:
18208  * Ext JS Library 1.1.1
18209  * Copyright(c) 2006-2007, Ext JS, LLC.
18210  *
18211  * Originally Released Under LGPL - original licence link has changed is not relivant.
18212  *
18213  * Fork - LGPL
18214  * <script type="text/javascript">
18215  */
18216  
18217 /**
18218  * @class Roo.form.MonthField
18219  * @extends Roo.form.TriggerField
18220  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18221 * @constructor
18222 * Create a new MonthField
18223 * @param {Object} config
18224  */
18225 Roo.form.MonthField = function(config){
18226     
18227     Roo.form.MonthField.superclass.constructor.call(this, config);
18228     
18229       this.addEvents({
18230          
18231         /**
18232          * @event select
18233          * Fires when a date is selected
18234              * @param {Roo.form.MonthFieeld} combo This combo box
18235              * @param {Date} date The date selected
18236              */
18237         'select' : true
18238          
18239     });
18240     
18241     
18242     if(typeof this.minValue == "string") {
18243         this.minValue = this.parseDate(this.minValue);
18244     }
18245     if(typeof this.maxValue == "string") {
18246         this.maxValue = this.parseDate(this.maxValue);
18247     }
18248     this.ddMatch = null;
18249     if(this.disabledDates){
18250         var dd = this.disabledDates;
18251         var re = "(?:";
18252         for(var i = 0; i < dd.length; i++){
18253             re += dd[i];
18254             if(i != dd.length-1) {
18255                 re += "|";
18256             }
18257         }
18258         this.ddMatch = new RegExp(re + ")");
18259     }
18260 };
18261
18262 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18263     /**
18264      * @cfg {String} format
18265      * The default date format string which can be overriden for localization support.  The format must be
18266      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18267      */
18268     format : "M Y",
18269     /**
18270      * @cfg {String} altFormats
18271      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18272      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18273      */
18274     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18275     /**
18276      * @cfg {Array} disabledDays
18277      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18278      */
18279     disabledDays : [0,1,2,3,4,5,6],
18280     /**
18281      * @cfg {String} disabledDaysText
18282      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18283      */
18284     disabledDaysText : "Disabled",
18285     /**
18286      * @cfg {Array} disabledDates
18287      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18288      * expression so they are very powerful. Some examples:
18289      * <ul>
18290      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18291      * <li>["03/08", "09/16"] would disable those days for every year</li>
18292      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18293      * <li>["03/../2006"] would disable every day in March 2006</li>
18294      * <li>["^03"] would disable every day in every March</li>
18295      * </ul>
18296      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18297      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18298      */
18299     disabledDates : null,
18300     /**
18301      * @cfg {String} disabledDatesText
18302      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18303      */
18304     disabledDatesText : "Disabled",
18305     /**
18306      * @cfg {Date/String} minValue
18307      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18308      * valid format (defaults to null).
18309      */
18310     minValue : null,
18311     /**
18312      * @cfg {Date/String} maxValue
18313      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18314      * valid format (defaults to null).
18315      */
18316     maxValue : null,
18317     /**
18318      * @cfg {String} minText
18319      * The error text to display when the date in the cell is before minValue (defaults to
18320      * 'The date in this field must be after {minValue}').
18321      */
18322     minText : "The date in this field must be equal to or after {0}",
18323     /**
18324      * @cfg {String} maxTextf
18325      * The error text to display when the date in the cell is after maxValue (defaults to
18326      * 'The date in this field must be before {maxValue}').
18327      */
18328     maxText : "The date in this field must be equal to or before {0}",
18329     /**
18330      * @cfg {String} invalidText
18331      * The error text to display when the date in the field is invalid (defaults to
18332      * '{value} is not a valid date - it must be in the format {format}').
18333      */
18334     invalidText : "{0} is not a valid date - it must be in the format {1}",
18335     /**
18336      * @cfg {String} triggerClass
18337      * An additional CSS class used to style the trigger button.  The trigger will always get the
18338      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18339      * which displays a calendar icon).
18340      */
18341     triggerClass : 'x-form-date-trigger',
18342     
18343
18344     /**
18345      * @cfg {Boolean} useIso
18346      * if enabled, then the date field will use a hidden field to store the 
18347      * real value as iso formated date. default (true)
18348      */ 
18349     useIso : true,
18350     /**
18351      * @cfg {String/Object} autoCreate
18352      * A DomHelper element spec, or true for a default element spec (defaults to
18353      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18354      */ 
18355     // private
18356     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18357     
18358     // private
18359     hiddenField: false,
18360     
18361     hideMonthPicker : false,
18362     
18363     onRender : function(ct, position)
18364     {
18365         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18366         if (this.useIso) {
18367             this.el.dom.removeAttribute('name'); 
18368             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18369                     'before', true);
18370             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18371             // prevent input submission
18372             this.hiddenName = this.name;
18373         }
18374             
18375             
18376     },
18377     
18378     // private
18379     validateValue : function(value)
18380     {
18381         value = this.formatDate(value);
18382         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18383             return false;
18384         }
18385         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18386              return true;
18387         }
18388         var svalue = value;
18389         value = this.parseDate(value);
18390         if(!value){
18391             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18392             return false;
18393         }
18394         var time = value.getTime();
18395         if(this.minValue && time < this.minValue.getTime()){
18396             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18397             return false;
18398         }
18399         if(this.maxValue && time > this.maxValue.getTime()){
18400             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18401             return false;
18402         }
18403         /*if(this.disabledDays){
18404             var day = value.getDay();
18405             for(var i = 0; i < this.disabledDays.length; i++) {
18406                 if(day === this.disabledDays[i]){
18407                     this.markInvalid(this.disabledDaysText);
18408                     return false;
18409                 }
18410             }
18411         }
18412         */
18413         var fvalue = this.formatDate(value);
18414         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18415             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18416             return false;
18417         }
18418         */
18419         return true;
18420     },
18421
18422     // private
18423     // Provides logic to override the default TriggerField.validateBlur which just returns true
18424     validateBlur : function(){
18425         return !this.menu || !this.menu.isVisible();
18426     },
18427
18428     /**
18429      * Returns the current date value of the date field.
18430      * @return {Date} The date value
18431      */
18432     getValue : function(){
18433         
18434         
18435         
18436         return  this.hiddenField ?
18437                 this.hiddenField.value :
18438                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18439     },
18440
18441     /**
18442      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18443      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18444      * (the default format used is "m/d/y").
18445      * <br />Usage:
18446      * <pre><code>
18447 //All of these calls set the same date value (May 4, 2006)
18448
18449 //Pass a date object:
18450 var dt = new Date('5/4/06');
18451 monthField.setValue(dt);
18452
18453 //Pass a date string (default format):
18454 monthField.setValue('5/4/06');
18455
18456 //Pass a date string (custom format):
18457 monthField.format = 'Y-m-d';
18458 monthField.setValue('2006-5-4');
18459 </code></pre>
18460      * @param {String/Date} date The date or valid date string
18461      */
18462     setValue : function(date){
18463         Roo.log('month setValue' + date);
18464         // can only be first of month..
18465         
18466         var val = this.parseDate(date);
18467         
18468         if (this.hiddenField) {
18469             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18470         }
18471         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18472         this.value = this.parseDate(date);
18473     },
18474
18475     // private
18476     parseDate : function(value){
18477         if(!value || value instanceof Date){
18478             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18479             return value;
18480         }
18481         var v = Date.parseDate(value, this.format);
18482         if (!v && this.useIso) {
18483             v = Date.parseDate(value, 'Y-m-d');
18484         }
18485         if (v) {
18486             // 
18487             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18488         }
18489         
18490         
18491         if(!v && this.altFormats){
18492             if(!this.altFormatsArray){
18493                 this.altFormatsArray = this.altFormats.split("|");
18494             }
18495             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18496                 v = Date.parseDate(value, this.altFormatsArray[i]);
18497             }
18498         }
18499         return v;
18500     },
18501
18502     // private
18503     formatDate : function(date, fmt){
18504         return (!date || !(date instanceof Date)) ?
18505                date : date.dateFormat(fmt || this.format);
18506     },
18507
18508     // private
18509     menuListeners : {
18510         select: function(m, d){
18511             this.setValue(d);
18512             this.fireEvent('select', this, d);
18513         },
18514         show : function(){ // retain focus styling
18515             this.onFocus();
18516         },
18517         hide : function(){
18518             this.focus.defer(10, this);
18519             var ml = this.menuListeners;
18520             this.menu.un("select", ml.select,  this);
18521             this.menu.un("show", ml.show,  this);
18522             this.menu.un("hide", ml.hide,  this);
18523         }
18524     },
18525     // private
18526     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18527     onTriggerClick : function(){
18528         if(this.disabled){
18529             return;
18530         }
18531         if(this.menu == null){
18532             this.menu = new Roo.menu.DateMenu();
18533            
18534         }
18535         
18536         Roo.apply(this.menu.picker,  {
18537             
18538             showClear: this.allowBlank,
18539             minDate : this.minValue,
18540             maxDate : this.maxValue,
18541             disabledDatesRE : this.ddMatch,
18542             disabledDatesText : this.disabledDatesText,
18543             
18544             format : this.useIso ? 'Y-m-d' : this.format,
18545             minText : String.format(this.minText, this.formatDate(this.minValue)),
18546             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18547             
18548         });
18549          this.menu.on(Roo.apply({}, this.menuListeners, {
18550             scope:this
18551         }));
18552        
18553         
18554         var m = this.menu;
18555         var p = m.picker;
18556         
18557         // hide month picker get's called when we called by 'before hide';
18558         
18559         var ignorehide = true;
18560         p.hideMonthPicker  = function(disableAnim){
18561             if (ignorehide) {
18562                 return;
18563             }
18564              if(this.monthPicker){
18565                 Roo.log("hideMonthPicker called");
18566                 if(disableAnim === true){
18567                     this.monthPicker.hide();
18568                 }else{
18569                     this.monthPicker.slideOut('t', {duration:.2});
18570                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18571                     p.fireEvent("select", this, this.value);
18572                     m.hide();
18573                 }
18574             }
18575         }
18576         
18577         Roo.log('picker set value');
18578         Roo.log(this.getValue());
18579         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18580         m.show(this.el, 'tl-bl?');
18581         ignorehide  = false;
18582         // this will trigger hideMonthPicker..
18583         
18584         
18585         // hidden the day picker
18586         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18587         
18588         
18589         
18590       
18591         
18592         p.showMonthPicker.defer(100, p);
18593     
18594         
18595        
18596     },
18597
18598     beforeBlur : function(){
18599         var v = this.parseDate(this.getRawValue());
18600         if(v){
18601             this.setValue(v);
18602         }
18603     }
18604
18605     /** @cfg {Boolean} grow @hide */
18606     /** @cfg {Number} growMin @hide */
18607     /** @cfg {Number} growMax @hide */
18608     /**
18609      * @hide
18610      * @method autoSize
18611      */
18612 });/*
18613  * Based on:
18614  * Ext JS Library 1.1.1
18615  * Copyright(c) 2006-2007, Ext JS, LLC.
18616  *
18617  * Originally Released Under LGPL - original licence link has changed is not relivant.
18618  *
18619  * Fork - LGPL
18620  * <script type="text/javascript">
18621  */
18622  
18623
18624 /**
18625  * @class Roo.form.ComboBox
18626  * @extends Roo.form.TriggerField
18627  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18628  * @constructor
18629  * Create a new ComboBox.
18630  * @param {Object} config Configuration options
18631  */
18632 Roo.form.ComboBox = function(config){
18633     Roo.form.ComboBox.superclass.constructor.call(this, config);
18634     this.addEvents({
18635         /**
18636          * @event expand
18637          * Fires when the dropdown list is expanded
18638              * @param {Roo.form.ComboBox} combo This combo box
18639              */
18640         'expand' : true,
18641         /**
18642          * @event collapse
18643          * Fires when the dropdown list is collapsed
18644              * @param {Roo.form.ComboBox} combo This combo box
18645              */
18646         'collapse' : true,
18647         /**
18648          * @event beforeselect
18649          * Fires before a list item is selected. Return false to cancel the selection.
18650              * @param {Roo.form.ComboBox} combo This combo box
18651              * @param {Roo.data.Record} record The data record returned from the underlying store
18652              * @param {Number} index The index of the selected item in the dropdown list
18653              */
18654         'beforeselect' : true,
18655         /**
18656          * @event select
18657          * Fires when a list item is selected
18658              * @param {Roo.form.ComboBox} combo This combo box
18659              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18660              * @param {Number} index The index of the selected item in the dropdown list
18661              */
18662         'select' : true,
18663         /**
18664          * @event beforequery
18665          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18666          * The event object passed has these properties:
18667              * @param {Roo.form.ComboBox} combo This combo box
18668              * @param {String} query The query
18669              * @param {Boolean} forceAll true to force "all" query
18670              * @param {Boolean} cancel true to cancel the query
18671              * @param {Object} e The query event object
18672              */
18673         'beforequery': true,
18674          /**
18675          * @event add
18676          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18677              * @param {Roo.form.ComboBox} combo This combo box
18678              */
18679         'add' : true,
18680         /**
18681          * @event edit
18682          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18683              * @param {Roo.form.ComboBox} combo This combo box
18684              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18685              */
18686         'edit' : true
18687         
18688         
18689     });
18690     if(this.transform){
18691         this.allowDomMove = false;
18692         var s = Roo.getDom(this.transform);
18693         if(!this.hiddenName){
18694             this.hiddenName = s.name;
18695         }
18696         if(!this.store){
18697             this.mode = 'local';
18698             var d = [], opts = s.options;
18699             for(var i = 0, len = opts.length;i < len; i++){
18700                 var o = opts[i];
18701                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18702                 if(o.selected) {
18703                     this.value = value;
18704                 }
18705                 d.push([value, o.text]);
18706             }
18707             this.store = new Roo.data.SimpleStore({
18708                 'id': 0,
18709                 fields: ['value', 'text'],
18710                 data : d
18711             });
18712             this.valueField = 'value';
18713             this.displayField = 'text';
18714         }
18715         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18716         if(!this.lazyRender){
18717             this.target = true;
18718             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18719             s.parentNode.removeChild(s); // remove it
18720             this.render(this.el.parentNode);
18721         }else{
18722             s.parentNode.removeChild(s); // remove it
18723         }
18724
18725     }
18726     if (this.store) {
18727         this.store = Roo.factory(this.store, Roo.data);
18728     }
18729     
18730     this.selectedIndex = -1;
18731     if(this.mode == 'local'){
18732         if(config.queryDelay === undefined){
18733             this.queryDelay = 10;
18734         }
18735         if(config.minChars === undefined){
18736             this.minChars = 0;
18737         }
18738     }
18739 };
18740
18741 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18742     /**
18743      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18744      */
18745     /**
18746      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18747      * rendering into an Roo.Editor, defaults to false)
18748      */
18749     /**
18750      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18751      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18752      */
18753     /**
18754      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18755      */
18756     /**
18757      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18758      * the dropdown list (defaults to undefined, with no header element)
18759      */
18760
18761      /**
18762      * @cfg {String/Roo.Template} tpl The template to use to render the output
18763      */
18764      
18765     // private
18766     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18767     /**
18768      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18769      */
18770     listWidth: undefined,
18771     /**
18772      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18773      * mode = 'remote' or 'text' if mode = 'local')
18774      */
18775     displayField: undefined,
18776     /**
18777      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18778      * mode = 'remote' or 'value' if mode = 'local'). 
18779      * Note: use of a valueField requires the user make a selection
18780      * in order for a value to be mapped.
18781      */
18782     valueField: undefined,
18783     
18784     
18785     /**
18786      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18787      * field's data value (defaults to the underlying DOM element's name)
18788      */
18789     hiddenName: undefined,
18790     /**
18791      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18792      */
18793     listClass: '',
18794     /**
18795      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18796      */
18797     selectedClass: 'x-combo-selected',
18798     /**
18799      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18800      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18801      * which displays a downward arrow icon).
18802      */
18803     triggerClass : 'x-form-arrow-trigger',
18804     /**
18805      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18806      */
18807     shadow:'sides',
18808     /**
18809      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18810      * anchor positions (defaults to 'tl-bl')
18811      */
18812     listAlign: 'tl-bl?',
18813     /**
18814      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18815      */
18816     maxHeight: 300,
18817     /**
18818      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18819      * query specified by the allQuery config option (defaults to 'query')
18820      */
18821     triggerAction: 'query',
18822     /**
18823      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18824      * (defaults to 4, does not apply if editable = false)
18825      */
18826     minChars : 4,
18827     /**
18828      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18829      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18830      */
18831     typeAhead: false,
18832     /**
18833      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18834      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18835      */
18836     queryDelay: 500,
18837     /**
18838      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18839      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18840      */
18841     pageSize: 0,
18842     /**
18843      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18844      * when editable = true (defaults to false)
18845      */
18846     selectOnFocus:false,
18847     /**
18848      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18849      */
18850     queryParam: 'query',
18851     /**
18852      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18853      * when mode = 'remote' (defaults to 'Loading...')
18854      */
18855     loadingText: 'Loading...',
18856     /**
18857      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18858      */
18859     resizable: false,
18860     /**
18861      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18862      */
18863     handleHeight : 8,
18864     /**
18865      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18866      * traditional select (defaults to true)
18867      */
18868     editable: true,
18869     /**
18870      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18871      */
18872     allQuery: '',
18873     /**
18874      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18875      */
18876     mode: 'remote',
18877     /**
18878      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18879      * listWidth has a higher value)
18880      */
18881     minListWidth : 70,
18882     /**
18883      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18884      * allow the user to set arbitrary text into the field (defaults to false)
18885      */
18886     forceSelection:false,
18887     /**
18888      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18889      * if typeAhead = true (defaults to 250)
18890      */
18891     typeAheadDelay : 250,
18892     /**
18893      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18894      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18895      */
18896     valueNotFoundText : undefined,
18897     /**
18898      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18899      */
18900     blockFocus : false,
18901     
18902     /**
18903      * @cfg {Boolean} disableClear Disable showing of clear button.
18904      */
18905     disableClear : false,
18906     /**
18907      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18908      */
18909     alwaysQuery : false,
18910     
18911     //private
18912     addicon : false,
18913     editicon: false,
18914     
18915     // element that contains real text value.. (when hidden is used..)
18916      
18917     // private
18918     onRender : function(ct, position)
18919     {
18920         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18921         
18922         if(this.hiddenName){
18923             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18924                     'before', true);
18925             this.hiddenField.value =
18926                 this.hiddenValue !== undefined ? this.hiddenValue :
18927                 this.value !== undefined ? this.value : '';
18928
18929             // prevent input submission
18930             this.el.dom.removeAttribute('name');
18931              
18932              
18933         }
18934         
18935         if(Roo.isGecko){
18936             this.el.dom.setAttribute('autocomplete', 'off');
18937         }
18938
18939         var cls = 'x-combo-list';
18940
18941         this.list = new Roo.Layer({
18942             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18943         });
18944
18945         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18946         this.list.setWidth(lw);
18947         this.list.swallowEvent('mousewheel');
18948         this.assetHeight = 0;
18949
18950         if(this.title){
18951             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18952             this.assetHeight += this.header.getHeight();
18953         }
18954
18955         this.innerList = this.list.createChild({cls:cls+'-inner'});
18956         this.innerList.on('mouseover', this.onViewOver, this);
18957         this.innerList.on('mousemove', this.onViewMove, this);
18958         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18959         
18960         if(this.allowBlank && !this.pageSize && !this.disableClear){
18961             this.footer = this.list.createChild({cls:cls+'-ft'});
18962             this.pageTb = new Roo.Toolbar(this.footer);
18963            
18964         }
18965         if(this.pageSize){
18966             this.footer = this.list.createChild({cls:cls+'-ft'});
18967             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18968                     {pageSize: this.pageSize});
18969             
18970         }
18971         
18972         if (this.pageTb && this.allowBlank && !this.disableClear) {
18973             var _this = this;
18974             this.pageTb.add(new Roo.Toolbar.Fill(), {
18975                 cls: 'x-btn-icon x-btn-clear',
18976                 text: '&#160;',
18977                 handler: function()
18978                 {
18979                     _this.collapse();
18980                     _this.clearValue();
18981                     _this.onSelect(false, -1);
18982                 }
18983             });
18984         }
18985         if (this.footer) {
18986             this.assetHeight += this.footer.getHeight();
18987         }
18988         
18989
18990         if(!this.tpl){
18991             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18992         }
18993
18994         this.view = new Roo.View(this.innerList, this.tpl, {
18995             singleSelect:true,
18996             store: this.store,
18997             selectedClass: this.selectedClass
18998         });
18999
19000         this.view.on('click', this.onViewClick, this);
19001
19002         this.store.on('beforeload', this.onBeforeLoad, this);
19003         this.store.on('load', this.onLoad, this);
19004         this.store.on('loadexception', this.onLoadException, this);
19005
19006         if(this.resizable){
19007             this.resizer = new Roo.Resizable(this.list,  {
19008                pinned:true, handles:'se'
19009             });
19010             this.resizer.on('resize', function(r, w, h){
19011                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
19012                 this.listWidth = w;
19013                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
19014                 this.restrictHeight();
19015             }, this);
19016             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
19017         }
19018         if(!this.editable){
19019             this.editable = true;
19020             this.setEditable(false);
19021         }  
19022         
19023         
19024         if (typeof(this.events.add.listeners) != 'undefined') {
19025             
19026             this.addicon = this.wrap.createChild(
19027                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
19028        
19029             this.addicon.on('click', function(e) {
19030                 this.fireEvent('add', this);
19031             }, this);
19032         }
19033         if (typeof(this.events.edit.listeners) != 'undefined') {
19034             
19035             this.editicon = this.wrap.createChild(
19036                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
19037             if (this.addicon) {
19038                 this.editicon.setStyle('margin-left', '40px');
19039             }
19040             this.editicon.on('click', function(e) {
19041                 
19042                 // we fire even  if inothing is selected..
19043                 this.fireEvent('edit', this, this.lastData );
19044                 
19045             }, this);
19046         }
19047         
19048         
19049         
19050     },
19051
19052     // private
19053     initEvents : function(){
19054         Roo.form.ComboBox.superclass.initEvents.call(this);
19055
19056         this.keyNav = new Roo.KeyNav(this.el, {
19057             "up" : function(e){
19058                 this.inKeyMode = true;
19059                 this.selectPrev();
19060             },
19061
19062             "down" : function(e){
19063                 if(!this.isExpanded()){
19064                     this.onTriggerClick();
19065                 }else{
19066                     this.inKeyMode = true;
19067                     this.selectNext();
19068                 }
19069             },
19070
19071             "enter" : function(e){
19072                 this.onViewClick();
19073                 //return true;
19074             },
19075
19076             "esc" : function(e){
19077                 this.collapse();
19078             },
19079
19080             "tab" : function(e){
19081                 this.onViewClick(false);
19082                 this.fireEvent("specialkey", this, e);
19083                 return true;
19084             },
19085
19086             scope : this,
19087
19088             doRelay : function(foo, bar, hname){
19089                 if(hname == 'down' || this.scope.isExpanded()){
19090                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
19091                 }
19092                 return true;
19093             },
19094
19095             forceKeyDown: true
19096         });
19097         this.queryDelay = Math.max(this.queryDelay || 10,
19098                 this.mode == 'local' ? 10 : 250);
19099         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
19100         if(this.typeAhead){
19101             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
19102         }
19103         if(this.editable !== false){
19104             this.el.on("keyup", this.onKeyUp, this);
19105         }
19106         if(this.forceSelection){
19107             this.on('blur', this.doForce, this);
19108         }
19109     },
19110
19111     onDestroy : function(){
19112         if(this.view){
19113             this.view.setStore(null);
19114             this.view.el.removeAllListeners();
19115             this.view.el.remove();
19116             this.view.purgeListeners();
19117         }
19118         if(this.list){
19119             this.list.destroy();
19120         }
19121         if(this.store){
19122             this.store.un('beforeload', this.onBeforeLoad, this);
19123             this.store.un('load', this.onLoad, this);
19124             this.store.un('loadexception', this.onLoadException, this);
19125         }
19126         Roo.form.ComboBox.superclass.onDestroy.call(this);
19127     },
19128
19129     // private
19130     fireKey : function(e){
19131         if(e.isNavKeyPress() && !this.list.isVisible()){
19132             this.fireEvent("specialkey", this, e);
19133         }
19134     },
19135
19136     // private
19137     onResize: function(w, h){
19138         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19139         
19140         if(typeof w != 'number'){
19141             // we do not handle it!?!?
19142             return;
19143         }
19144         var tw = this.trigger.getWidth();
19145         tw += this.addicon ? this.addicon.getWidth() : 0;
19146         tw += this.editicon ? this.editicon.getWidth() : 0;
19147         var x = w - tw;
19148         this.el.setWidth( this.adjustWidth('input', x));
19149             
19150         this.trigger.setStyle('left', x+'px');
19151         
19152         if(this.list && this.listWidth === undefined){
19153             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19154             this.list.setWidth(lw);
19155             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19156         }
19157         
19158     
19159         
19160     },
19161
19162     /**
19163      * Allow or prevent the user from directly editing the field text.  If false is passed,
19164      * the user will only be able to select from the items defined in the dropdown list.  This method
19165      * is the runtime equivalent of setting the 'editable' config option at config time.
19166      * @param {Boolean} value True to allow the user to directly edit the field text
19167      */
19168     setEditable : function(value){
19169         if(value == this.editable){
19170             return;
19171         }
19172         this.editable = value;
19173         if(!value){
19174             this.el.dom.setAttribute('readOnly', true);
19175             this.el.on('mousedown', this.onTriggerClick,  this);
19176             this.el.addClass('x-combo-noedit');
19177         }else{
19178             this.el.dom.setAttribute('readOnly', false);
19179             this.el.un('mousedown', this.onTriggerClick,  this);
19180             this.el.removeClass('x-combo-noedit');
19181         }
19182     },
19183
19184     // private
19185     onBeforeLoad : function(){
19186         if(!this.hasFocus){
19187             return;
19188         }
19189         this.innerList.update(this.loadingText ?
19190                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19191         this.restrictHeight();
19192         this.selectedIndex = -1;
19193     },
19194
19195     // private
19196     onLoad : function(){
19197         if(!this.hasFocus){
19198             return;
19199         }
19200         if(this.store.getCount() > 0){
19201             this.expand();
19202             this.restrictHeight();
19203             if(this.lastQuery == this.allQuery){
19204                 if(this.editable){
19205                     this.el.dom.select();
19206                 }
19207                 if(!this.selectByValue(this.value, true)){
19208                     this.select(0, true);
19209                 }
19210             }else{
19211                 this.selectNext();
19212                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19213                     this.taTask.delay(this.typeAheadDelay);
19214                 }
19215             }
19216         }else{
19217             this.onEmptyResults();
19218         }
19219         //this.el.focus();
19220     },
19221     // private
19222     onLoadException : function()
19223     {
19224         this.collapse();
19225         Roo.log(this.store.reader.jsonData);
19226         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19227             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19228         }
19229         
19230         
19231     },
19232     // private
19233     onTypeAhead : function(){
19234         if(this.store.getCount() > 0){
19235             var r = this.store.getAt(0);
19236             var newValue = r.data[this.displayField];
19237             var len = newValue.length;
19238             var selStart = this.getRawValue().length;
19239             if(selStart != len){
19240                 this.setRawValue(newValue);
19241                 this.selectText(selStart, newValue.length);
19242             }
19243         }
19244     },
19245
19246     // private
19247     onSelect : function(record, index){
19248         if(this.fireEvent('beforeselect', this, record, index) !== false){
19249             this.setFromData(index > -1 ? record.data : false);
19250             this.collapse();
19251             this.fireEvent('select', this, record, index);
19252         }
19253     },
19254
19255     /**
19256      * Returns the currently selected field value or empty string if no value is set.
19257      * @return {String} value The selected value
19258      */
19259     getValue : function(){
19260         if(this.valueField){
19261             return typeof this.value != 'undefined' ? this.value : '';
19262         }
19263         return Roo.form.ComboBox.superclass.getValue.call(this);
19264     },
19265
19266     /**
19267      * Clears any text/value currently set in the field
19268      */
19269     clearValue : function(){
19270         if(this.hiddenField){
19271             this.hiddenField.value = '';
19272         }
19273         this.value = '';
19274         this.setRawValue('');
19275         this.lastSelectionText = '';
19276         
19277     },
19278
19279     /**
19280      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19281      * will be displayed in the field.  If the value does not match the data value of an existing item,
19282      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19283      * Otherwise the field will be blank (although the value will still be set).
19284      * @param {String} value The value to match
19285      */
19286     setValue : function(v){
19287         var text = v;
19288         if(this.valueField){
19289             var r = this.findRecord(this.valueField, v);
19290             if(r){
19291                 text = r.data[this.displayField];
19292             }else if(this.valueNotFoundText !== undefined){
19293                 text = this.valueNotFoundText;
19294             }
19295         }
19296         this.lastSelectionText = text;
19297         if(this.hiddenField){
19298             this.hiddenField.value = v;
19299         }
19300         Roo.form.ComboBox.superclass.setValue.call(this, text);
19301         this.value = v;
19302     },
19303     /**
19304      * @property {Object} the last set data for the element
19305      */
19306     
19307     lastData : false,
19308     /**
19309      * Sets the value of the field based on a object which is related to the record format for the store.
19310      * @param {Object} value the value to set as. or false on reset?
19311      */
19312     setFromData : function(o){
19313         var dv = ''; // display value
19314         var vv = ''; // value value..
19315         this.lastData = o;
19316         if (this.displayField) {
19317             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19318         } else {
19319             // this is an error condition!!!
19320             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19321         }
19322         
19323         if(this.valueField){
19324             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19325         }
19326         if(this.hiddenField){
19327             this.hiddenField.value = vv;
19328             
19329             this.lastSelectionText = dv;
19330             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19331             this.value = vv;
19332             return;
19333         }
19334         // no hidden field.. - we store the value in 'value', but still display
19335         // display field!!!!
19336         this.lastSelectionText = dv;
19337         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19338         this.value = vv;
19339         
19340         
19341     },
19342     // private
19343     reset : function(){
19344         // overridden so that last data is reset..
19345         this.setValue(this.resetValue);
19346         this.originalValue = this.getValue();
19347         this.clearInvalid();
19348         this.lastData = false;
19349         if (this.view) {
19350             this.view.clearSelections();
19351         }
19352     },
19353     // private
19354     findRecord : function(prop, value){
19355         var record;
19356         if(this.store.getCount() > 0){
19357             this.store.each(function(r){
19358                 if(r.data[prop] == value){
19359                     record = r;
19360                     return false;
19361                 }
19362                 return true;
19363             });
19364         }
19365         return record;
19366     },
19367     
19368     getName: function()
19369     {
19370         // returns hidden if it's set..
19371         if (!this.rendered) {return ''};
19372         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19373         
19374     },
19375     // private
19376     onViewMove : function(e, t){
19377         this.inKeyMode = false;
19378     },
19379
19380     // private
19381     onViewOver : function(e, t){
19382         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19383             return;
19384         }
19385         var item = this.view.findItemFromChild(t);
19386         if(item){
19387             var index = this.view.indexOf(item);
19388             this.select(index, false);
19389         }
19390     },
19391
19392     // private
19393     onViewClick : function(doFocus)
19394     {
19395         var index = this.view.getSelectedIndexes()[0];
19396         var r = this.store.getAt(index);
19397         if(r){
19398             this.onSelect(r, index);
19399         }
19400         if(doFocus !== false && !this.blockFocus){
19401             this.el.focus();
19402         }
19403     },
19404
19405     // private
19406     restrictHeight : function(){
19407         this.innerList.dom.style.height = '';
19408         var inner = this.innerList.dom;
19409         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19410         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19411         this.list.beginUpdate();
19412         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19413         this.list.alignTo(this.el, this.listAlign);
19414         this.list.endUpdate();
19415     },
19416
19417     // private
19418     onEmptyResults : function(){
19419         this.collapse();
19420     },
19421
19422     /**
19423      * Returns true if the dropdown list is expanded, else false.
19424      */
19425     isExpanded : function(){
19426         return this.list.isVisible();
19427     },
19428
19429     /**
19430      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19431      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19432      * @param {String} value The data value of the item to select
19433      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19434      * selected item if it is not currently in view (defaults to true)
19435      * @return {Boolean} True if the value matched an item in the list, else false
19436      */
19437     selectByValue : function(v, scrollIntoView){
19438         if(v !== undefined && v !== null){
19439             var r = this.findRecord(this.valueField || this.displayField, v);
19440             if(r){
19441                 this.select(this.store.indexOf(r), scrollIntoView);
19442                 return true;
19443             }
19444         }
19445         return false;
19446     },
19447
19448     /**
19449      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19450      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19451      * @param {Number} index The zero-based index of the list item to select
19452      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19453      * selected item if it is not currently in view (defaults to true)
19454      */
19455     select : function(index, scrollIntoView){
19456         this.selectedIndex = index;
19457         this.view.select(index);
19458         if(scrollIntoView !== false){
19459             var el = this.view.getNode(index);
19460             if(el){
19461                 this.innerList.scrollChildIntoView(el, false);
19462             }
19463         }
19464     },
19465
19466     // private
19467     selectNext : function(){
19468         var ct = this.store.getCount();
19469         if(ct > 0){
19470             if(this.selectedIndex == -1){
19471                 this.select(0);
19472             }else if(this.selectedIndex < ct-1){
19473                 this.select(this.selectedIndex+1);
19474             }
19475         }
19476     },
19477
19478     // private
19479     selectPrev : function(){
19480         var ct = this.store.getCount();
19481         if(ct > 0){
19482             if(this.selectedIndex == -1){
19483                 this.select(0);
19484             }else if(this.selectedIndex != 0){
19485                 this.select(this.selectedIndex-1);
19486             }
19487         }
19488     },
19489
19490     // private
19491     onKeyUp : function(e){
19492         if(this.editable !== false && !e.isSpecialKey()){
19493             this.lastKey = e.getKey();
19494             this.dqTask.delay(this.queryDelay);
19495         }
19496     },
19497
19498     // private
19499     validateBlur : function(){
19500         return !this.list || !this.list.isVisible();   
19501     },
19502
19503     // private
19504     initQuery : function(){
19505         this.doQuery(this.getRawValue());
19506     },
19507
19508     // private
19509     doForce : function(){
19510         if(this.el.dom.value.length > 0){
19511             this.el.dom.value =
19512                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19513              
19514         }
19515     },
19516
19517     /**
19518      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19519      * query allowing the query action to be canceled if needed.
19520      * @param {String} query The SQL query to execute
19521      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19522      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19523      * saved in the current store (defaults to false)
19524      */
19525     doQuery : function(q, forceAll){
19526         if(q === undefined || q === null){
19527             q = '';
19528         }
19529         var qe = {
19530             query: q,
19531             forceAll: forceAll,
19532             combo: this,
19533             cancel:false
19534         };
19535         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19536             return false;
19537         }
19538         q = qe.query;
19539         forceAll = qe.forceAll;
19540         if(forceAll === true || (q.length >= this.minChars)){
19541             if(this.lastQuery != q || this.alwaysQuery){
19542                 this.lastQuery = q;
19543                 if(this.mode == 'local'){
19544                     this.selectedIndex = -1;
19545                     if(forceAll){
19546                         this.store.clearFilter();
19547                     }else{
19548                         this.store.filter(this.displayField, q);
19549                     }
19550                     this.onLoad();
19551                 }else{
19552                     this.store.baseParams[this.queryParam] = q;
19553                     this.store.load({
19554                         params: this.getParams(q)
19555                     });
19556                     this.expand();
19557                 }
19558             }else{
19559                 this.selectedIndex = -1;
19560                 this.onLoad();   
19561             }
19562         }
19563     },
19564
19565     // private
19566     getParams : function(q){
19567         var p = {};
19568         //p[this.queryParam] = q;
19569         if(this.pageSize){
19570             p.start = 0;
19571             p.limit = this.pageSize;
19572         }
19573         return p;
19574     },
19575
19576     /**
19577      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19578      */
19579     collapse : function(){
19580         if(!this.isExpanded()){
19581             return;
19582         }
19583         this.list.hide();
19584         Roo.get(document).un('mousedown', this.collapseIf, this);
19585         Roo.get(document).un('mousewheel', this.collapseIf, this);
19586         if (!this.editable) {
19587             Roo.get(document).un('keydown', this.listKeyPress, this);
19588         }
19589         this.fireEvent('collapse', this);
19590     },
19591
19592     // private
19593     collapseIf : function(e){
19594         if(!e.within(this.wrap) && !e.within(this.list)){
19595             this.collapse();
19596         }
19597     },
19598
19599     /**
19600      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19601      */
19602     expand : function(){
19603         if(this.isExpanded() || !this.hasFocus){
19604             return;
19605         }
19606         this.list.alignTo(this.el, this.listAlign);
19607         this.list.show();
19608         Roo.get(document).on('mousedown', this.collapseIf, this);
19609         Roo.get(document).on('mousewheel', this.collapseIf, this);
19610         if (!this.editable) {
19611             Roo.get(document).on('keydown', this.listKeyPress, this);
19612         }
19613         
19614         this.fireEvent('expand', this);
19615     },
19616
19617     // private
19618     // Implements the default empty TriggerField.onTriggerClick function
19619     onTriggerClick : function(){
19620         if(this.disabled){
19621             return;
19622         }
19623         if(this.isExpanded()){
19624             this.collapse();
19625             if (!this.blockFocus) {
19626                 this.el.focus();
19627             }
19628             
19629         }else {
19630             this.hasFocus = true;
19631             if(this.triggerAction == 'all') {
19632                 this.doQuery(this.allQuery, true);
19633             } else {
19634                 this.doQuery(this.getRawValue());
19635             }
19636             if (!this.blockFocus) {
19637                 this.el.focus();
19638             }
19639         }
19640     },
19641     listKeyPress : function(e)
19642     {
19643         //Roo.log('listkeypress');
19644         // scroll to first matching element based on key pres..
19645         if (e.isSpecialKey()) {
19646             return false;
19647         }
19648         var k = String.fromCharCode(e.getKey()).toUpperCase();
19649         //Roo.log(k);
19650         var match  = false;
19651         var csel = this.view.getSelectedNodes();
19652         var cselitem = false;
19653         if (csel.length) {
19654             var ix = this.view.indexOf(csel[0]);
19655             cselitem  = this.store.getAt(ix);
19656             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19657                 cselitem = false;
19658             }
19659             
19660         }
19661         
19662         this.store.each(function(v) { 
19663             if (cselitem) {
19664                 // start at existing selection.
19665                 if (cselitem.id == v.id) {
19666                     cselitem = false;
19667                 }
19668                 return;
19669             }
19670                 
19671             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19672                 match = this.store.indexOf(v);
19673                 return false;
19674             }
19675         }, this);
19676         
19677         if (match === false) {
19678             return true; // no more action?
19679         }
19680         // scroll to?
19681         this.view.select(match);
19682         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19683         sn.scrollIntoView(sn.dom.parentNode, false);
19684     } 
19685
19686     /** 
19687     * @cfg {Boolean} grow 
19688     * @hide 
19689     */
19690     /** 
19691     * @cfg {Number} growMin 
19692     * @hide 
19693     */
19694     /** 
19695     * @cfg {Number} growMax 
19696     * @hide 
19697     */
19698     /**
19699      * @hide
19700      * @method autoSize
19701      */
19702 });/*
19703  * Copyright(c) 2010-2012, Roo J Solutions Limited
19704  *
19705  * Licence LGPL
19706  *
19707  */
19708
19709 /**
19710  * @class Roo.form.ComboBoxArray
19711  * @extends Roo.form.TextField
19712  * A facebook style adder... for lists of email / people / countries  etc...
19713  * pick multiple items from a combo box, and shows each one.
19714  *
19715  *  Fred [x]  Brian [x]  [Pick another |v]
19716  *
19717  *
19718  *  For this to work: it needs various extra information
19719  *    - normal combo problay has
19720  *      name, hiddenName
19721  *    + displayField, valueField
19722  *
19723  *    For our purpose...
19724  *
19725  *
19726  *   If we change from 'extends' to wrapping...
19727  *   
19728  *  
19729  *
19730  
19731  
19732  * @constructor
19733  * Create a new ComboBoxArray.
19734  * @param {Object} config Configuration options
19735  */
19736  
19737
19738 Roo.form.ComboBoxArray = function(config)
19739 {
19740     this.addEvents({
19741         /**
19742          * @event beforeremove
19743          * Fires before remove the value from the list
19744              * @param {Roo.form.ComboBoxArray} _self This combo box array
19745              * @param {Roo.form.ComboBoxArray.Item} item removed item
19746              */
19747         'beforeremove' : true,
19748         /**
19749          * @event remove
19750          * Fires when remove the value from the list
19751              * @param {Roo.form.ComboBoxArray} _self This combo box array
19752              * @param {Roo.form.ComboBoxArray.Item} item removed item
19753              */
19754         'remove' : true
19755         
19756         
19757     });
19758     
19759     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19760     
19761     this.items = new Roo.util.MixedCollection(false);
19762     
19763     // construct the child combo...
19764     
19765     
19766     
19767     
19768    
19769     
19770 }
19771
19772  
19773 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19774
19775     /**
19776      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19777      */
19778     
19779     lastData : false,
19780     
19781     // behavies liek a hiddne field
19782     inputType:      'hidden',
19783     /**
19784      * @cfg {Number} width The width of the box that displays the selected element
19785      */ 
19786     width:          300,
19787
19788     
19789     
19790     /**
19791      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19792      */
19793     name : false,
19794     /**
19795      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19796      */
19797     hiddenName : false,
19798     
19799     
19800     // private the array of items that are displayed..
19801     items  : false,
19802     // private - the hidden field el.
19803     hiddenEl : false,
19804     // private - the filed el..
19805     el : false,
19806     
19807     //validateValue : function() { return true; }, // all values are ok!
19808     //onAddClick: function() { },
19809     
19810     onRender : function(ct, position) 
19811     {
19812         
19813         // create the standard hidden element
19814         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19815         
19816         
19817         // give fake names to child combo;
19818         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19819         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19820         
19821         this.combo = Roo.factory(this.combo, Roo.form);
19822         this.combo.onRender(ct, position);
19823         if (typeof(this.combo.width) != 'undefined') {
19824             this.combo.onResize(this.combo.width,0);
19825         }
19826         
19827         this.combo.initEvents();
19828         
19829         // assigned so form know we need to do this..
19830         this.store          = this.combo.store;
19831         this.valueField     = this.combo.valueField;
19832         this.displayField   = this.combo.displayField ;
19833         
19834         
19835         this.combo.wrap.addClass('x-cbarray-grp');
19836         
19837         var cbwrap = this.combo.wrap.createChild(
19838             {tag: 'div', cls: 'x-cbarray-cb'},
19839             this.combo.el.dom
19840         );
19841         
19842              
19843         this.hiddenEl = this.combo.wrap.createChild({
19844             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19845         });
19846         this.el = this.combo.wrap.createChild({
19847             tag: 'input',  type:'hidden' , name: this.name, value : ''
19848         });
19849          //   this.el.dom.removeAttribute("name");
19850         
19851         
19852         this.outerWrap = this.combo.wrap;
19853         this.wrap = cbwrap;
19854         
19855         this.outerWrap.setWidth(this.width);
19856         this.outerWrap.dom.removeChild(this.el.dom);
19857         
19858         this.wrap.dom.appendChild(this.el.dom);
19859         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19860         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19861         
19862         this.combo.trigger.setStyle('position','relative');
19863         this.combo.trigger.setStyle('left', '0px');
19864         this.combo.trigger.setStyle('top', '2px');
19865         
19866         this.combo.el.setStyle('vertical-align', 'text-bottom');
19867         
19868         //this.trigger.setStyle('vertical-align', 'top');
19869         
19870         // this should use the code from combo really... on('add' ....)
19871         if (this.adder) {
19872             
19873         
19874             this.adder = this.outerWrap.createChild(
19875                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19876             var _t = this;
19877             this.adder.on('click', function(e) {
19878                 _t.fireEvent('adderclick', this, e);
19879             }, _t);
19880         }
19881         //var _t = this;
19882         //this.adder.on('click', this.onAddClick, _t);
19883         
19884         
19885         this.combo.on('select', function(cb, rec, ix) {
19886             this.addItem(rec.data);
19887             
19888             cb.setValue('');
19889             cb.el.dom.value = '';
19890             //cb.lastData = rec.data;
19891             // add to list
19892             
19893         }, this);
19894         
19895         
19896     },
19897     
19898     
19899     getName: function()
19900     {
19901         // returns hidden if it's set..
19902         if (!this.rendered) {return ''};
19903         return  this.hiddenName ? this.hiddenName : this.name;
19904         
19905     },
19906     
19907     
19908     onResize: function(w, h){
19909         
19910         return;
19911         // not sure if this is needed..
19912         //this.combo.onResize(w,h);
19913         
19914         if(typeof w != 'number'){
19915             // we do not handle it!?!?
19916             return;
19917         }
19918         var tw = this.combo.trigger.getWidth();
19919         tw += this.addicon ? this.addicon.getWidth() : 0;
19920         tw += this.editicon ? this.editicon.getWidth() : 0;
19921         var x = w - tw;
19922         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19923             
19924         this.combo.trigger.setStyle('left', '0px');
19925         
19926         if(this.list && this.listWidth === undefined){
19927             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19928             this.list.setWidth(lw);
19929             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19930         }
19931         
19932     
19933         
19934     },
19935     
19936     addItem: function(rec)
19937     {
19938         var valueField = this.combo.valueField;
19939         var displayField = this.combo.displayField;
19940         
19941         if (this.items.indexOfKey(rec[valueField]) > -1) {
19942             //console.log("GOT " + rec.data.id);
19943             return;
19944         }
19945         
19946         var x = new Roo.form.ComboBoxArray.Item({
19947             //id : rec[this.idField],
19948             data : rec,
19949             displayField : displayField ,
19950             tipField : displayField ,
19951             cb : this
19952         });
19953         // use the 
19954         this.items.add(rec[valueField],x);
19955         // add it before the element..
19956         this.updateHiddenEl();
19957         x.render(this.outerWrap, this.wrap.dom);
19958         // add the image handler..
19959     },
19960     
19961     updateHiddenEl : function()
19962     {
19963         this.validate();
19964         if (!this.hiddenEl) {
19965             return;
19966         }
19967         var ar = [];
19968         var idField = this.combo.valueField;
19969         
19970         this.items.each(function(f) {
19971             ar.push(f.data[idField]);
19972         });
19973         this.hiddenEl.dom.value = ar.join(',');
19974         this.validate();
19975     },
19976     
19977     reset : function()
19978     {
19979         this.items.clear();
19980         
19981         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19982            el.remove();
19983         });
19984         
19985         this.el.dom.value = '';
19986         if (this.hiddenEl) {
19987             this.hiddenEl.dom.value = '';
19988         }
19989         
19990     },
19991     getValue: function()
19992     {
19993         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19994     },
19995     setValue: function(v) // not a valid action - must use addItems..
19996     {
19997         
19998         this.reset();
19999          
20000         if (this.store.isLocal && (typeof(v) == 'string')) {
20001             // then we can use the store to find the values..
20002             // comma seperated at present.. this needs to allow JSON based encoding..
20003             this.hiddenEl.value  = v;
20004             var v_ar = [];
20005             Roo.each(v.split(','), function(k) {
20006                 Roo.log("CHECK " + this.valueField + ',' + k);
20007                 var li = this.store.query(this.valueField, k);
20008                 if (!li.length) {
20009                     return;
20010                 }
20011                 var add = {};
20012                 add[this.valueField] = k;
20013                 add[this.displayField] = li.item(0).data[this.displayField];
20014                 
20015                 this.addItem(add);
20016             }, this) 
20017              
20018         }
20019         if (typeof(v) == 'object' ) {
20020             // then let's assume it's an array of objects..
20021             Roo.each(v, function(l) {
20022                 this.addItem(l);
20023             }, this);
20024              
20025         }
20026         
20027         
20028     },
20029     setFromData: function(v)
20030     {
20031         // this recieves an object, if setValues is called.
20032         this.reset();
20033         this.el.dom.value = v[this.displayField];
20034         this.hiddenEl.dom.value = v[this.valueField];
20035         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
20036             return;
20037         }
20038         var kv = v[this.valueField];
20039         var dv = v[this.displayField];
20040         kv = typeof(kv) != 'string' ? '' : kv;
20041         dv = typeof(dv) != 'string' ? '' : dv;
20042         
20043         
20044         var keys = kv.split(',');
20045         var display = dv.split(',');
20046         for (var i = 0 ; i < keys.length; i++) {
20047             
20048             add = {};
20049             add[this.valueField] = keys[i];
20050             add[this.displayField] = display[i];
20051             this.addItem(add);
20052         }
20053       
20054         
20055     },
20056     
20057     /**
20058      * Validates the combox array value
20059      * @return {Boolean} True if the value is valid, else false
20060      */
20061     validate : function(){
20062         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
20063             this.clearInvalid();
20064             return true;
20065         }
20066         return false;
20067     },
20068     
20069     validateValue : function(value){
20070         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
20071         
20072     },
20073     
20074     /*@
20075      * overide
20076      * 
20077      */
20078     isDirty : function() {
20079         if(this.disabled) {
20080             return false;
20081         }
20082         
20083         try {
20084             var d = Roo.decode(String(this.originalValue));
20085         } catch (e) {
20086             return String(this.getValue()) !== String(this.originalValue);
20087         }
20088         
20089         var originalValue = [];
20090         
20091         for (var i = 0; i < d.length; i++){
20092             originalValue.push(d[i][this.valueField]);
20093         }
20094         
20095         return String(this.getValue()) !== String(originalValue.join(','));
20096         
20097     }
20098     
20099 });
20100
20101
20102
20103 /**
20104  * @class Roo.form.ComboBoxArray.Item
20105  * @extends Roo.BoxComponent
20106  * A selected item in the list
20107  *  Fred [x]  Brian [x]  [Pick another |v]
20108  * 
20109  * @constructor
20110  * Create a new item.
20111  * @param {Object} config Configuration options
20112  */
20113  
20114 Roo.form.ComboBoxArray.Item = function(config) {
20115     config.id = Roo.id();
20116     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20117 }
20118
20119 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20120     data : {},
20121     cb: false,
20122     displayField : false,
20123     tipField : false,
20124     
20125     
20126     defaultAutoCreate : {
20127         tag: 'div',
20128         cls: 'x-cbarray-item',
20129         cn : [ 
20130             { tag: 'div' },
20131             {
20132                 tag: 'img',
20133                 width:16,
20134                 height : 16,
20135                 src : Roo.BLANK_IMAGE_URL ,
20136                 align: 'center'
20137             }
20138         ]
20139         
20140     },
20141     
20142  
20143     onRender : function(ct, position)
20144     {
20145         Roo.form.Field.superclass.onRender.call(this, ct, position);
20146         
20147         if(!this.el){
20148             var cfg = this.getAutoCreate();
20149             this.el = ct.createChild(cfg, position);
20150         }
20151         
20152         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20153         
20154         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20155             this.cb.renderer(this.data) :
20156             String.format('{0}',this.data[this.displayField]);
20157         
20158             
20159         this.el.child('div').dom.setAttribute('qtip',
20160                         String.format('{0}',this.data[this.tipField])
20161         );
20162         
20163         this.el.child('img').on('click', this.remove, this);
20164         
20165     },
20166    
20167     remove : function()
20168     {
20169         if(this.cb.disabled){
20170             return;
20171         }
20172         
20173         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20174             this.cb.items.remove(this);
20175             this.el.child('img').un('click', this.remove, this);
20176             this.el.remove();
20177             this.cb.updateHiddenEl();
20178
20179             this.cb.fireEvent('remove', this.cb, this);
20180         }
20181         
20182     }
20183 });/*
20184  * Based on:
20185  * Ext JS Library 1.1.1
20186  * Copyright(c) 2006-2007, Ext JS, LLC.
20187  *
20188  * Originally Released Under LGPL - original licence link has changed is not relivant.
20189  *
20190  * Fork - LGPL
20191  * <script type="text/javascript">
20192  */
20193 /**
20194  * @class Roo.form.Checkbox
20195  * @extends Roo.form.Field
20196  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20197  * @constructor
20198  * Creates a new Checkbox
20199  * @param {Object} config Configuration options
20200  */
20201 Roo.form.Checkbox = function(config){
20202     Roo.form.Checkbox.superclass.constructor.call(this, config);
20203     this.addEvents({
20204         /**
20205          * @event check
20206          * Fires when the checkbox is checked or unchecked.
20207              * @param {Roo.form.Checkbox} this This checkbox
20208              * @param {Boolean} checked The new checked value
20209              */
20210         check : true
20211     });
20212 };
20213
20214 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20215     /**
20216      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20217      */
20218     focusClass : undefined,
20219     /**
20220      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20221      */
20222     fieldClass: "x-form-field",
20223     /**
20224      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20225      */
20226     checked: false,
20227     /**
20228      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20229      * {tag: "input", type: "checkbox", autocomplete: "off"})
20230      */
20231     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20232     /**
20233      * @cfg {String} boxLabel The text that appears beside the checkbox
20234      */
20235     boxLabel : "",
20236     /**
20237      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20238      */  
20239     inputValue : '1',
20240     /**
20241      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20242      */
20243      valueOff: '0', // value when not checked..
20244
20245     actionMode : 'viewEl', 
20246     //
20247     // private
20248     itemCls : 'x-menu-check-item x-form-item',
20249     groupClass : 'x-menu-group-item',
20250     inputType : 'hidden',
20251     
20252     
20253     inSetChecked: false, // check that we are not calling self...
20254     
20255     inputElement: false, // real input element?
20256     basedOn: false, // ????
20257     
20258     isFormField: true, // not sure where this is needed!!!!
20259
20260     onResize : function(){
20261         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20262         if(!this.boxLabel){
20263             this.el.alignTo(this.wrap, 'c-c');
20264         }
20265     },
20266
20267     initEvents : function(){
20268         Roo.form.Checkbox.superclass.initEvents.call(this);
20269         this.el.on("click", this.onClick,  this);
20270         this.el.on("change", this.onClick,  this);
20271     },
20272
20273
20274     getResizeEl : function(){
20275         return this.wrap;
20276     },
20277
20278     getPositionEl : function(){
20279         return this.wrap;
20280     },
20281
20282     // private
20283     onRender : function(ct, position){
20284         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20285         /*
20286         if(this.inputValue !== undefined){
20287             this.el.dom.value = this.inputValue;
20288         }
20289         */
20290         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20291         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20292         var viewEl = this.wrap.createChild({ 
20293             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20294         this.viewEl = viewEl;   
20295         this.wrap.on('click', this.onClick,  this); 
20296         
20297         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20298         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20299         
20300         
20301         
20302         if(this.boxLabel){
20303             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20304         //    viewEl.on('click', this.onClick,  this); 
20305         }
20306         //if(this.checked){
20307             this.setChecked(this.checked);
20308         //}else{
20309             //this.checked = this.el.dom;
20310         //}
20311
20312     },
20313
20314     // private
20315     initValue : Roo.emptyFn,
20316
20317     /**
20318      * Returns the checked state of the checkbox.
20319      * @return {Boolean} True if checked, else false
20320      */
20321     getValue : function(){
20322         if(this.el){
20323             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20324         }
20325         return this.valueOff;
20326         
20327     },
20328
20329         // private
20330     onClick : function(){ 
20331         if (this.disabled) {
20332             return;
20333         }
20334         this.setChecked(!this.checked);
20335
20336         //if(this.el.dom.checked != this.checked){
20337         //    this.setValue(this.el.dom.checked);
20338        // }
20339     },
20340
20341     /**
20342      * Sets the checked state of the checkbox.
20343      * On is always based on a string comparison between inputValue and the param.
20344      * @param {Boolean/String} value - the value to set 
20345      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20346      */
20347     setValue : function(v,suppressEvent){
20348         
20349         
20350         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20351         //if(this.el && this.el.dom){
20352         //    this.el.dom.checked = this.checked;
20353         //    this.el.dom.defaultChecked = this.checked;
20354         //}
20355         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20356         //this.fireEvent("check", this, this.checked);
20357     },
20358     // private..
20359     setChecked : function(state,suppressEvent)
20360     {
20361         if (this.inSetChecked) {
20362             this.checked = state;
20363             return;
20364         }
20365         
20366     
20367         if(this.wrap){
20368             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20369         }
20370         this.checked = state;
20371         if(suppressEvent !== true){
20372             this.fireEvent('check', this, state);
20373         }
20374         this.inSetChecked = true;
20375         this.el.dom.value = state ? this.inputValue : this.valueOff;
20376         this.inSetChecked = false;
20377         
20378     },
20379     // handle setting of hidden value by some other method!!?!?
20380     setFromHidden: function()
20381     {
20382         if(!this.el){
20383             return;
20384         }
20385         //console.log("SET FROM HIDDEN");
20386         //alert('setFrom hidden');
20387         this.setValue(this.el.dom.value);
20388     },
20389     
20390     onDestroy : function()
20391     {
20392         if(this.viewEl){
20393             Roo.get(this.viewEl).remove();
20394         }
20395          
20396         Roo.form.Checkbox.superclass.onDestroy.call(this);
20397     },
20398     
20399     setBoxLabel : function(str)
20400     {
20401         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20402     }
20403
20404 });/*
20405  * Based on:
20406  * Ext JS Library 1.1.1
20407  * Copyright(c) 2006-2007, Ext JS, LLC.
20408  *
20409  * Originally Released Under LGPL - original licence link has changed is not relivant.
20410  *
20411  * Fork - LGPL
20412  * <script type="text/javascript">
20413  */
20414  
20415 /**
20416  * @class Roo.form.Radio
20417  * @extends Roo.form.Checkbox
20418  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20419  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20420  * @constructor
20421  * Creates a new Radio
20422  * @param {Object} config Configuration options
20423  */
20424 Roo.form.Radio = function(){
20425     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20426 };
20427 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20428     inputType: 'radio',
20429
20430     /**
20431      * If this radio is part of a group, it will return the selected value
20432      * @return {String}
20433      */
20434     getGroupValue : function(){
20435         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20436     },
20437     
20438     
20439     onRender : function(ct, position){
20440         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20441         
20442         if(this.inputValue !== undefined){
20443             this.el.dom.value = this.inputValue;
20444         }
20445          
20446         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20447         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20448         //var viewEl = this.wrap.createChild({ 
20449         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20450         //this.viewEl = viewEl;   
20451         //this.wrap.on('click', this.onClick,  this); 
20452         
20453         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20454         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20455         
20456         
20457         
20458         if(this.boxLabel){
20459             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20460         //    viewEl.on('click', this.onClick,  this); 
20461         }
20462          if(this.checked){
20463             this.el.dom.checked =   'checked' ;
20464         }
20465          
20466     } 
20467     
20468     
20469 });//<script type="text/javascript">
20470
20471 /*
20472  * Based  Ext JS Library 1.1.1
20473  * Copyright(c) 2006-2007, Ext JS, LLC.
20474  * LGPL
20475  *
20476  */
20477  
20478 /**
20479  * @class Roo.HtmlEditorCore
20480  * @extends Roo.Component
20481  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20482  *
20483  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20484  */
20485
20486 Roo.HtmlEditorCore = function(config){
20487     
20488     
20489     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20490     
20491     
20492     this.addEvents({
20493         /**
20494          * @event initialize
20495          * Fires when the editor is fully initialized (including the iframe)
20496          * @param {Roo.HtmlEditorCore} this
20497          */
20498         initialize: true,
20499         /**
20500          * @event activate
20501          * Fires when the editor is first receives the focus. Any insertion must wait
20502          * until after this event.
20503          * @param {Roo.HtmlEditorCore} this
20504          */
20505         activate: true,
20506          /**
20507          * @event beforesync
20508          * Fires before the textarea is updated with content from the editor iframe. Return false
20509          * to cancel the sync.
20510          * @param {Roo.HtmlEditorCore} this
20511          * @param {String} html
20512          */
20513         beforesync: true,
20514          /**
20515          * @event beforepush
20516          * Fires before the iframe editor is updated with content from the textarea. Return false
20517          * to cancel the push.
20518          * @param {Roo.HtmlEditorCore} this
20519          * @param {String} html
20520          */
20521         beforepush: true,
20522          /**
20523          * @event sync
20524          * Fires when the textarea is updated with content from the editor iframe.
20525          * @param {Roo.HtmlEditorCore} this
20526          * @param {String} html
20527          */
20528         sync: true,
20529          /**
20530          * @event push
20531          * Fires when the iframe editor is updated with content from the textarea.
20532          * @param {Roo.HtmlEditorCore} this
20533          * @param {String} html
20534          */
20535         push: true,
20536         
20537         /**
20538          * @event editorevent
20539          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20540          * @param {Roo.HtmlEditorCore} this
20541          */
20542         editorevent: true
20543         
20544     });
20545     
20546     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20547     
20548     // defaults : white / black...
20549     this.applyBlacklists();
20550     
20551     
20552     
20553 };
20554
20555
20556 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20557
20558
20559      /**
20560      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20561      */
20562     
20563     owner : false,
20564     
20565      /**
20566      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20567      *                        Roo.resizable.
20568      */
20569     resizable : false,
20570      /**
20571      * @cfg {Number} height (in pixels)
20572      */   
20573     height: 300,
20574    /**
20575      * @cfg {Number} width (in pixels)
20576      */   
20577     width: 500,
20578     
20579     /**
20580      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20581      * 
20582      */
20583     stylesheets: false,
20584     
20585     // id of frame..
20586     frameId: false,
20587     
20588     // private properties
20589     validationEvent : false,
20590     deferHeight: true,
20591     initialized : false,
20592     activated : false,
20593     sourceEditMode : false,
20594     onFocus : Roo.emptyFn,
20595     iframePad:3,
20596     hideMode:'offsets',
20597     
20598     clearUp: true,
20599     
20600     // blacklist + whitelisted elements..
20601     black: false,
20602     white: false,
20603      
20604     bodyCls : '',
20605
20606     /**
20607      * Protected method that will not generally be called directly. It
20608      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20609      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20610      */
20611     getDocMarkup : function(){
20612         // body styles..
20613         var st = '';
20614         
20615         // inherit styels from page...?? 
20616         if (this.stylesheets === false) {
20617             
20618             Roo.get(document.head).select('style').each(function(node) {
20619                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20620             });
20621             
20622             Roo.get(document.head).select('link').each(function(node) { 
20623                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20624             });
20625             
20626         } else if (!this.stylesheets.length) {
20627                 // simple..
20628                 st = '<style type="text/css">' +
20629                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20630                    '</style>';
20631         } else { 
20632             st = '<style type="text/css">' +
20633                     this.stylesheets +
20634                 '</style>';
20635         }
20636         
20637         st +=  '<style type="text/css">' +
20638             'IMG { cursor: pointer } ' +
20639         '</style>';
20640
20641         var cls = 'roo-htmleditor-body';
20642         
20643         if(this.bodyCls.length){
20644             cls += ' ' + this.bodyCls;
20645         }
20646         
20647         return '<html><head>' + st  +
20648             //<style type="text/css">' +
20649             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20650             //'</style>' +
20651             ' </head><body class="' +  cls + '"></body></html>';
20652     },
20653
20654     // private
20655     onRender : function(ct, position)
20656     {
20657         var _t = this;
20658         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20659         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20660         
20661         
20662         this.el.dom.style.border = '0 none';
20663         this.el.dom.setAttribute('tabIndex', -1);
20664         this.el.addClass('x-hidden hide');
20665         
20666         
20667         
20668         if(Roo.isIE){ // fix IE 1px bogus margin
20669             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20670         }
20671        
20672         
20673         this.frameId = Roo.id();
20674         
20675          
20676         
20677         var iframe = this.owner.wrap.createChild({
20678             tag: 'iframe',
20679             cls: 'form-control', // bootstrap..
20680             id: this.frameId,
20681             name: this.frameId,
20682             frameBorder : 'no',
20683             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20684         }, this.el
20685         );
20686         
20687         
20688         this.iframe = iframe.dom;
20689
20690          this.assignDocWin();
20691         
20692         this.doc.designMode = 'on';
20693        
20694         this.doc.open();
20695         this.doc.write(this.getDocMarkup());
20696         this.doc.close();
20697
20698         
20699         var task = { // must defer to wait for browser to be ready
20700             run : function(){
20701                 //console.log("run task?" + this.doc.readyState);
20702                 this.assignDocWin();
20703                 if(this.doc.body || this.doc.readyState == 'complete'){
20704                     try {
20705                         this.doc.designMode="on";
20706                     } catch (e) {
20707                         return;
20708                     }
20709                     Roo.TaskMgr.stop(task);
20710                     this.initEditor.defer(10, this);
20711                 }
20712             },
20713             interval : 10,
20714             duration: 10000,
20715             scope: this
20716         };
20717         Roo.TaskMgr.start(task);
20718
20719     },
20720
20721     // private
20722     onResize : function(w, h)
20723     {
20724          Roo.log('resize: ' +w + ',' + h );
20725         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20726         if(!this.iframe){
20727             return;
20728         }
20729         if(typeof w == 'number'){
20730             
20731             this.iframe.style.width = w + 'px';
20732         }
20733         if(typeof h == 'number'){
20734             
20735             this.iframe.style.height = h + 'px';
20736             if(this.doc){
20737                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20738             }
20739         }
20740         
20741     },
20742
20743     /**
20744      * Toggles the editor between standard and source edit mode.
20745      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20746      */
20747     toggleSourceEdit : function(sourceEditMode){
20748         
20749         this.sourceEditMode = sourceEditMode === true;
20750         
20751         if(this.sourceEditMode){
20752  
20753             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20754             
20755         }else{
20756             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20757             //this.iframe.className = '';
20758             this.deferFocus();
20759         }
20760         //this.setSize(this.owner.wrap.getSize());
20761         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20762     },
20763
20764     
20765   
20766
20767     /**
20768      * Protected method that will not generally be called directly. If you need/want
20769      * custom HTML cleanup, this is the method you should override.
20770      * @param {String} html The HTML to be cleaned
20771      * return {String} The cleaned HTML
20772      */
20773     cleanHtml : function(html){
20774         html = String(html);
20775         if(html.length > 5){
20776             if(Roo.isSafari){ // strip safari nonsense
20777                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20778             }
20779         }
20780         if(html == '&nbsp;'){
20781             html = '';
20782         }
20783         return html;
20784     },
20785
20786     /**
20787      * HTML Editor -> Textarea
20788      * Protected method that will not generally be called directly. Syncs the contents
20789      * of the editor iframe with the textarea.
20790      */
20791     syncValue : function(){
20792         if(this.initialized){
20793             var bd = (this.doc.body || this.doc.documentElement);
20794             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20795             var html = bd.innerHTML;
20796             if(Roo.isSafari){
20797                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20798                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20799                 if(m && m[1]){
20800                     html = '<div style="'+m[0]+'">' + html + '</div>';
20801                 }
20802             }
20803             html = this.cleanHtml(html);
20804             // fix up the special chars.. normaly like back quotes in word...
20805             // however we do not want to do this with chinese..
20806             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20807                 
20808                 var cc = match.charCodeAt();
20809
20810                 // Get the character value, handling surrogate pairs
20811                 if (match.length == 2) {
20812                     // It's a surrogate pair, calculate the Unicode code point
20813                     var high = match.charCodeAt(0) - 0xD800;
20814                     var low  = match.charCodeAt(1) - 0xDC00;
20815                     cc = (high * 0x400) + low + 0x10000;
20816                 }  else if (
20817                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20818                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20819                     (cc >= 0xf900 && cc < 0xfb00 )
20820                 ) {
20821                         return match;
20822                 }  
20823          
20824                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20825                 return "&#" + cc + ";";
20826                 
20827                 
20828             });
20829             
20830             
20831              
20832             if(this.owner.fireEvent('beforesync', this, html) !== false){
20833                 this.el.dom.value = html;
20834                 this.owner.fireEvent('sync', this, html);
20835             }
20836         }
20837     },
20838
20839     /**
20840      * Protected method that will not generally be called directly. Pushes the value of the textarea
20841      * into the iframe editor.
20842      */
20843     pushValue : function(){
20844         if(this.initialized){
20845             var v = this.el.dom.value.trim();
20846             
20847 //            if(v.length < 1){
20848 //                v = '&#160;';
20849 //            }
20850             
20851             if(this.owner.fireEvent('beforepush', this, v) !== false){
20852                 var d = (this.doc.body || this.doc.documentElement);
20853                 d.innerHTML = v;
20854                 this.cleanUpPaste();
20855                 this.el.dom.value = d.innerHTML;
20856                 this.owner.fireEvent('push', this, v);
20857             }
20858         }
20859     },
20860
20861     // private
20862     deferFocus : function(){
20863         this.focus.defer(10, this);
20864     },
20865
20866     // doc'ed in Field
20867     focus : function(){
20868         if(this.win && !this.sourceEditMode){
20869             this.win.focus();
20870         }else{
20871             this.el.focus();
20872         }
20873     },
20874     
20875     assignDocWin: function()
20876     {
20877         var iframe = this.iframe;
20878         
20879          if(Roo.isIE){
20880             this.doc = iframe.contentWindow.document;
20881             this.win = iframe.contentWindow;
20882         } else {
20883 //            if (!Roo.get(this.frameId)) {
20884 //                return;
20885 //            }
20886 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20887 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20888             
20889             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20890                 return;
20891             }
20892             
20893             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20894             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20895         }
20896     },
20897     
20898     // private
20899     initEditor : function(){
20900         //console.log("INIT EDITOR");
20901         this.assignDocWin();
20902         
20903         
20904         
20905         this.doc.designMode="on";
20906         this.doc.open();
20907         this.doc.write(this.getDocMarkup());
20908         this.doc.close();
20909         
20910         var dbody = (this.doc.body || this.doc.documentElement);
20911         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20912         // this copies styles from the containing element into thsi one..
20913         // not sure why we need all of this..
20914         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20915         
20916         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20917         //ss['background-attachment'] = 'fixed'; // w3c
20918         dbody.bgProperties = 'fixed'; // ie
20919         //Roo.DomHelper.applyStyles(dbody, ss);
20920         Roo.EventManager.on(this.doc, {
20921             //'mousedown': this.onEditorEvent,
20922             'mouseup': this.onEditorEvent,
20923             'dblclick': this.onEditorEvent,
20924             'click': this.onEditorEvent,
20925             'keyup': this.onEditorEvent,
20926             buffer:100,
20927             scope: this
20928         });
20929         if(Roo.isGecko){
20930             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20931         }
20932         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20933             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20934         }
20935         this.initialized = true;
20936
20937         this.owner.fireEvent('initialize', this);
20938         this.pushValue();
20939     },
20940
20941     // private
20942     onDestroy : function(){
20943         
20944         
20945         
20946         if(this.rendered){
20947             
20948             //for (var i =0; i < this.toolbars.length;i++) {
20949             //    // fixme - ask toolbars for heights?
20950             //    this.toolbars[i].onDestroy();
20951            // }
20952             
20953             //this.wrap.dom.innerHTML = '';
20954             //this.wrap.remove();
20955         }
20956     },
20957
20958     // private
20959     onFirstFocus : function(){
20960         
20961         this.assignDocWin();
20962         
20963         
20964         this.activated = true;
20965          
20966     
20967         if(Roo.isGecko){ // prevent silly gecko errors
20968             this.win.focus();
20969             var s = this.win.getSelection();
20970             if(!s.focusNode || s.focusNode.nodeType != 3){
20971                 var r = s.getRangeAt(0);
20972                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20973                 r.collapse(true);
20974                 this.deferFocus();
20975             }
20976             try{
20977                 this.execCmd('useCSS', true);
20978                 this.execCmd('styleWithCSS', false);
20979             }catch(e){}
20980         }
20981         this.owner.fireEvent('activate', this);
20982     },
20983
20984     // private
20985     adjustFont: function(btn){
20986         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20987         //if(Roo.isSafari){ // safari
20988         //    adjust *= 2;
20989        // }
20990         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20991         if(Roo.isSafari){ // safari
20992             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20993             v =  (v < 10) ? 10 : v;
20994             v =  (v > 48) ? 48 : v;
20995             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20996             
20997         }
20998         
20999         
21000         v = Math.max(1, v+adjust);
21001         
21002         this.execCmd('FontSize', v  );
21003     },
21004
21005     onEditorEvent : function(e)
21006     {
21007         this.owner.fireEvent('editorevent', this, e);
21008       //  this.updateToolbar();
21009         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21010     },
21011
21012     insertTag : function(tg)
21013     {
21014         // could be a bit smarter... -> wrap the current selected tRoo..
21015         if (tg.toLowerCase() == 'span' ||
21016             tg.toLowerCase() == 'code' ||
21017             tg.toLowerCase() == 'sup' ||
21018             tg.toLowerCase() == 'sub' 
21019             ) {
21020             
21021             range = this.createRange(this.getSelection());
21022             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21023             wrappingNode.appendChild(range.extractContents());
21024             range.insertNode(wrappingNode);
21025
21026             return;
21027             
21028             
21029             
21030         }
21031         this.execCmd("formatblock",   tg);
21032         
21033     },
21034     
21035     insertText : function(txt)
21036     {
21037         
21038         
21039         var range = this.createRange();
21040         range.deleteContents();
21041                //alert(Sender.getAttribute('label'));
21042                
21043         range.insertNode(this.doc.createTextNode(txt));
21044     } ,
21045     
21046      
21047
21048     /**
21049      * Executes a Midas editor command on the editor document and performs necessary focus and
21050      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21051      * @param {String} cmd The Midas command
21052      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21053      */
21054     relayCmd : function(cmd, value){
21055         this.win.focus();
21056         this.execCmd(cmd, value);
21057         this.owner.fireEvent('editorevent', this);
21058         //this.updateToolbar();
21059         this.owner.deferFocus();
21060     },
21061
21062     /**
21063      * Executes a Midas editor command directly on the editor document.
21064      * For visual commands, you should use {@link #relayCmd} instead.
21065      * <b>This should only be called after the editor is initialized.</b>
21066      * @param {String} cmd The Midas command
21067      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21068      */
21069     execCmd : function(cmd, value){
21070         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21071         this.syncValue();
21072     },
21073  
21074  
21075    
21076     /**
21077      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21078      * to insert tRoo.
21079      * @param {String} text | dom node.. 
21080      */
21081     insertAtCursor : function(text)
21082     {
21083         
21084         if(!this.activated){
21085             return;
21086         }
21087         /*
21088         if(Roo.isIE){
21089             this.win.focus();
21090             var r = this.doc.selection.createRange();
21091             if(r){
21092                 r.collapse(true);
21093                 r.pasteHTML(text);
21094                 this.syncValue();
21095                 this.deferFocus();
21096             
21097             }
21098             return;
21099         }
21100         */
21101         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21102             this.win.focus();
21103             
21104             
21105             // from jquery ui (MIT licenced)
21106             var range, node;
21107             var win = this.win;
21108             
21109             if (win.getSelection && win.getSelection().getRangeAt) {
21110                 range = win.getSelection().getRangeAt(0);
21111                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21112                 range.insertNode(node);
21113             } else if (win.document.selection && win.document.selection.createRange) {
21114                 // no firefox support
21115                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21116                 win.document.selection.createRange().pasteHTML(txt);
21117             } else {
21118                 // no firefox support
21119                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21120                 this.execCmd('InsertHTML', txt);
21121             } 
21122             
21123             this.syncValue();
21124             
21125             this.deferFocus();
21126         }
21127     },
21128  // private
21129     mozKeyPress : function(e){
21130         if(e.ctrlKey){
21131             var c = e.getCharCode(), cmd;
21132           
21133             if(c > 0){
21134                 c = String.fromCharCode(c).toLowerCase();
21135                 switch(c){
21136                     case 'b':
21137                         cmd = 'bold';
21138                         break;
21139                     case 'i':
21140                         cmd = 'italic';
21141                         break;
21142                     
21143                     case 'u':
21144                         cmd = 'underline';
21145                         break;
21146                     
21147                     case 'v':
21148                         this.cleanUpPaste.defer(100, this);
21149                         return;
21150                         
21151                 }
21152                 if(cmd){
21153                     this.win.focus();
21154                     this.execCmd(cmd);
21155                     this.deferFocus();
21156                     e.preventDefault();
21157                 }
21158                 
21159             }
21160         }
21161     },
21162
21163     // private
21164     fixKeys : function(){ // load time branching for fastest keydown performance
21165         if(Roo.isIE){
21166             return function(e){
21167                 var k = e.getKey(), r;
21168                 if(k == e.TAB){
21169                     e.stopEvent();
21170                     r = this.doc.selection.createRange();
21171                     if(r){
21172                         r.collapse(true);
21173                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21174                         this.deferFocus();
21175                     }
21176                     return;
21177                 }
21178                 
21179                 if(k == e.ENTER){
21180                     r = this.doc.selection.createRange();
21181                     if(r){
21182                         var target = r.parentElement();
21183                         if(!target || target.tagName.toLowerCase() != 'li'){
21184                             e.stopEvent();
21185                             r.pasteHTML('<br />');
21186                             r.collapse(false);
21187                             r.select();
21188                         }
21189                     }
21190                 }
21191                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21192                     this.cleanUpPaste.defer(100, this);
21193                     return;
21194                 }
21195                 
21196                 
21197             };
21198         }else if(Roo.isOpera){
21199             return function(e){
21200                 var k = e.getKey();
21201                 if(k == e.TAB){
21202                     e.stopEvent();
21203                     this.win.focus();
21204                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21205                     this.deferFocus();
21206                 }
21207                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21208                     this.cleanUpPaste.defer(100, this);
21209                     return;
21210                 }
21211                 
21212             };
21213         }else if(Roo.isSafari){
21214             return function(e){
21215                 var k = e.getKey();
21216                 
21217                 if(k == e.TAB){
21218                     e.stopEvent();
21219                     this.execCmd('InsertText','\t');
21220                     this.deferFocus();
21221                     return;
21222                 }
21223                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21224                     this.cleanUpPaste.defer(100, this);
21225                     return;
21226                 }
21227                 
21228              };
21229         }
21230     }(),
21231     
21232     getAllAncestors: function()
21233     {
21234         var p = this.getSelectedNode();
21235         var a = [];
21236         if (!p) {
21237             a.push(p); // push blank onto stack..
21238             p = this.getParentElement();
21239         }
21240         
21241         
21242         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21243             a.push(p);
21244             p = p.parentNode;
21245         }
21246         a.push(this.doc.body);
21247         return a;
21248     },
21249     lastSel : false,
21250     lastSelNode : false,
21251     
21252     
21253     getSelection : function() 
21254     {
21255         this.assignDocWin();
21256         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21257     },
21258     
21259     getSelectedNode: function() 
21260     {
21261         // this may only work on Gecko!!!
21262         
21263         // should we cache this!!!!
21264         
21265         
21266         
21267          
21268         var range = this.createRange(this.getSelection()).cloneRange();
21269         
21270         if (Roo.isIE) {
21271             var parent = range.parentElement();
21272             while (true) {
21273                 var testRange = range.duplicate();
21274                 testRange.moveToElementText(parent);
21275                 if (testRange.inRange(range)) {
21276                     break;
21277                 }
21278                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21279                     break;
21280                 }
21281                 parent = parent.parentElement;
21282             }
21283             return parent;
21284         }
21285         
21286         // is ancestor a text element.
21287         var ac =  range.commonAncestorContainer;
21288         if (ac.nodeType == 3) {
21289             ac = ac.parentNode;
21290         }
21291         
21292         var ar = ac.childNodes;
21293          
21294         var nodes = [];
21295         var other_nodes = [];
21296         var has_other_nodes = false;
21297         for (var i=0;i<ar.length;i++) {
21298             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21299                 continue;
21300             }
21301             // fullly contained node.
21302             
21303             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21304                 nodes.push(ar[i]);
21305                 continue;
21306             }
21307             
21308             // probably selected..
21309             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21310                 other_nodes.push(ar[i]);
21311                 continue;
21312             }
21313             // outer..
21314             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21315                 continue;
21316             }
21317             
21318             
21319             has_other_nodes = true;
21320         }
21321         if (!nodes.length && other_nodes.length) {
21322             nodes= other_nodes;
21323         }
21324         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21325             return false;
21326         }
21327         
21328         return nodes[0];
21329     },
21330     createRange: function(sel)
21331     {
21332         // this has strange effects when using with 
21333         // top toolbar - not sure if it's a great idea.
21334         //this.editor.contentWindow.focus();
21335         if (typeof sel != "undefined") {
21336             try {
21337                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21338             } catch(e) {
21339                 return this.doc.createRange();
21340             }
21341         } else {
21342             return this.doc.createRange();
21343         }
21344     },
21345     getParentElement: function()
21346     {
21347         
21348         this.assignDocWin();
21349         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21350         
21351         var range = this.createRange(sel);
21352          
21353         try {
21354             var p = range.commonAncestorContainer;
21355             while (p.nodeType == 3) { // text node
21356                 p = p.parentNode;
21357             }
21358             return p;
21359         } catch (e) {
21360             return null;
21361         }
21362     
21363     },
21364     /***
21365      *
21366      * Range intersection.. the hard stuff...
21367      *  '-1' = before
21368      *  '0' = hits..
21369      *  '1' = after.
21370      *         [ -- selected range --- ]
21371      *   [fail]                        [fail]
21372      *
21373      *    basically..
21374      *      if end is before start or  hits it. fail.
21375      *      if start is after end or hits it fail.
21376      *
21377      *   if either hits (but other is outside. - then it's not 
21378      *   
21379      *    
21380      **/
21381     
21382     
21383     // @see http://www.thismuchiknow.co.uk/?p=64.
21384     rangeIntersectsNode : function(range, node)
21385     {
21386         var nodeRange = node.ownerDocument.createRange();
21387         try {
21388             nodeRange.selectNode(node);
21389         } catch (e) {
21390             nodeRange.selectNodeContents(node);
21391         }
21392     
21393         var rangeStartRange = range.cloneRange();
21394         rangeStartRange.collapse(true);
21395     
21396         var rangeEndRange = range.cloneRange();
21397         rangeEndRange.collapse(false);
21398     
21399         var nodeStartRange = nodeRange.cloneRange();
21400         nodeStartRange.collapse(true);
21401     
21402         var nodeEndRange = nodeRange.cloneRange();
21403         nodeEndRange.collapse(false);
21404     
21405         return rangeStartRange.compareBoundaryPoints(
21406                  Range.START_TO_START, nodeEndRange) == -1 &&
21407                rangeEndRange.compareBoundaryPoints(
21408                  Range.START_TO_START, nodeStartRange) == 1;
21409         
21410          
21411     },
21412     rangeCompareNode : function(range, node)
21413     {
21414         var nodeRange = node.ownerDocument.createRange();
21415         try {
21416             nodeRange.selectNode(node);
21417         } catch (e) {
21418             nodeRange.selectNodeContents(node);
21419         }
21420         
21421         
21422         range.collapse(true);
21423     
21424         nodeRange.collapse(true);
21425      
21426         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21427         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21428          
21429         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21430         
21431         var nodeIsBefore   =  ss == 1;
21432         var nodeIsAfter    = ee == -1;
21433         
21434         if (nodeIsBefore && nodeIsAfter) {
21435             return 0; // outer
21436         }
21437         if (!nodeIsBefore && nodeIsAfter) {
21438             return 1; //right trailed.
21439         }
21440         
21441         if (nodeIsBefore && !nodeIsAfter) {
21442             return 2;  // left trailed.
21443         }
21444         // fully contined.
21445         return 3;
21446     },
21447
21448     // private? - in a new class?
21449     cleanUpPaste :  function()
21450     {
21451         // cleans up the whole document..
21452         Roo.log('cleanuppaste');
21453         
21454         this.cleanUpChildren(this.doc.body);
21455         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21456         if (clean != this.doc.body.innerHTML) {
21457             this.doc.body.innerHTML = clean;
21458         }
21459         
21460     },
21461     
21462     cleanWordChars : function(input) {// change the chars to hex code
21463         var he = Roo.HtmlEditorCore;
21464         
21465         var output = input;
21466         Roo.each(he.swapCodes, function(sw) { 
21467             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21468             
21469             output = output.replace(swapper, sw[1]);
21470         });
21471         
21472         return output;
21473     },
21474     
21475     
21476     cleanUpChildren : function (n)
21477     {
21478         if (!n.childNodes.length) {
21479             return;
21480         }
21481         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21482            this.cleanUpChild(n.childNodes[i]);
21483         }
21484     },
21485     
21486     
21487         
21488     
21489     cleanUpChild : function (node)
21490     {
21491         var ed = this;
21492         //console.log(node);
21493         if (node.nodeName == "#text") {
21494             // clean up silly Windows -- stuff?
21495             return; 
21496         }
21497         if (node.nodeName == "#comment") {
21498             node.parentNode.removeChild(node);
21499             // clean up silly Windows -- stuff?
21500             return; 
21501         }
21502         var lcname = node.tagName.toLowerCase();
21503         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21504         // whitelist of tags..
21505         
21506         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21507             // remove node.
21508             node.parentNode.removeChild(node);
21509             return;
21510             
21511         }
21512         
21513         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21514         
21515         // spans with no attributes - just remove them..
21516         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21517             remove_keep_children = true;
21518         }
21519         
21520         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21521         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21522         
21523         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21524         //    remove_keep_children = true;
21525         //}
21526         
21527         if (remove_keep_children) {
21528             this.cleanUpChildren(node);
21529             // inserts everything just before this node...
21530             while (node.childNodes.length) {
21531                 var cn = node.childNodes[0];
21532                 node.removeChild(cn);
21533                 node.parentNode.insertBefore(cn, node);
21534             }
21535             node.parentNode.removeChild(node);
21536             return;
21537         }
21538         
21539         if (!node.attributes || !node.attributes.length) {
21540             
21541           
21542             
21543             
21544             this.cleanUpChildren(node);
21545             return;
21546         }
21547         
21548         function cleanAttr(n,v)
21549         {
21550             
21551             if (v.match(/^\./) || v.match(/^\//)) {
21552                 return;
21553             }
21554             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21555                 return;
21556             }
21557             if (v.match(/^#/)) {
21558                 return;
21559             }
21560 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21561             node.removeAttribute(n);
21562             
21563         }
21564         
21565         var cwhite = this.cwhite;
21566         var cblack = this.cblack;
21567             
21568         function cleanStyle(n,v)
21569         {
21570             if (v.match(/expression/)) { //XSS?? should we even bother..
21571                 node.removeAttribute(n);
21572                 return;
21573             }
21574             
21575             var parts = v.split(/;/);
21576             var clean = [];
21577             
21578             Roo.each(parts, function(p) {
21579                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21580                 if (!p.length) {
21581                     return true;
21582                 }
21583                 var l = p.split(':').shift().replace(/\s+/g,'');
21584                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21585                 
21586                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21587 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21588                     //node.removeAttribute(n);
21589                     return true;
21590                 }
21591                 //Roo.log()
21592                 // only allow 'c whitelisted system attributes'
21593                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21594 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21595                     //node.removeAttribute(n);
21596                     return true;
21597                 }
21598                 
21599                 
21600                  
21601                 
21602                 clean.push(p);
21603                 return true;
21604             });
21605             if (clean.length) { 
21606                 node.setAttribute(n, clean.join(';'));
21607             } else {
21608                 node.removeAttribute(n);
21609             }
21610             
21611         }
21612         
21613         
21614         for (var i = node.attributes.length-1; i > -1 ; i--) {
21615             var a = node.attributes[i];
21616             //console.log(a);
21617             
21618             if (a.name.toLowerCase().substr(0,2)=='on')  {
21619                 node.removeAttribute(a.name);
21620                 continue;
21621             }
21622             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21623                 node.removeAttribute(a.name);
21624                 continue;
21625             }
21626             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21627                 cleanAttr(a.name,a.value); // fixme..
21628                 continue;
21629             }
21630             if (a.name == 'style') {
21631                 cleanStyle(a.name,a.value);
21632                 continue;
21633             }
21634             /// clean up MS crap..
21635             // tecnically this should be a list of valid class'es..
21636             
21637             
21638             if (a.name == 'class') {
21639                 if (a.value.match(/^Mso/)) {
21640                     node.removeAttribute('class');
21641                 }
21642                 
21643                 if (a.value.match(/^body$/)) {
21644                     node.removeAttribute('class');
21645                 }
21646                 continue;
21647             }
21648             
21649             // style cleanup!?
21650             // class cleanup?
21651             
21652         }
21653         
21654         
21655         this.cleanUpChildren(node);
21656         
21657         
21658     },
21659     
21660     /**
21661      * Clean up MS wordisms...
21662      */
21663     cleanWord : function(node)
21664     {
21665         if (!node) {
21666             this.cleanWord(this.doc.body);
21667             return;
21668         }
21669         
21670         if(
21671                 node.nodeName == 'SPAN' &&
21672                 !node.hasAttributes() &&
21673                 node.childNodes.length == 1 &&
21674                 node.firstChild.nodeName == "#text"  
21675         ) {
21676             var textNode = node.firstChild;
21677             node.removeChild(textNode);
21678             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21679                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21680             }
21681             node.parentNode.insertBefore(textNode, node);
21682             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21683                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21684             }
21685             node.parentNode.removeChild(node);
21686         }
21687         
21688         if (node.nodeName == "#text") {
21689             // clean up silly Windows -- stuff?
21690             return; 
21691         }
21692         if (node.nodeName == "#comment") {
21693             node.parentNode.removeChild(node);
21694             // clean up silly Windows -- stuff?
21695             return; 
21696         }
21697         
21698         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21699             node.parentNode.removeChild(node);
21700             return;
21701         }
21702         //Roo.log(node.tagName);
21703         // remove - but keep children..
21704         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21705             //Roo.log('-- removed');
21706             while (node.childNodes.length) {
21707                 var cn = node.childNodes[0];
21708                 node.removeChild(cn);
21709                 node.parentNode.insertBefore(cn, node);
21710                 // move node to parent - and clean it..
21711                 this.cleanWord(cn);
21712             }
21713             node.parentNode.removeChild(node);
21714             /// no need to iterate chidlren = it's got none..
21715             //this.iterateChildren(node, this.cleanWord);
21716             return;
21717         }
21718         // clean styles
21719         if (node.className.length) {
21720             
21721             var cn = node.className.split(/\W+/);
21722             var cna = [];
21723             Roo.each(cn, function(cls) {
21724                 if (cls.match(/Mso[a-zA-Z]+/)) {
21725                     return;
21726                 }
21727                 cna.push(cls);
21728             });
21729             node.className = cna.length ? cna.join(' ') : '';
21730             if (!cna.length) {
21731                 node.removeAttribute("class");
21732             }
21733         }
21734         
21735         if (node.hasAttribute("lang")) {
21736             node.removeAttribute("lang");
21737         }
21738         
21739         if (node.hasAttribute("style")) {
21740             
21741             var styles = node.getAttribute("style").split(";");
21742             var nstyle = [];
21743             Roo.each(styles, function(s) {
21744                 if (!s.match(/:/)) {
21745                     return;
21746                 }
21747                 var kv = s.split(":");
21748                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21749                     return;
21750                 }
21751                 // what ever is left... we allow.
21752                 nstyle.push(s);
21753             });
21754             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21755             if (!nstyle.length) {
21756                 node.removeAttribute('style');
21757             }
21758         }
21759         this.iterateChildren(node, this.cleanWord);
21760         
21761         
21762         
21763     },
21764     /**
21765      * iterateChildren of a Node, calling fn each time, using this as the scole..
21766      * @param {DomNode} node node to iterate children of.
21767      * @param {Function} fn method of this class to call on each item.
21768      */
21769     iterateChildren : function(node, fn)
21770     {
21771         if (!node.childNodes.length) {
21772                 return;
21773         }
21774         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21775            fn.call(this, node.childNodes[i])
21776         }
21777     },
21778     
21779     
21780     /**
21781      * cleanTableWidths.
21782      *
21783      * Quite often pasting from word etc.. results in tables with column and widths.
21784      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21785      *
21786      */
21787     cleanTableWidths : function(node)
21788     {
21789          
21790          
21791         if (!node) {
21792             this.cleanTableWidths(this.doc.body);
21793             return;
21794         }
21795         
21796         // ignore list...
21797         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21798             return; 
21799         }
21800         Roo.log(node.tagName);
21801         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21802             this.iterateChildren(node, this.cleanTableWidths);
21803             return;
21804         }
21805         if (node.hasAttribute('width')) {
21806             node.removeAttribute('width');
21807         }
21808         
21809          
21810         if (node.hasAttribute("style")) {
21811             // pretty basic...
21812             
21813             var styles = node.getAttribute("style").split(";");
21814             var nstyle = [];
21815             Roo.each(styles, function(s) {
21816                 if (!s.match(/:/)) {
21817                     return;
21818                 }
21819                 var kv = s.split(":");
21820                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21821                     return;
21822                 }
21823                 // what ever is left... we allow.
21824                 nstyle.push(s);
21825             });
21826             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21827             if (!nstyle.length) {
21828                 node.removeAttribute('style');
21829             }
21830         }
21831         
21832         this.iterateChildren(node, this.cleanTableWidths);
21833         
21834         
21835     },
21836     
21837     
21838     
21839     
21840     domToHTML : function(currentElement, depth, nopadtext) {
21841         
21842         depth = depth || 0;
21843         nopadtext = nopadtext || false;
21844     
21845         if (!currentElement) {
21846             return this.domToHTML(this.doc.body);
21847         }
21848         
21849         //Roo.log(currentElement);
21850         var j;
21851         var allText = false;
21852         var nodeName = currentElement.nodeName;
21853         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21854         
21855         if  (nodeName == '#text') {
21856             
21857             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21858         }
21859         
21860         
21861         var ret = '';
21862         if (nodeName != 'BODY') {
21863              
21864             var i = 0;
21865             // Prints the node tagName, such as <A>, <IMG>, etc
21866             if (tagName) {
21867                 var attr = [];
21868                 for(i = 0; i < currentElement.attributes.length;i++) {
21869                     // quoting?
21870                     var aname = currentElement.attributes.item(i).name;
21871                     if (!currentElement.attributes.item(i).value.length) {
21872                         continue;
21873                     }
21874                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21875                 }
21876                 
21877                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21878             } 
21879             else {
21880                 
21881                 // eack
21882             }
21883         } else {
21884             tagName = false;
21885         }
21886         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21887             return ret;
21888         }
21889         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21890             nopadtext = true;
21891         }
21892         
21893         
21894         // Traverse the tree
21895         i = 0;
21896         var currentElementChild = currentElement.childNodes.item(i);
21897         var allText = true;
21898         var innerHTML  = '';
21899         lastnode = '';
21900         while (currentElementChild) {
21901             // Formatting code (indent the tree so it looks nice on the screen)
21902             var nopad = nopadtext;
21903             if (lastnode == 'SPAN') {
21904                 nopad  = true;
21905             }
21906             // text
21907             if  (currentElementChild.nodeName == '#text') {
21908                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21909                 toadd = nopadtext ? toadd : toadd.trim();
21910                 if (!nopad && toadd.length > 80) {
21911                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21912                 }
21913                 innerHTML  += toadd;
21914                 
21915                 i++;
21916                 currentElementChild = currentElement.childNodes.item(i);
21917                 lastNode = '';
21918                 continue;
21919             }
21920             allText = false;
21921             
21922             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21923                 
21924             // Recursively traverse the tree structure of the child node
21925             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21926             lastnode = currentElementChild.nodeName;
21927             i++;
21928             currentElementChild=currentElement.childNodes.item(i);
21929         }
21930         
21931         ret += innerHTML;
21932         
21933         if (!allText) {
21934                 // The remaining code is mostly for formatting the tree
21935             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21936         }
21937         
21938         
21939         if (tagName) {
21940             ret+= "</"+tagName+">";
21941         }
21942         return ret;
21943         
21944     },
21945         
21946     applyBlacklists : function()
21947     {
21948         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21949         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21950         
21951         this.white = [];
21952         this.black = [];
21953         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21954             if (b.indexOf(tag) > -1) {
21955                 return;
21956             }
21957             this.white.push(tag);
21958             
21959         }, this);
21960         
21961         Roo.each(w, function(tag) {
21962             if (b.indexOf(tag) > -1) {
21963                 return;
21964             }
21965             if (this.white.indexOf(tag) > -1) {
21966                 return;
21967             }
21968             this.white.push(tag);
21969             
21970         }, this);
21971         
21972         
21973         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21974             if (w.indexOf(tag) > -1) {
21975                 return;
21976             }
21977             this.black.push(tag);
21978             
21979         }, this);
21980         
21981         Roo.each(b, function(tag) {
21982             if (w.indexOf(tag) > -1) {
21983                 return;
21984             }
21985             if (this.black.indexOf(tag) > -1) {
21986                 return;
21987             }
21988             this.black.push(tag);
21989             
21990         }, this);
21991         
21992         
21993         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21994         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21995         
21996         this.cwhite = [];
21997         this.cblack = [];
21998         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21999             if (b.indexOf(tag) > -1) {
22000                 return;
22001             }
22002             this.cwhite.push(tag);
22003             
22004         }, this);
22005         
22006         Roo.each(w, function(tag) {
22007             if (b.indexOf(tag) > -1) {
22008                 return;
22009             }
22010             if (this.cwhite.indexOf(tag) > -1) {
22011                 return;
22012             }
22013             this.cwhite.push(tag);
22014             
22015         }, this);
22016         
22017         
22018         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22019             if (w.indexOf(tag) > -1) {
22020                 return;
22021             }
22022             this.cblack.push(tag);
22023             
22024         }, this);
22025         
22026         Roo.each(b, function(tag) {
22027             if (w.indexOf(tag) > -1) {
22028                 return;
22029             }
22030             if (this.cblack.indexOf(tag) > -1) {
22031                 return;
22032             }
22033             this.cblack.push(tag);
22034             
22035         }, this);
22036     },
22037     
22038     setStylesheets : function(stylesheets)
22039     {
22040         if(typeof(stylesheets) == 'string'){
22041             Roo.get(this.iframe.contentDocument.head).createChild({
22042                 tag : 'link',
22043                 rel : 'stylesheet',
22044                 type : 'text/css',
22045                 href : stylesheets
22046             });
22047             
22048             return;
22049         }
22050         var _this = this;
22051      
22052         Roo.each(stylesheets, function(s) {
22053             if(!s.length){
22054                 return;
22055             }
22056             
22057             Roo.get(_this.iframe.contentDocument.head).createChild({
22058                 tag : 'link',
22059                 rel : 'stylesheet',
22060                 type : 'text/css',
22061                 href : s
22062             });
22063         });
22064
22065         
22066     },
22067     
22068     removeStylesheets : function()
22069     {
22070         var _this = this;
22071         
22072         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22073             s.remove();
22074         });
22075     },
22076     
22077     setStyle : function(style)
22078     {
22079         Roo.get(this.iframe.contentDocument.head).createChild({
22080             tag : 'style',
22081             type : 'text/css',
22082             html : style
22083         });
22084
22085         return;
22086     }
22087     
22088     // hide stuff that is not compatible
22089     /**
22090      * @event blur
22091      * @hide
22092      */
22093     /**
22094      * @event change
22095      * @hide
22096      */
22097     /**
22098      * @event focus
22099      * @hide
22100      */
22101     /**
22102      * @event specialkey
22103      * @hide
22104      */
22105     /**
22106      * @cfg {String} fieldClass @hide
22107      */
22108     /**
22109      * @cfg {String} focusClass @hide
22110      */
22111     /**
22112      * @cfg {String} autoCreate @hide
22113      */
22114     /**
22115      * @cfg {String} inputType @hide
22116      */
22117     /**
22118      * @cfg {String} invalidClass @hide
22119      */
22120     /**
22121      * @cfg {String} invalidText @hide
22122      */
22123     /**
22124      * @cfg {String} msgFx @hide
22125      */
22126     /**
22127      * @cfg {String} validateOnBlur @hide
22128      */
22129 });
22130
22131 Roo.HtmlEditorCore.white = [
22132         'area', 'br', 'img', 'input', 'hr', 'wbr',
22133         
22134        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22135        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22136        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22137        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22138        'table',   'ul',         'xmp', 
22139        
22140        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22141       'thead',   'tr', 
22142      
22143       'dir', 'menu', 'ol', 'ul', 'dl',
22144        
22145       'embed',  'object'
22146 ];
22147
22148
22149 Roo.HtmlEditorCore.black = [
22150     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22151         'applet', // 
22152         'base',   'basefont', 'bgsound', 'blink',  'body', 
22153         'frame',  'frameset', 'head',    'html',   'ilayer', 
22154         'iframe', 'layer',  'link',     'meta',    'object',   
22155         'script', 'style' ,'title',  'xml' // clean later..
22156 ];
22157 Roo.HtmlEditorCore.clean = [
22158     'script', 'style', 'title', 'xml'
22159 ];
22160 Roo.HtmlEditorCore.remove = [
22161     'font'
22162 ];
22163 // attributes..
22164
22165 Roo.HtmlEditorCore.ablack = [
22166     'on'
22167 ];
22168     
22169 Roo.HtmlEditorCore.aclean = [ 
22170     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22171 ];
22172
22173 // protocols..
22174 Roo.HtmlEditorCore.pwhite= [
22175         'http',  'https',  'mailto'
22176 ];
22177
22178 // white listed style attributes.
22179 Roo.HtmlEditorCore.cwhite= [
22180       //  'text-align', /// default is to allow most things..
22181       
22182          
22183 //        'font-size'//??
22184 ];
22185
22186 // black listed style attributes.
22187 Roo.HtmlEditorCore.cblack= [
22188       //  'font-size' -- this can be set by the project 
22189 ];
22190
22191
22192 Roo.HtmlEditorCore.swapCodes   =[ 
22193     [    8211, "--" ], 
22194     [    8212, "--" ], 
22195     [    8216,  "'" ],  
22196     [    8217, "'" ],  
22197     [    8220, '"' ],  
22198     [    8221, '"' ],  
22199     [    8226, "*" ],  
22200     [    8230, "..." ]
22201 ]; 
22202
22203     //<script type="text/javascript">
22204
22205 /*
22206  * Ext JS Library 1.1.1
22207  * Copyright(c) 2006-2007, Ext JS, LLC.
22208  * Licence LGPL
22209  * 
22210  */
22211  
22212  
22213 Roo.form.HtmlEditor = function(config){
22214     
22215     
22216     
22217     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22218     
22219     if (!this.toolbars) {
22220         this.toolbars = [];
22221     }
22222     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22223     
22224     
22225 };
22226
22227 /**
22228  * @class Roo.form.HtmlEditor
22229  * @extends Roo.form.Field
22230  * Provides a lightweight HTML Editor component.
22231  *
22232  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22233  * 
22234  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22235  * supported by this editor.</b><br/><br/>
22236  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22237  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22238  */
22239 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22240     /**
22241      * @cfg {Boolean} clearUp
22242      */
22243     clearUp : true,
22244       /**
22245      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22246      */
22247     toolbars : false,
22248    
22249      /**
22250      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22251      *                        Roo.resizable.
22252      */
22253     resizable : false,
22254      /**
22255      * @cfg {Number} height (in pixels)
22256      */   
22257     height: 300,
22258    /**
22259      * @cfg {Number} width (in pixels)
22260      */   
22261     width: 500,
22262     
22263     /**
22264      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22265      * 
22266      */
22267     stylesheets: false,
22268     
22269     
22270      /**
22271      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22272      * 
22273      */
22274     cblack: false,
22275     /**
22276      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22277      * 
22278      */
22279     cwhite: false,
22280     
22281      /**
22282      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22283      * 
22284      */
22285     black: false,
22286     /**
22287      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22288      * 
22289      */
22290     white: false,
22291     
22292     // id of frame..
22293     frameId: false,
22294     
22295     // private properties
22296     validationEvent : false,
22297     deferHeight: true,
22298     initialized : false,
22299     activated : false,
22300     
22301     onFocus : Roo.emptyFn,
22302     iframePad:3,
22303     hideMode:'offsets',
22304     
22305     actionMode : 'container', // defaults to hiding it...
22306     
22307     defaultAutoCreate : { // modified by initCompnoent..
22308         tag: "textarea",
22309         style:"width:500px;height:300px;",
22310         autocomplete: "new-password"
22311     },
22312
22313     // private
22314     initComponent : function(){
22315         this.addEvents({
22316             /**
22317              * @event initialize
22318              * Fires when the editor is fully initialized (including the iframe)
22319              * @param {HtmlEditor} this
22320              */
22321             initialize: true,
22322             /**
22323              * @event activate
22324              * Fires when the editor is first receives the focus. Any insertion must wait
22325              * until after this event.
22326              * @param {HtmlEditor} this
22327              */
22328             activate: true,
22329              /**
22330              * @event beforesync
22331              * Fires before the textarea is updated with content from the editor iframe. Return false
22332              * to cancel the sync.
22333              * @param {HtmlEditor} this
22334              * @param {String} html
22335              */
22336             beforesync: true,
22337              /**
22338              * @event beforepush
22339              * Fires before the iframe editor is updated with content from the textarea. Return false
22340              * to cancel the push.
22341              * @param {HtmlEditor} this
22342              * @param {String} html
22343              */
22344             beforepush: true,
22345              /**
22346              * @event sync
22347              * Fires when the textarea is updated with content from the editor iframe.
22348              * @param {HtmlEditor} this
22349              * @param {String} html
22350              */
22351             sync: true,
22352              /**
22353              * @event push
22354              * Fires when the iframe editor is updated with content from the textarea.
22355              * @param {HtmlEditor} this
22356              * @param {String} html
22357              */
22358             push: true,
22359              /**
22360              * @event editmodechange
22361              * Fires when the editor switches edit modes
22362              * @param {HtmlEditor} this
22363              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22364              */
22365             editmodechange: true,
22366             /**
22367              * @event editorevent
22368              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22369              * @param {HtmlEditor} this
22370              */
22371             editorevent: true,
22372             /**
22373              * @event firstfocus
22374              * Fires when on first focus - needed by toolbars..
22375              * @param {HtmlEditor} this
22376              */
22377             firstfocus: true,
22378             /**
22379              * @event autosave
22380              * Auto save the htmlEditor value as a file into Events
22381              * @param {HtmlEditor} this
22382              */
22383             autosave: true,
22384             /**
22385              * @event savedpreview
22386              * preview the saved version of htmlEditor
22387              * @param {HtmlEditor} this
22388              */
22389             savedpreview: true,
22390             
22391             /**
22392             * @event stylesheetsclick
22393             * Fires when press the Sytlesheets button
22394             * @param {Roo.HtmlEditorCore} this
22395             */
22396             stylesheetsclick: true
22397         });
22398         this.defaultAutoCreate =  {
22399             tag: "textarea",
22400             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22401             autocomplete: "new-password"
22402         };
22403     },
22404
22405     /**
22406      * Protected method that will not generally be called directly. It
22407      * is called when the editor creates its toolbar. Override this method if you need to
22408      * add custom toolbar buttons.
22409      * @param {HtmlEditor} editor
22410      */
22411     createToolbar : function(editor){
22412         Roo.log("create toolbars");
22413         if (!editor.toolbars || !editor.toolbars.length) {
22414             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22415         }
22416         
22417         for (var i =0 ; i < editor.toolbars.length;i++) {
22418             editor.toolbars[i] = Roo.factory(
22419                     typeof(editor.toolbars[i]) == 'string' ?
22420                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22421                 Roo.form.HtmlEditor);
22422             editor.toolbars[i].init(editor);
22423         }
22424          
22425         
22426     },
22427
22428      
22429     // private
22430     onRender : function(ct, position)
22431     {
22432         var _t = this;
22433         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22434         
22435         this.wrap = this.el.wrap({
22436             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22437         });
22438         
22439         this.editorcore.onRender(ct, position);
22440          
22441         if (this.resizable) {
22442             this.resizeEl = new Roo.Resizable(this.wrap, {
22443                 pinned : true,
22444                 wrap: true,
22445                 dynamic : true,
22446                 minHeight : this.height,
22447                 height: this.height,
22448                 handles : this.resizable,
22449                 width: this.width,
22450                 listeners : {
22451                     resize : function(r, w, h) {
22452                         _t.onResize(w,h); // -something
22453                     }
22454                 }
22455             });
22456             
22457         }
22458         this.createToolbar(this);
22459        
22460         
22461         if(!this.width){
22462             this.setSize(this.wrap.getSize());
22463         }
22464         if (this.resizeEl) {
22465             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22466             // should trigger onReize..
22467         }
22468         
22469         this.keyNav = new Roo.KeyNav(this.el, {
22470             
22471             "tab" : function(e){
22472                 e.preventDefault();
22473                 
22474                 var value = this.getValue();
22475                 
22476                 var start = this.el.dom.selectionStart;
22477                 var end = this.el.dom.selectionEnd;
22478                 
22479                 if(!e.shiftKey){
22480                     
22481                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22482                     this.el.dom.setSelectionRange(end + 1, end + 1);
22483                     return;
22484                 }
22485                 
22486                 var f = value.substring(0, start).split("\t");
22487                 
22488                 if(f.pop().length != 0){
22489                     return;
22490                 }
22491                 
22492                 this.setValue(f.join("\t") + value.substring(end));
22493                 this.el.dom.setSelectionRange(start - 1, start - 1);
22494                 
22495             },
22496             
22497             "home" : function(e){
22498                 e.preventDefault();
22499                 
22500                 var curr = this.el.dom.selectionStart;
22501                 var lines = this.getValue().split("\n");
22502                 
22503                 if(!lines.length){
22504                     return;
22505                 }
22506                 
22507                 if(e.ctrlKey){
22508                     this.el.dom.setSelectionRange(0, 0);
22509                     return;
22510                 }
22511                 
22512                 var pos = 0;
22513                 
22514                 for (var i = 0; i < lines.length;i++) {
22515                     pos += lines[i].length;
22516                     
22517                     if(i != 0){
22518                         pos += 1;
22519                     }
22520                     
22521                     if(pos < curr){
22522                         continue;
22523                     }
22524                     
22525                     pos -= lines[i].length;
22526                     
22527                     break;
22528                 }
22529                 
22530                 if(!e.shiftKey){
22531                     this.el.dom.setSelectionRange(pos, pos);
22532                     return;
22533                 }
22534                 
22535                 this.el.dom.selectionStart = pos;
22536                 this.el.dom.selectionEnd = curr;
22537             },
22538             
22539             "end" : function(e){
22540                 e.preventDefault();
22541                 
22542                 var curr = this.el.dom.selectionStart;
22543                 var lines = this.getValue().split("\n");
22544                 
22545                 if(!lines.length){
22546                     return;
22547                 }
22548                 
22549                 if(e.ctrlKey){
22550                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22551                     return;
22552                 }
22553                 
22554                 var pos = 0;
22555                 
22556                 for (var i = 0; i < lines.length;i++) {
22557                     
22558                     pos += lines[i].length;
22559                     
22560                     if(i != 0){
22561                         pos += 1;
22562                     }
22563                     
22564                     if(pos < curr){
22565                         continue;
22566                     }
22567                     
22568                     break;
22569                 }
22570                 
22571                 if(!e.shiftKey){
22572                     this.el.dom.setSelectionRange(pos, pos);
22573                     return;
22574                 }
22575                 
22576                 this.el.dom.selectionStart = curr;
22577                 this.el.dom.selectionEnd = pos;
22578             },
22579
22580             scope : this,
22581
22582             doRelay : function(foo, bar, hname){
22583                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22584             },
22585
22586             forceKeyDown: true
22587         });
22588         
22589 //        if(this.autosave && this.w){
22590 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22591 //        }
22592     },
22593
22594     // private
22595     onResize : function(w, h)
22596     {
22597         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22598         var ew = false;
22599         var eh = false;
22600         
22601         if(this.el ){
22602             if(typeof w == 'number'){
22603                 var aw = w - this.wrap.getFrameWidth('lr');
22604                 this.el.setWidth(this.adjustWidth('textarea', aw));
22605                 ew = aw;
22606             }
22607             if(typeof h == 'number'){
22608                 var tbh = 0;
22609                 for (var i =0; i < this.toolbars.length;i++) {
22610                     // fixme - ask toolbars for heights?
22611                     tbh += this.toolbars[i].tb.el.getHeight();
22612                     if (this.toolbars[i].footer) {
22613                         tbh += this.toolbars[i].footer.el.getHeight();
22614                     }
22615                 }
22616                 
22617                 
22618                 
22619                 
22620                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22621                 ah -= 5; // knock a few pixes off for look..
22622 //                Roo.log(ah);
22623                 this.el.setHeight(this.adjustWidth('textarea', ah));
22624                 var eh = ah;
22625             }
22626         }
22627         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22628         this.editorcore.onResize(ew,eh);
22629         
22630     },
22631
22632     /**
22633      * Toggles the editor between standard and source edit mode.
22634      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22635      */
22636     toggleSourceEdit : function(sourceEditMode)
22637     {
22638         this.editorcore.toggleSourceEdit(sourceEditMode);
22639         
22640         if(this.editorcore.sourceEditMode){
22641             Roo.log('editor - showing textarea');
22642             
22643 //            Roo.log('in');
22644 //            Roo.log(this.syncValue());
22645             this.editorcore.syncValue();
22646             this.el.removeClass('x-hidden');
22647             this.el.dom.removeAttribute('tabIndex');
22648             this.el.focus();
22649             
22650             for (var i = 0; i < this.toolbars.length; i++) {
22651                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22652                     this.toolbars[i].tb.hide();
22653                     this.toolbars[i].footer.hide();
22654                 }
22655             }
22656             
22657         }else{
22658             Roo.log('editor - hiding textarea');
22659 //            Roo.log('out')
22660 //            Roo.log(this.pushValue()); 
22661             this.editorcore.pushValue();
22662             
22663             this.el.addClass('x-hidden');
22664             this.el.dom.setAttribute('tabIndex', -1);
22665             
22666             for (var i = 0; i < this.toolbars.length; i++) {
22667                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22668                     this.toolbars[i].tb.show();
22669                     this.toolbars[i].footer.show();
22670                 }
22671             }
22672             
22673             //this.deferFocus();
22674         }
22675         
22676         this.setSize(this.wrap.getSize());
22677         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22678         
22679         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22680     },
22681  
22682     // private (for BoxComponent)
22683     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22684
22685     // private (for BoxComponent)
22686     getResizeEl : function(){
22687         return this.wrap;
22688     },
22689
22690     // private (for BoxComponent)
22691     getPositionEl : function(){
22692         return this.wrap;
22693     },
22694
22695     // private
22696     initEvents : function(){
22697         this.originalValue = this.getValue();
22698     },
22699
22700     /**
22701      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22702      * @method
22703      */
22704     markInvalid : Roo.emptyFn,
22705     /**
22706      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22707      * @method
22708      */
22709     clearInvalid : Roo.emptyFn,
22710
22711     setValue : function(v){
22712         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22713         this.editorcore.pushValue();
22714     },
22715
22716      
22717     // private
22718     deferFocus : function(){
22719         this.focus.defer(10, this);
22720     },
22721
22722     // doc'ed in Field
22723     focus : function(){
22724         this.editorcore.focus();
22725         
22726     },
22727       
22728
22729     // private
22730     onDestroy : function(){
22731         
22732         
22733         
22734         if(this.rendered){
22735             
22736             for (var i =0; i < this.toolbars.length;i++) {
22737                 // fixme - ask toolbars for heights?
22738                 this.toolbars[i].onDestroy();
22739             }
22740             
22741             this.wrap.dom.innerHTML = '';
22742             this.wrap.remove();
22743         }
22744     },
22745
22746     // private
22747     onFirstFocus : function(){
22748         //Roo.log("onFirstFocus");
22749         this.editorcore.onFirstFocus();
22750          for (var i =0; i < this.toolbars.length;i++) {
22751             this.toolbars[i].onFirstFocus();
22752         }
22753         
22754     },
22755     
22756     // private
22757     syncValue : function()
22758     {
22759         this.editorcore.syncValue();
22760     },
22761     
22762     pushValue : function()
22763     {
22764         this.editorcore.pushValue();
22765     },
22766     
22767     setStylesheets : function(stylesheets)
22768     {
22769         this.editorcore.setStylesheets(stylesheets);
22770     },
22771     
22772     removeStylesheets : function()
22773     {
22774         this.editorcore.removeStylesheets();
22775     }
22776      
22777     
22778     // hide stuff that is not compatible
22779     /**
22780      * @event blur
22781      * @hide
22782      */
22783     /**
22784      * @event change
22785      * @hide
22786      */
22787     /**
22788      * @event focus
22789      * @hide
22790      */
22791     /**
22792      * @event specialkey
22793      * @hide
22794      */
22795     /**
22796      * @cfg {String} fieldClass @hide
22797      */
22798     /**
22799      * @cfg {String} focusClass @hide
22800      */
22801     /**
22802      * @cfg {String} autoCreate @hide
22803      */
22804     /**
22805      * @cfg {String} inputType @hide
22806      */
22807     /**
22808      * @cfg {String} invalidClass @hide
22809      */
22810     /**
22811      * @cfg {String} invalidText @hide
22812      */
22813     /**
22814      * @cfg {String} msgFx @hide
22815      */
22816     /**
22817      * @cfg {String} validateOnBlur @hide
22818      */
22819 });
22820  
22821     // <script type="text/javascript">
22822 /*
22823  * Based on
22824  * Ext JS Library 1.1.1
22825  * Copyright(c) 2006-2007, Ext JS, LLC.
22826  *  
22827  
22828  */
22829
22830 /**
22831  * @class Roo.form.HtmlEditorToolbar1
22832  * Basic Toolbar
22833  * 
22834  * Usage:
22835  *
22836  new Roo.form.HtmlEditor({
22837     ....
22838     toolbars : [
22839         new Roo.form.HtmlEditorToolbar1({
22840             disable : { fonts: 1 , format: 1, ..., ... , ...],
22841             btns : [ .... ]
22842         })
22843     }
22844      
22845  * 
22846  * @cfg {Object} disable List of elements to disable..
22847  * @cfg {Array} btns List of additional buttons.
22848  * 
22849  * 
22850  * NEEDS Extra CSS? 
22851  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22852  */
22853  
22854 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22855 {
22856     
22857     Roo.apply(this, config);
22858     
22859     // default disabled, based on 'good practice'..
22860     this.disable = this.disable || {};
22861     Roo.applyIf(this.disable, {
22862         fontSize : true,
22863         colors : true,
22864         specialElements : true
22865     });
22866     
22867     
22868     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22869     // dont call parent... till later.
22870 }
22871
22872 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22873     
22874     tb: false,
22875     
22876     rendered: false,
22877     
22878     editor : false,
22879     editorcore : false,
22880     /**
22881      * @cfg {Object} disable  List of toolbar elements to disable
22882          
22883      */
22884     disable : false,
22885     
22886     
22887      /**
22888      * @cfg {String} createLinkText The default text for the create link prompt
22889      */
22890     createLinkText : 'Please enter the URL for the link:',
22891     /**
22892      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22893      */
22894     defaultLinkValue : 'http:/'+'/',
22895    
22896     
22897       /**
22898      * @cfg {Array} fontFamilies An array of available font families
22899      */
22900     fontFamilies : [
22901         'Arial',
22902         'Courier New',
22903         'Tahoma',
22904         'Times New Roman',
22905         'Verdana'
22906     ],
22907     
22908     specialChars : [
22909            "&#169;",
22910           "&#174;",     
22911           "&#8482;",    
22912           "&#163;" ,    
22913          // "&#8212;",    
22914           "&#8230;",    
22915           "&#247;" ,    
22916         //  "&#225;" ,     ?? a acute?
22917            "&#8364;"    , //Euro
22918        //   "&#8220;"    ,
22919         //  "&#8221;"    ,
22920         //  "&#8226;"    ,
22921           "&#176;"  //   , // degrees
22922
22923          // "&#233;"     , // e ecute
22924          // "&#250;"     , // u ecute?
22925     ],
22926     
22927     specialElements : [
22928         {
22929             text: "Insert Table",
22930             xtype: 'MenuItem',
22931             xns : Roo.Menu,
22932             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22933                 
22934         },
22935         {    
22936             text: "Insert Image",
22937             xtype: 'MenuItem',
22938             xns : Roo.Menu,
22939             ihtml : '<img src="about:blank"/>'
22940             
22941         }
22942         
22943          
22944     ],
22945     
22946     
22947     inputElements : [ 
22948             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22949             "input:submit", "input:button", "select", "textarea", "label" ],
22950     formats : [
22951         ["p"] ,  
22952         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22953         ["pre"],[ "code"], 
22954         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22955         ['div'],['span'],
22956         ['sup'],['sub']
22957     ],
22958     
22959     cleanStyles : [
22960         "font-size"
22961     ],
22962      /**
22963      * @cfg {String} defaultFont default font to use.
22964      */
22965     defaultFont: 'tahoma',
22966    
22967     fontSelect : false,
22968     
22969     
22970     formatCombo : false,
22971     
22972     init : function(editor)
22973     {
22974         this.editor = editor;
22975         this.editorcore = editor.editorcore ? editor.editorcore : editor;
22976         var editorcore = this.editorcore;
22977         
22978         var _t = this;
22979         
22980         var fid = editorcore.frameId;
22981         var etb = this;
22982         function btn(id, toggle, handler){
22983             var xid = fid + '-'+ id ;
22984             return {
22985                 id : xid,
22986                 cmd : id,
22987                 cls : 'x-btn-icon x-edit-'+id,
22988                 enableToggle:toggle !== false,
22989                 scope: _t, // was editor...
22990                 handler:handler||_t.relayBtnCmd,
22991                 clickEvent:'mousedown',
22992                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22993                 tabIndex:-1
22994             };
22995         }
22996         
22997         
22998         
22999         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23000         this.tb = tb;
23001          // stop form submits
23002         tb.el.on('click', function(e){
23003             e.preventDefault(); // what does this do?
23004         });
23005
23006         if(!this.disable.font) { // && !Roo.isSafari){
23007             /* why no safari for fonts 
23008             editor.fontSelect = tb.el.createChild({
23009                 tag:'select',
23010                 tabIndex: -1,
23011                 cls:'x-font-select',
23012                 html: this.createFontOptions()
23013             });
23014             
23015             editor.fontSelect.on('change', function(){
23016                 var font = editor.fontSelect.dom.value;
23017                 editor.relayCmd('fontname', font);
23018                 editor.deferFocus();
23019             }, editor);
23020             
23021             tb.add(
23022                 editor.fontSelect.dom,
23023                 '-'
23024             );
23025             */
23026             
23027         };
23028         if(!this.disable.formats){
23029             this.formatCombo = new Roo.form.ComboBox({
23030                 store: new Roo.data.SimpleStore({
23031                     id : 'tag',
23032                     fields: ['tag'],
23033                     data : this.formats // from states.js
23034                 }),
23035                 blockFocus : true,
23036                 name : '',
23037                 //autoCreate : {tag: "div",  size: "20"},
23038                 displayField:'tag',
23039                 typeAhead: false,
23040                 mode: 'local',
23041                 editable : false,
23042                 triggerAction: 'all',
23043                 emptyText:'Add tag',
23044                 selectOnFocus:true,
23045                 width:135,
23046                 listeners : {
23047                     'select': function(c, r, i) {
23048                         editorcore.insertTag(r.get('tag'));
23049                         editor.focus();
23050                     }
23051                 }
23052
23053             });
23054             tb.addField(this.formatCombo);
23055             
23056         }
23057         
23058         if(!this.disable.format){
23059             tb.add(
23060                 btn('bold'),
23061                 btn('italic'),
23062                 btn('underline'),
23063                 btn('strikethrough')
23064             );
23065         };
23066         if(!this.disable.fontSize){
23067             tb.add(
23068                 '-',
23069                 
23070                 
23071                 btn('increasefontsize', false, editorcore.adjustFont),
23072                 btn('decreasefontsize', false, editorcore.adjustFont)
23073             );
23074         };
23075         
23076         
23077         if(!this.disable.colors){
23078             tb.add(
23079                 '-', {
23080                     id:editorcore.frameId +'-forecolor',
23081                     cls:'x-btn-icon x-edit-forecolor',
23082                     clickEvent:'mousedown',
23083                     tooltip: this.buttonTips['forecolor'] || undefined,
23084                     tabIndex:-1,
23085                     menu : new Roo.menu.ColorMenu({
23086                         allowReselect: true,
23087                         focus: Roo.emptyFn,
23088                         value:'000000',
23089                         plain:true,
23090                         selectHandler: function(cp, color){
23091                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23092                             editor.deferFocus();
23093                         },
23094                         scope: editorcore,
23095                         clickEvent:'mousedown'
23096                     })
23097                 }, {
23098                     id:editorcore.frameId +'backcolor',
23099                     cls:'x-btn-icon x-edit-backcolor',
23100                     clickEvent:'mousedown',
23101                     tooltip: this.buttonTips['backcolor'] || undefined,
23102                     tabIndex:-1,
23103                     menu : new Roo.menu.ColorMenu({
23104                         focus: Roo.emptyFn,
23105                         value:'FFFFFF',
23106                         plain:true,
23107                         allowReselect: true,
23108                         selectHandler: function(cp, color){
23109                             if(Roo.isGecko){
23110                                 editorcore.execCmd('useCSS', false);
23111                                 editorcore.execCmd('hilitecolor', color);
23112                                 editorcore.execCmd('useCSS', true);
23113                                 editor.deferFocus();
23114                             }else{
23115                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23116                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23117                                 editor.deferFocus();
23118                             }
23119                         },
23120                         scope:editorcore,
23121                         clickEvent:'mousedown'
23122                     })
23123                 }
23124             );
23125         };
23126         // now add all the items...
23127         
23128
23129         if(!this.disable.alignments){
23130             tb.add(
23131                 '-',
23132                 btn('justifyleft'),
23133                 btn('justifycenter'),
23134                 btn('justifyright')
23135             );
23136         };
23137
23138         //if(!Roo.isSafari){
23139             if(!this.disable.links){
23140                 tb.add(
23141                     '-',
23142                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23143                 );
23144             };
23145
23146             if(!this.disable.lists){
23147                 tb.add(
23148                     '-',
23149                     btn('insertorderedlist'),
23150                     btn('insertunorderedlist')
23151                 );
23152             }
23153             if(!this.disable.sourceEdit){
23154                 tb.add(
23155                     '-',
23156                     btn('sourceedit', true, function(btn){
23157                         this.toggleSourceEdit(btn.pressed);
23158                     })
23159                 );
23160             }
23161         //}
23162         
23163         var smenu = { };
23164         // special menu.. - needs to be tidied up..
23165         if (!this.disable.special) {
23166             smenu = {
23167                 text: "&#169;",
23168                 cls: 'x-edit-none',
23169                 
23170                 menu : {
23171                     items : []
23172                 }
23173             };
23174             for (var i =0; i < this.specialChars.length; i++) {
23175                 smenu.menu.items.push({
23176                     
23177                     html: this.specialChars[i],
23178                     handler: function(a,b) {
23179                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23180                         //editor.insertAtCursor(a.html);
23181                         
23182                     },
23183                     tabIndex:-1
23184                 });
23185             }
23186             
23187             
23188             tb.add(smenu);
23189             
23190             
23191         }
23192         
23193         var cmenu = { };
23194         if (!this.disable.cleanStyles) {
23195             cmenu = {
23196                 cls: 'x-btn-icon x-btn-clear',
23197                 
23198                 menu : {
23199                     items : []
23200                 }
23201             };
23202             for (var i =0; i < this.cleanStyles.length; i++) {
23203                 cmenu.menu.items.push({
23204                     actiontype : this.cleanStyles[i],
23205                     html: 'Remove ' + this.cleanStyles[i],
23206                     handler: function(a,b) {
23207 //                        Roo.log(a);
23208 //                        Roo.log(b);
23209                         var c = Roo.get(editorcore.doc.body);
23210                         c.select('[style]').each(function(s) {
23211                             s.dom.style.removeProperty(a.actiontype);
23212                         });
23213                         editorcore.syncValue();
23214                     },
23215                     tabIndex:-1
23216                 });
23217             }
23218              cmenu.menu.items.push({
23219                 actiontype : 'tablewidths',
23220                 html: 'Remove Table Widths',
23221                 handler: function(a,b) {
23222                     editorcore.cleanTableWidths();
23223                     editorcore.syncValue();
23224                 },
23225                 tabIndex:-1
23226             });
23227             cmenu.menu.items.push({
23228                 actiontype : 'word',
23229                 html: 'Remove MS Word Formating',
23230                 handler: function(a,b) {
23231                     editorcore.cleanWord();
23232                     editorcore.syncValue();
23233                 },
23234                 tabIndex:-1
23235             });
23236             
23237             cmenu.menu.items.push({
23238                 actiontype : 'all',
23239                 html: 'Remove All Styles',
23240                 handler: function(a,b) {
23241                     
23242                     var c = Roo.get(editorcore.doc.body);
23243                     c.select('[style]').each(function(s) {
23244                         s.dom.removeAttribute('style');
23245                     });
23246                     editorcore.syncValue();
23247                 },
23248                 tabIndex:-1
23249             });
23250             
23251             cmenu.menu.items.push({
23252                 actiontype : 'all',
23253                 html: 'Remove All CSS Classes',
23254                 handler: function(a,b) {
23255                     
23256                     var c = Roo.get(editorcore.doc.body);
23257                     c.select('[class]').each(function(s) {
23258                         s.dom.removeAttribute('class');
23259                     });
23260                     editorcore.cleanWord();
23261                     editorcore.syncValue();
23262                 },
23263                 tabIndex:-1
23264             });
23265             
23266              cmenu.menu.items.push({
23267                 actiontype : 'tidy',
23268                 html: 'Tidy HTML Source',
23269                 handler: function(a,b) {
23270                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23271                     editorcore.syncValue();
23272                 },
23273                 tabIndex:-1
23274             });
23275             
23276             
23277             tb.add(cmenu);
23278         }
23279          
23280         if (!this.disable.specialElements) {
23281             var semenu = {
23282                 text: "Other;",
23283                 cls: 'x-edit-none',
23284                 menu : {
23285                     items : []
23286                 }
23287             };
23288             for (var i =0; i < this.specialElements.length; i++) {
23289                 semenu.menu.items.push(
23290                     Roo.apply({ 
23291                         handler: function(a,b) {
23292                             editor.insertAtCursor(this.ihtml);
23293                         }
23294                     }, this.specialElements[i])
23295                 );
23296                     
23297             }
23298             
23299             tb.add(semenu);
23300             
23301             
23302         }
23303          
23304         
23305         if (this.btns) {
23306             for(var i =0; i< this.btns.length;i++) {
23307                 var b = Roo.factory(this.btns[i],Roo.form);
23308                 b.cls =  'x-edit-none';
23309                 
23310                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23311                     b.cls += ' x-init-enable';
23312                 }
23313                 
23314                 b.scope = editorcore;
23315                 tb.add(b);
23316             }
23317         
23318         }
23319         
23320         
23321         
23322         // disable everything...
23323         
23324         this.tb.items.each(function(item){
23325             
23326            if(
23327                 item.id != editorcore.frameId+ '-sourceedit' && 
23328                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23329             ){
23330                 
23331                 item.disable();
23332             }
23333         });
23334         this.rendered = true;
23335         
23336         // the all the btns;
23337         editor.on('editorevent', this.updateToolbar, this);
23338         // other toolbars need to implement this..
23339         //editor.on('editmodechange', this.updateToolbar, this);
23340     },
23341     
23342     
23343     relayBtnCmd : function(btn) {
23344         this.editorcore.relayCmd(btn.cmd);
23345     },
23346     // private used internally
23347     createLink : function(){
23348         Roo.log("create link?");
23349         var url = prompt(this.createLinkText, this.defaultLinkValue);
23350         if(url && url != 'http:/'+'/'){
23351             this.editorcore.relayCmd('createlink', url);
23352         }
23353     },
23354
23355     
23356     /**
23357      * Protected method that will not generally be called directly. It triggers
23358      * a toolbar update by reading the markup state of the current selection in the editor.
23359      */
23360     updateToolbar: function(){
23361
23362         if(!this.editorcore.activated){
23363             this.editor.onFirstFocus();
23364             return;
23365         }
23366
23367         var btns = this.tb.items.map, 
23368             doc = this.editorcore.doc,
23369             frameId = this.editorcore.frameId;
23370
23371         if(!this.disable.font && !Roo.isSafari){
23372             /*
23373             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23374             if(name != this.fontSelect.dom.value){
23375                 this.fontSelect.dom.value = name;
23376             }
23377             */
23378         }
23379         if(!this.disable.format){
23380             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23381             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23382             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23383             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23384         }
23385         if(!this.disable.alignments){
23386             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23387             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23388             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23389         }
23390         if(!Roo.isSafari && !this.disable.lists){
23391             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23392             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23393         }
23394         
23395         var ans = this.editorcore.getAllAncestors();
23396         if (this.formatCombo) {
23397             
23398             
23399             var store = this.formatCombo.store;
23400             this.formatCombo.setValue("");
23401             for (var i =0; i < ans.length;i++) {
23402                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23403                     // select it..
23404                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23405                     break;
23406                 }
23407             }
23408         }
23409         
23410         
23411         
23412         // hides menus... - so this cant be on a menu...
23413         Roo.menu.MenuMgr.hideAll();
23414
23415         //this.editorsyncValue();
23416     },
23417    
23418     
23419     createFontOptions : function(){
23420         var buf = [], fs = this.fontFamilies, ff, lc;
23421         
23422         
23423         
23424         for(var i = 0, len = fs.length; i< len; i++){
23425             ff = fs[i];
23426             lc = ff.toLowerCase();
23427             buf.push(
23428                 '<option value="',lc,'" style="font-family:',ff,';"',
23429                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23430                     ff,
23431                 '</option>'
23432             );
23433         }
23434         return buf.join('');
23435     },
23436     
23437     toggleSourceEdit : function(sourceEditMode){
23438         
23439         Roo.log("toolbar toogle");
23440         if(sourceEditMode === undefined){
23441             sourceEditMode = !this.sourceEditMode;
23442         }
23443         this.sourceEditMode = sourceEditMode === true;
23444         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23445         // just toggle the button?
23446         if(btn.pressed !== this.sourceEditMode){
23447             btn.toggle(this.sourceEditMode);
23448             return;
23449         }
23450         
23451         if(sourceEditMode){
23452             Roo.log("disabling buttons");
23453             this.tb.items.each(function(item){
23454                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23455                     item.disable();
23456                 }
23457             });
23458           
23459         }else{
23460             Roo.log("enabling buttons");
23461             if(this.editorcore.initialized){
23462                 this.tb.items.each(function(item){
23463                     item.enable();
23464                 });
23465             }
23466             
23467         }
23468         Roo.log("calling toggole on editor");
23469         // tell the editor that it's been pressed..
23470         this.editor.toggleSourceEdit(sourceEditMode);
23471        
23472     },
23473      /**
23474      * Object collection of toolbar tooltips for the buttons in the editor. The key
23475      * is the command id associated with that button and the value is a valid QuickTips object.
23476      * For example:
23477 <pre><code>
23478 {
23479     bold : {
23480         title: 'Bold (Ctrl+B)',
23481         text: 'Make the selected text bold.',
23482         cls: 'x-html-editor-tip'
23483     },
23484     italic : {
23485         title: 'Italic (Ctrl+I)',
23486         text: 'Make the selected text italic.',
23487         cls: 'x-html-editor-tip'
23488     },
23489     ...
23490 </code></pre>
23491     * @type Object
23492      */
23493     buttonTips : {
23494         bold : {
23495             title: 'Bold (Ctrl+B)',
23496             text: 'Make the selected text bold.',
23497             cls: 'x-html-editor-tip'
23498         },
23499         italic : {
23500             title: 'Italic (Ctrl+I)',
23501             text: 'Make the selected text italic.',
23502             cls: 'x-html-editor-tip'
23503         },
23504         underline : {
23505             title: 'Underline (Ctrl+U)',
23506             text: 'Underline the selected text.',
23507             cls: 'x-html-editor-tip'
23508         },
23509         strikethrough : {
23510             title: 'Strikethrough',
23511             text: 'Strikethrough the selected text.',
23512             cls: 'x-html-editor-tip'
23513         },
23514         increasefontsize : {
23515             title: 'Grow Text',
23516             text: 'Increase the font size.',
23517             cls: 'x-html-editor-tip'
23518         },
23519         decreasefontsize : {
23520             title: 'Shrink Text',
23521             text: 'Decrease the font size.',
23522             cls: 'x-html-editor-tip'
23523         },
23524         backcolor : {
23525             title: 'Text Highlight Color',
23526             text: 'Change the background color of the selected text.',
23527             cls: 'x-html-editor-tip'
23528         },
23529         forecolor : {
23530             title: 'Font Color',
23531             text: 'Change the color of the selected text.',
23532             cls: 'x-html-editor-tip'
23533         },
23534         justifyleft : {
23535             title: 'Align Text Left',
23536             text: 'Align text to the left.',
23537             cls: 'x-html-editor-tip'
23538         },
23539         justifycenter : {
23540             title: 'Center Text',
23541             text: 'Center text in the editor.',
23542             cls: 'x-html-editor-tip'
23543         },
23544         justifyright : {
23545             title: 'Align Text Right',
23546             text: 'Align text to the right.',
23547             cls: 'x-html-editor-tip'
23548         },
23549         insertunorderedlist : {
23550             title: 'Bullet List',
23551             text: 'Start a bulleted list.',
23552             cls: 'x-html-editor-tip'
23553         },
23554         insertorderedlist : {
23555             title: 'Numbered List',
23556             text: 'Start a numbered list.',
23557             cls: 'x-html-editor-tip'
23558         },
23559         createlink : {
23560             title: 'Hyperlink',
23561             text: 'Make the selected text a hyperlink.',
23562             cls: 'x-html-editor-tip'
23563         },
23564         sourceedit : {
23565             title: 'Source Edit',
23566             text: 'Switch to source editing mode.',
23567             cls: 'x-html-editor-tip'
23568         }
23569     },
23570     // private
23571     onDestroy : function(){
23572         if(this.rendered){
23573             
23574             this.tb.items.each(function(item){
23575                 if(item.menu){
23576                     item.menu.removeAll();
23577                     if(item.menu.el){
23578                         item.menu.el.destroy();
23579                     }
23580                 }
23581                 item.destroy();
23582             });
23583              
23584         }
23585     },
23586     onFirstFocus: function() {
23587         this.tb.items.each(function(item){
23588            item.enable();
23589         });
23590     }
23591 });
23592
23593
23594
23595
23596 // <script type="text/javascript">
23597 /*
23598  * Based on
23599  * Ext JS Library 1.1.1
23600  * Copyright(c) 2006-2007, Ext JS, LLC.
23601  *  
23602  
23603  */
23604
23605  
23606 /**
23607  * @class Roo.form.HtmlEditor.ToolbarContext
23608  * Context Toolbar
23609  * 
23610  * Usage:
23611  *
23612  new Roo.form.HtmlEditor({
23613     ....
23614     toolbars : [
23615         { xtype: 'ToolbarStandard', styles : {} }
23616         { xtype: 'ToolbarContext', disable : {} }
23617     ]
23618 })
23619
23620      
23621  * 
23622  * @config : {Object} disable List of elements to disable.. (not done yet.)
23623  * @config : {Object} styles  Map of styles available.
23624  * 
23625  */
23626
23627 Roo.form.HtmlEditor.ToolbarContext = function(config)
23628 {
23629     
23630     Roo.apply(this, config);
23631     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23632     // dont call parent... till later.
23633     this.styles = this.styles || {};
23634 }
23635
23636  
23637
23638 Roo.form.HtmlEditor.ToolbarContext.types = {
23639     'IMG' : {
23640         width : {
23641             title: "Width",
23642             width: 40
23643         },
23644         height:  {
23645             title: "Height",
23646             width: 40
23647         },
23648         align: {
23649             title: "Align",
23650             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23651             width : 80
23652             
23653         },
23654         border: {
23655             title: "Border",
23656             width: 40
23657         },
23658         alt: {
23659             title: "Alt",
23660             width: 120
23661         },
23662         src : {
23663             title: "Src",
23664             width: 220
23665         }
23666         
23667     },
23668     'A' : {
23669         name : {
23670             title: "Name",
23671             width: 50
23672         },
23673         target:  {
23674             title: "Target",
23675             width: 120
23676         },
23677         href:  {
23678             title: "Href",
23679             width: 220
23680         } // border?
23681         
23682     },
23683     'TABLE' : {
23684         rows : {
23685             title: "Rows",
23686             width: 20
23687         },
23688         cols : {
23689             title: "Cols",
23690             width: 20
23691         },
23692         width : {
23693             title: "Width",
23694             width: 40
23695         },
23696         height : {
23697             title: "Height",
23698             width: 40
23699         },
23700         border : {
23701             title: "Border",
23702             width: 20
23703         }
23704     },
23705     'TD' : {
23706         width : {
23707             title: "Width",
23708             width: 40
23709         },
23710         height : {
23711             title: "Height",
23712             width: 40
23713         },   
23714         align: {
23715             title: "Align",
23716             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23717             width: 80
23718         },
23719         valign: {
23720             title: "Valign",
23721             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23722             width: 80
23723         },
23724         colspan: {
23725             title: "Colspan",
23726             width: 20
23727             
23728         },
23729          'font-family'  : {
23730             title : "Font",
23731             style : 'fontFamily',
23732             displayField: 'display',
23733             optname : 'font-family',
23734             width: 140
23735         }
23736     },
23737     'INPUT' : {
23738         name : {
23739             title: "name",
23740             width: 120
23741         },
23742         value : {
23743             title: "Value",
23744             width: 120
23745         },
23746         width : {
23747             title: "Width",
23748             width: 40
23749         }
23750     },
23751     'LABEL' : {
23752         'for' : {
23753             title: "For",
23754             width: 120
23755         }
23756     },
23757     'TEXTAREA' : {
23758           name : {
23759             title: "name",
23760             width: 120
23761         },
23762         rows : {
23763             title: "Rows",
23764             width: 20
23765         },
23766         cols : {
23767             title: "Cols",
23768             width: 20
23769         }
23770     },
23771     'SELECT' : {
23772         name : {
23773             title: "name",
23774             width: 120
23775         },
23776         selectoptions : {
23777             title: "Options",
23778             width: 200
23779         }
23780     },
23781     
23782     // should we really allow this??
23783     // should this just be 
23784     'BODY' : {
23785         title : {
23786             title: "Title",
23787             width: 200,
23788             disabled : true
23789         }
23790     },
23791     'SPAN' : {
23792         'font-family'  : {
23793             title : "Font",
23794             style : 'fontFamily',
23795             displayField: 'display',
23796             optname : 'font-family',
23797             width: 140
23798         }
23799     },
23800     'DIV' : {
23801         'font-family'  : {
23802             title : "Font",
23803             style : 'fontFamily',
23804             displayField: 'display',
23805             optname : 'font-family',
23806             width: 140
23807         }
23808     },
23809      'P' : {
23810         'font-family'  : {
23811             title : "Font",
23812             style : 'fontFamily',
23813             displayField: 'display',
23814             optname : 'font-family',
23815             width: 140
23816         }
23817     },
23818     
23819     '*' : {
23820         // empty..
23821     }
23822
23823 };
23824
23825 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23826 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23827
23828 Roo.form.HtmlEditor.ToolbarContext.options = {
23829         'font-family'  : [ 
23830                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23831                 [ 'Courier New', 'Courier New'],
23832                 [ 'Tahoma', 'Tahoma'],
23833                 [ 'Times New Roman,serif', 'Times'],
23834                 [ 'Verdana','Verdana' ]
23835         ]
23836 };
23837
23838 // fixme - these need to be configurable..
23839  
23840
23841 //Roo.form.HtmlEditor.ToolbarContext.types
23842
23843
23844 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23845     
23846     tb: false,
23847     
23848     rendered: false,
23849     
23850     editor : false,
23851     editorcore : false,
23852     /**
23853      * @cfg {Object} disable  List of toolbar elements to disable
23854          
23855      */
23856     disable : false,
23857     /**
23858      * @cfg {Object} styles List of styles 
23859      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23860      *
23861      * These must be defined in the page, so they get rendered correctly..
23862      * .headline { }
23863      * TD.underline { }
23864      * 
23865      */
23866     styles : false,
23867     
23868     options: false,
23869     
23870     toolbars : false,
23871     
23872     init : function(editor)
23873     {
23874         this.editor = editor;
23875         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23876         var editorcore = this.editorcore;
23877         
23878         var fid = editorcore.frameId;
23879         var etb = this;
23880         function btn(id, toggle, handler){
23881             var xid = fid + '-'+ id ;
23882             return {
23883                 id : xid,
23884                 cmd : id,
23885                 cls : 'x-btn-icon x-edit-'+id,
23886                 enableToggle:toggle !== false,
23887                 scope: editorcore, // was editor...
23888                 handler:handler||editorcore.relayBtnCmd,
23889                 clickEvent:'mousedown',
23890                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23891                 tabIndex:-1
23892             };
23893         }
23894         // create a new element.
23895         var wdiv = editor.wrap.createChild({
23896                 tag: 'div'
23897             }, editor.wrap.dom.firstChild.nextSibling, true);
23898         
23899         // can we do this more than once??
23900         
23901          // stop form submits
23902       
23903  
23904         // disable everything...
23905         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23906         this.toolbars = {};
23907            
23908         for (var i in  ty) {
23909           
23910             this.toolbars[i] = this.buildToolbar(ty[i],i);
23911         }
23912         this.tb = this.toolbars.BODY;
23913         this.tb.el.show();
23914         this.buildFooter();
23915         this.footer.show();
23916         editor.on('hide', function( ) { this.footer.hide() }, this);
23917         editor.on('show', function( ) { this.footer.show() }, this);
23918         
23919          
23920         this.rendered = true;
23921         
23922         // the all the btns;
23923         editor.on('editorevent', this.updateToolbar, this);
23924         // other toolbars need to implement this..
23925         //editor.on('editmodechange', this.updateToolbar, this);
23926     },
23927     
23928     
23929     
23930     /**
23931      * Protected method that will not generally be called directly. It triggers
23932      * a toolbar update by reading the markup state of the current selection in the editor.
23933      *
23934      * Note you can force an update by calling on('editorevent', scope, false)
23935      */
23936     updateToolbar: function(editor,ev,sel){
23937
23938         //Roo.log(ev);
23939         // capture mouse up - this is handy for selecting images..
23940         // perhaps should go somewhere else...
23941         if(!this.editorcore.activated){
23942              this.editor.onFirstFocus();
23943             return;
23944         }
23945         
23946         
23947         
23948         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23949         // selectNode - might want to handle IE?
23950         if (ev &&
23951             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23952             ev.target && ev.target.tagName == 'IMG') {
23953             // they have click on an image...
23954             // let's see if we can change the selection...
23955             sel = ev.target;
23956          
23957               var nodeRange = sel.ownerDocument.createRange();
23958             try {
23959                 nodeRange.selectNode(sel);
23960             } catch (e) {
23961                 nodeRange.selectNodeContents(sel);
23962             }
23963             //nodeRange.collapse(true);
23964             var s = this.editorcore.win.getSelection();
23965             s.removeAllRanges();
23966             s.addRange(nodeRange);
23967         }  
23968         
23969       
23970         var updateFooter = sel ? false : true;
23971         
23972         
23973         var ans = this.editorcore.getAllAncestors();
23974         
23975         // pick
23976         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23977         
23978         if (!sel) { 
23979             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
23980             sel = sel ? sel : this.editorcore.doc.body;
23981             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23982             
23983         }
23984         // pick a menu that exists..
23985         var tn = sel.tagName.toUpperCase();
23986         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23987         
23988         tn = sel.tagName.toUpperCase();
23989         
23990         var lastSel = this.tb.selectedNode;
23991         
23992         this.tb.selectedNode = sel;
23993         
23994         // if current menu does not match..
23995         
23996         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23997                 
23998             this.tb.el.hide();
23999             ///console.log("show: " + tn);
24000             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24001             this.tb.el.show();
24002             // update name
24003             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24004             
24005             
24006             // update attributes
24007             if (this.tb.fields) {
24008                 this.tb.fields.each(function(e) {
24009                     if (e.stylename) {
24010                         e.setValue(sel.style[e.stylename]);
24011                         return;
24012                     } 
24013                    e.setValue(sel.getAttribute(e.attrname));
24014                 });
24015             }
24016             
24017             var hasStyles = false;
24018             for(var i in this.styles) {
24019                 hasStyles = true;
24020                 break;
24021             }
24022             
24023             // update styles
24024             if (hasStyles) { 
24025                 var st = this.tb.fields.item(0);
24026                 
24027                 st.store.removeAll();
24028                
24029                 
24030                 var cn = sel.className.split(/\s+/);
24031                 
24032                 var avs = [];
24033                 if (this.styles['*']) {
24034                     
24035                     Roo.each(this.styles['*'], function(v) {
24036                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24037                     });
24038                 }
24039                 if (this.styles[tn]) { 
24040                     Roo.each(this.styles[tn], function(v) {
24041                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24042                     });
24043                 }
24044                 
24045                 st.store.loadData(avs);
24046                 st.collapse();
24047                 st.setValue(cn);
24048             }
24049             // flag our selected Node.
24050             this.tb.selectedNode = sel;
24051            
24052            
24053             Roo.menu.MenuMgr.hideAll();
24054
24055         }
24056         
24057         if (!updateFooter) {
24058             //this.footDisp.dom.innerHTML = ''; 
24059             return;
24060         }
24061         // update the footer
24062         //
24063         var html = '';
24064         
24065         this.footerEls = ans.reverse();
24066         Roo.each(this.footerEls, function(a,i) {
24067             if (!a) { return; }
24068             html += html.length ? ' &gt; '  :  '';
24069             
24070             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24071             
24072         });
24073        
24074         // 
24075         var sz = this.footDisp.up('td').getSize();
24076         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24077         this.footDisp.dom.style.marginLeft = '5px';
24078         
24079         this.footDisp.dom.style.overflow = 'hidden';
24080         
24081         this.footDisp.dom.innerHTML = html;
24082             
24083         //this.editorsyncValue();
24084     },
24085      
24086     
24087    
24088        
24089     // private
24090     onDestroy : function(){
24091         if(this.rendered){
24092             
24093             this.tb.items.each(function(item){
24094                 if(item.menu){
24095                     item.menu.removeAll();
24096                     if(item.menu.el){
24097                         item.menu.el.destroy();
24098                     }
24099                 }
24100                 item.destroy();
24101             });
24102              
24103         }
24104     },
24105     onFirstFocus: function() {
24106         // need to do this for all the toolbars..
24107         this.tb.items.each(function(item){
24108            item.enable();
24109         });
24110     },
24111     buildToolbar: function(tlist, nm)
24112     {
24113         var editor = this.editor;
24114         var editorcore = this.editorcore;
24115          // create a new element.
24116         var wdiv = editor.wrap.createChild({
24117                 tag: 'div'
24118             }, editor.wrap.dom.firstChild.nextSibling, true);
24119         
24120        
24121         var tb = new Roo.Toolbar(wdiv);
24122         // add the name..
24123         
24124         tb.add(nm+ ":&nbsp;");
24125         
24126         var styles = [];
24127         for(var i in this.styles) {
24128             styles.push(i);
24129         }
24130         
24131         // styles...
24132         if (styles && styles.length) {
24133             
24134             // this needs a multi-select checkbox...
24135             tb.addField( new Roo.form.ComboBox({
24136                 store: new Roo.data.SimpleStore({
24137                     id : 'val',
24138                     fields: ['val', 'selected'],
24139                     data : [] 
24140                 }),
24141                 name : '-roo-edit-className',
24142                 attrname : 'className',
24143                 displayField: 'val',
24144                 typeAhead: false,
24145                 mode: 'local',
24146                 editable : false,
24147                 triggerAction: 'all',
24148                 emptyText:'Select Style',
24149                 selectOnFocus:true,
24150                 width: 130,
24151                 listeners : {
24152                     'select': function(c, r, i) {
24153                         // initial support only for on class per el..
24154                         tb.selectedNode.className =  r ? r.get('val') : '';
24155                         editorcore.syncValue();
24156                     }
24157                 }
24158     
24159             }));
24160         }
24161         
24162         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24163         var tbops = tbc.options;
24164         
24165         for (var i in tlist) {
24166             
24167             var item = tlist[i];
24168             tb.add(item.title + ":&nbsp;");
24169             
24170             
24171             //optname == used so you can configure the options available..
24172             var opts = item.opts ? item.opts : false;
24173             if (item.optname) {
24174                 opts = tbops[item.optname];
24175            
24176             }
24177             
24178             if (opts) {
24179                 // opts == pulldown..
24180                 tb.addField( new Roo.form.ComboBox({
24181                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24182                         id : 'val',
24183                         fields: ['val', 'display'],
24184                         data : opts  
24185                     }),
24186                     name : '-roo-edit-' + i,
24187                     attrname : i,
24188                     stylename : item.style ? item.style : false,
24189                     displayField: item.displayField ? item.displayField : 'val',
24190                     valueField :  'val',
24191                     typeAhead: false,
24192                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24193                     editable : false,
24194                     triggerAction: 'all',
24195                     emptyText:'Select',
24196                     selectOnFocus:true,
24197                     width: item.width ? item.width  : 130,
24198                     listeners : {
24199                         'select': function(c, r, i) {
24200                             if (c.stylename) {
24201                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24202                                 return;
24203                             }
24204                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24205                         }
24206                     }
24207
24208                 }));
24209                 continue;
24210                     
24211                  
24212                 
24213                 tb.addField( new Roo.form.TextField({
24214                     name: i,
24215                     width: 100,
24216                     //allowBlank:false,
24217                     value: ''
24218                 }));
24219                 continue;
24220             }
24221             tb.addField( new Roo.form.TextField({
24222                 name: '-roo-edit-' + i,
24223                 attrname : i,
24224                 
24225                 width: item.width,
24226                 //allowBlank:true,
24227                 value: '',
24228                 listeners: {
24229                     'change' : function(f, nv, ov) {
24230                         tb.selectedNode.setAttribute(f.attrname, nv);
24231                         editorcore.syncValue();
24232                     }
24233                 }
24234             }));
24235              
24236         }
24237         
24238         var _this = this;
24239         
24240         if(nm == 'BODY'){
24241             tb.addSeparator();
24242         
24243             tb.addButton( {
24244                 text: 'Stylesheets',
24245
24246                 listeners : {
24247                     click : function ()
24248                     {
24249                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24250                     }
24251                 }
24252             });
24253         }
24254         
24255         tb.addFill();
24256         tb.addButton( {
24257             text: 'Remove Tag',
24258     
24259             listeners : {
24260                 click : function ()
24261                 {
24262                     // remove
24263                     // undo does not work.
24264                      
24265                     var sn = tb.selectedNode;
24266                     
24267                     var pn = sn.parentNode;
24268                     
24269                     var stn =  sn.childNodes[0];
24270                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24271                     while (sn.childNodes.length) {
24272                         var node = sn.childNodes[0];
24273                         sn.removeChild(node);
24274                         //Roo.log(node);
24275                         pn.insertBefore(node, sn);
24276                         
24277                     }
24278                     pn.removeChild(sn);
24279                     var range = editorcore.createRange();
24280         
24281                     range.setStart(stn,0);
24282                     range.setEnd(en,0); //????
24283                     //range.selectNode(sel);
24284                     
24285                     
24286                     var selection = editorcore.getSelection();
24287                     selection.removeAllRanges();
24288                     selection.addRange(range);
24289                     
24290                     
24291                     
24292                     //_this.updateToolbar(null, null, pn);
24293                     _this.updateToolbar(null, null, null);
24294                     _this.footDisp.dom.innerHTML = ''; 
24295                 }
24296             }
24297             
24298                     
24299                 
24300             
24301         });
24302         
24303         
24304         tb.el.on('click', function(e){
24305             e.preventDefault(); // what does this do?
24306         });
24307         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24308         tb.el.hide();
24309         tb.name = nm;
24310         // dont need to disable them... as they will get hidden
24311         return tb;
24312          
24313         
24314     },
24315     buildFooter : function()
24316     {
24317         
24318         var fel = this.editor.wrap.createChild();
24319         this.footer = new Roo.Toolbar(fel);
24320         // toolbar has scrolly on left / right?
24321         var footDisp= new Roo.Toolbar.Fill();
24322         var _t = this;
24323         this.footer.add(
24324             {
24325                 text : '&lt;',
24326                 xtype: 'Button',
24327                 handler : function() {
24328                     _t.footDisp.scrollTo('left',0,true)
24329                 }
24330             }
24331         );
24332         this.footer.add( footDisp );
24333         this.footer.add( 
24334             {
24335                 text : '&gt;',
24336                 xtype: 'Button',
24337                 handler : function() {
24338                     // no animation..
24339                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24340                 }
24341             }
24342         );
24343         var fel = Roo.get(footDisp.el);
24344         fel.addClass('x-editor-context');
24345         this.footDispWrap = fel; 
24346         this.footDispWrap.overflow  = 'hidden';
24347         
24348         this.footDisp = fel.createChild();
24349         this.footDispWrap.on('click', this.onContextClick, this)
24350         
24351         
24352     },
24353     onContextClick : function (ev,dom)
24354     {
24355         ev.preventDefault();
24356         var  cn = dom.className;
24357         //Roo.log(cn);
24358         if (!cn.match(/x-ed-loc-/)) {
24359             return;
24360         }
24361         var n = cn.split('-').pop();
24362         var ans = this.footerEls;
24363         var sel = ans[n];
24364         
24365          // pick
24366         var range = this.editorcore.createRange();
24367         
24368         range.selectNodeContents(sel);
24369         //range.selectNode(sel);
24370         
24371         
24372         var selection = this.editorcore.getSelection();
24373         selection.removeAllRanges();
24374         selection.addRange(range);
24375         
24376         
24377         
24378         this.updateToolbar(null, null, sel);
24379         
24380         
24381     }
24382     
24383     
24384     
24385     
24386     
24387 });
24388
24389
24390
24391
24392
24393 /*
24394  * Based on:
24395  * Ext JS Library 1.1.1
24396  * Copyright(c) 2006-2007, Ext JS, LLC.
24397  *
24398  * Originally Released Under LGPL - original licence link has changed is not relivant.
24399  *
24400  * Fork - LGPL
24401  * <script type="text/javascript">
24402  */
24403  
24404 /**
24405  * @class Roo.form.BasicForm
24406  * @extends Roo.util.Observable
24407  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24408  * @constructor
24409  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24410  * @param {Object} config Configuration options
24411  */
24412 Roo.form.BasicForm = function(el, config){
24413     this.allItems = [];
24414     this.childForms = [];
24415     Roo.apply(this, config);
24416     /*
24417      * The Roo.form.Field items in this form.
24418      * @type MixedCollection
24419      */
24420      
24421      
24422     this.items = new Roo.util.MixedCollection(false, function(o){
24423         return o.id || (o.id = Roo.id());
24424     });
24425     this.addEvents({
24426         /**
24427          * @event beforeaction
24428          * Fires before any action is performed. Return false to cancel the action.
24429          * @param {Form} this
24430          * @param {Action} action The action to be performed
24431          */
24432         beforeaction: true,
24433         /**
24434          * @event actionfailed
24435          * Fires when an action fails.
24436          * @param {Form} this
24437          * @param {Action} action The action that failed
24438          */
24439         actionfailed : true,
24440         /**
24441          * @event actioncomplete
24442          * Fires when an action is completed.
24443          * @param {Form} this
24444          * @param {Action} action The action that completed
24445          */
24446         actioncomplete : true
24447     });
24448     if(el){
24449         this.initEl(el);
24450     }
24451     Roo.form.BasicForm.superclass.constructor.call(this);
24452     
24453     Roo.form.BasicForm.popover.apply();
24454 };
24455
24456 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24457     /**
24458      * @cfg {String} method
24459      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24460      */
24461     /**
24462      * @cfg {DataReader} reader
24463      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24464      * This is optional as there is built-in support for processing JSON.
24465      */
24466     /**
24467      * @cfg {DataReader} errorReader
24468      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24469      * This is completely optional as there is built-in support for processing JSON.
24470      */
24471     /**
24472      * @cfg {String} url
24473      * The URL to use for form actions if one isn't supplied in the action options.
24474      */
24475     /**
24476      * @cfg {Boolean} fileUpload
24477      * Set to true if this form is a file upload.
24478      */
24479      
24480     /**
24481      * @cfg {Object} baseParams
24482      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24483      */
24484      /**
24485      
24486     /**
24487      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24488      */
24489     timeout: 30,
24490
24491     // private
24492     activeAction : null,
24493
24494     /**
24495      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24496      * or setValues() data instead of when the form was first created.
24497      */
24498     trackResetOnLoad : false,
24499     
24500     
24501     /**
24502      * childForms - used for multi-tab forms
24503      * @type {Array}
24504      */
24505     childForms : false,
24506     
24507     /**
24508      * allItems - full list of fields.
24509      * @type {Array}
24510      */
24511     allItems : false,
24512     
24513     /**
24514      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24515      * element by passing it or its id or mask the form itself by passing in true.
24516      * @type Mixed
24517      */
24518     waitMsgTarget : false,
24519     
24520     /**
24521      * @type Boolean
24522      */
24523     disableMask : false,
24524     
24525     /**
24526      * @cfg {Boolean} errorMask (true|false) default false
24527      */
24528     errorMask : false,
24529     
24530     /**
24531      * @cfg {Number} maskOffset Default 100
24532      */
24533     maskOffset : 100,
24534
24535     // private
24536     initEl : function(el){
24537         this.el = Roo.get(el);
24538         this.id = this.el.id || Roo.id();
24539         this.el.on('submit', this.onSubmit, this);
24540         this.el.addClass('x-form');
24541     },
24542
24543     // private
24544     onSubmit : function(e){
24545         e.stopEvent();
24546     },
24547
24548     /**
24549      * Returns true if client-side validation on the form is successful.
24550      * @return Boolean
24551      */
24552     isValid : function(){
24553         var valid = true;
24554         var target = false;
24555         this.items.each(function(f){
24556             if(f.validate()){
24557                 return;
24558             }
24559             
24560             valid = false;
24561                 
24562             if(!target && f.el.isVisible(true)){
24563                 target = f;
24564             }
24565         });
24566         
24567         if(this.errorMask && !valid){
24568             Roo.form.BasicForm.popover.mask(this, target);
24569         }
24570         
24571         return valid;
24572     },
24573
24574     /**
24575      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24576      * @return Boolean
24577      */
24578     isDirty : function(){
24579         var dirty = false;
24580         this.items.each(function(f){
24581            if(f.isDirty()){
24582                dirty = true;
24583                return false;
24584            }
24585         });
24586         return dirty;
24587     },
24588     
24589     /**
24590      * Returns true if any fields in this form have changed since their original load. (New version)
24591      * @return Boolean
24592      */
24593     
24594     hasChanged : function()
24595     {
24596         var dirty = false;
24597         this.items.each(function(f){
24598            if(f.hasChanged()){
24599                dirty = true;
24600                return false;
24601            }
24602         });
24603         return dirty;
24604         
24605     },
24606     /**
24607      * Resets all hasChanged to 'false' -
24608      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24609      * So hasChanged storage is only to be used for this purpose
24610      * @return Boolean
24611      */
24612     resetHasChanged : function()
24613     {
24614         this.items.each(function(f){
24615            f.resetHasChanged();
24616         });
24617         
24618     },
24619     
24620     
24621     /**
24622      * Performs a predefined action (submit or load) or custom actions you define on this form.
24623      * @param {String} actionName The name of the action type
24624      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24625      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24626      * accept other config options):
24627      * <pre>
24628 Property          Type             Description
24629 ----------------  ---------------  ----------------------------------------------------------------------------------
24630 url               String           The url for the action (defaults to the form's url)
24631 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24632 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24633 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24634                                    validate the form on the client (defaults to false)
24635      * </pre>
24636      * @return {BasicForm} this
24637      */
24638     doAction : function(action, options){
24639         if(typeof action == 'string'){
24640             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24641         }
24642         if(this.fireEvent('beforeaction', this, action) !== false){
24643             this.beforeAction(action);
24644             action.run.defer(100, action);
24645         }
24646         return this;
24647     },
24648
24649     /**
24650      * Shortcut to do a submit action.
24651      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24652      * @return {BasicForm} this
24653      */
24654     submit : function(options){
24655         this.doAction('submit', options);
24656         return this;
24657     },
24658
24659     /**
24660      * Shortcut to do a load action.
24661      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24662      * @return {BasicForm} this
24663      */
24664     load : function(options){
24665         this.doAction('load', options);
24666         return this;
24667     },
24668
24669     /**
24670      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24671      * @param {Record} record The record to edit
24672      * @return {BasicForm} this
24673      */
24674     updateRecord : function(record){
24675         record.beginEdit();
24676         var fs = record.fields;
24677         fs.each(function(f){
24678             var field = this.findField(f.name);
24679             if(field){
24680                 record.set(f.name, field.getValue());
24681             }
24682         }, this);
24683         record.endEdit();
24684         return this;
24685     },
24686
24687     /**
24688      * Loads an Roo.data.Record into this form.
24689      * @param {Record} record The record to load
24690      * @return {BasicForm} this
24691      */
24692     loadRecord : function(record){
24693         this.setValues(record.data);
24694         return this;
24695     },
24696
24697     // private
24698     beforeAction : function(action){
24699         var o = action.options;
24700         
24701         if(!this.disableMask) {
24702             if(this.waitMsgTarget === true){
24703                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24704             }else if(this.waitMsgTarget){
24705                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24706                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24707             }else {
24708                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24709             }
24710         }
24711         
24712          
24713     },
24714
24715     // private
24716     afterAction : function(action, success){
24717         this.activeAction = null;
24718         var o = action.options;
24719         
24720         if(!this.disableMask) {
24721             if(this.waitMsgTarget === true){
24722                 this.el.unmask();
24723             }else if(this.waitMsgTarget){
24724                 this.waitMsgTarget.unmask();
24725             }else{
24726                 Roo.MessageBox.updateProgress(1);
24727                 Roo.MessageBox.hide();
24728             }
24729         }
24730         
24731         if(success){
24732             if(o.reset){
24733                 this.reset();
24734             }
24735             Roo.callback(o.success, o.scope, [this, action]);
24736             this.fireEvent('actioncomplete', this, action);
24737             
24738         }else{
24739             
24740             // failure condition..
24741             // we have a scenario where updates need confirming.
24742             // eg. if a locking scenario exists..
24743             // we look for { errors : { needs_confirm : true }} in the response.
24744             if (
24745                 (typeof(action.result) != 'undefined')  &&
24746                 (typeof(action.result.errors) != 'undefined')  &&
24747                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24748            ){
24749                 var _t = this;
24750                 Roo.MessageBox.confirm(
24751                     "Change requires confirmation",
24752                     action.result.errorMsg,
24753                     function(r) {
24754                         if (r != 'yes') {
24755                             return;
24756                         }
24757                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24758                     }
24759                     
24760                 );
24761                 
24762                 
24763                 
24764                 return;
24765             }
24766             
24767             Roo.callback(o.failure, o.scope, [this, action]);
24768             // show an error message if no failed handler is set..
24769             if (!this.hasListener('actionfailed')) {
24770                 Roo.MessageBox.alert("Error",
24771                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24772                         action.result.errorMsg :
24773                         "Saving Failed, please check your entries or try again"
24774                 );
24775             }
24776             
24777             this.fireEvent('actionfailed', this, action);
24778         }
24779         
24780     },
24781
24782     /**
24783      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24784      * @param {String} id The value to search for
24785      * @return Field
24786      */
24787     findField : function(id){
24788         var field = this.items.get(id);
24789         if(!field){
24790             this.items.each(function(f){
24791                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24792                     field = f;
24793                     return false;
24794                 }
24795             });
24796         }
24797         return field || null;
24798     },
24799
24800     /**
24801      * Add a secondary form to this one, 
24802      * Used to provide tabbed forms. One form is primary, with hidden values 
24803      * which mirror the elements from the other forms.
24804      * 
24805      * @param {Roo.form.Form} form to add.
24806      * 
24807      */
24808     addForm : function(form)
24809     {
24810        
24811         if (this.childForms.indexOf(form) > -1) {
24812             // already added..
24813             return;
24814         }
24815         this.childForms.push(form);
24816         var n = '';
24817         Roo.each(form.allItems, function (fe) {
24818             
24819             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24820             if (this.findField(n)) { // already added..
24821                 return;
24822             }
24823             var add = new Roo.form.Hidden({
24824                 name : n
24825             });
24826             add.render(this.el);
24827             
24828             this.add( add );
24829         }, this);
24830         
24831     },
24832     /**
24833      * Mark fields in this form invalid in bulk.
24834      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24835      * @return {BasicForm} this
24836      */
24837     markInvalid : function(errors){
24838         if(errors instanceof Array){
24839             for(var i = 0, len = errors.length; i < len; i++){
24840                 var fieldError = errors[i];
24841                 var f = this.findField(fieldError.id);
24842                 if(f){
24843                     f.markInvalid(fieldError.msg);
24844                 }
24845             }
24846         }else{
24847             var field, id;
24848             for(id in errors){
24849                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24850                     field.markInvalid(errors[id]);
24851                 }
24852             }
24853         }
24854         Roo.each(this.childForms || [], function (f) {
24855             f.markInvalid(errors);
24856         });
24857         
24858         return this;
24859     },
24860
24861     /**
24862      * Set values for fields in this form in bulk.
24863      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24864      * @return {BasicForm} this
24865      */
24866     setValues : function(values){
24867         if(values instanceof Array){ // array of objects
24868             for(var i = 0, len = values.length; i < len; i++){
24869                 var v = values[i];
24870                 var f = this.findField(v.id);
24871                 if(f){
24872                     f.setValue(v.value);
24873                     if(this.trackResetOnLoad){
24874                         f.originalValue = f.getValue();
24875                     }
24876                 }
24877             }
24878         }else{ // object hash
24879             var field, id;
24880             for(id in values){
24881                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24882                     
24883                     if (field.setFromData && 
24884                         field.valueField && 
24885                         field.displayField &&
24886                         // combos' with local stores can 
24887                         // be queried via setValue()
24888                         // to set their value..
24889                         (field.store && !field.store.isLocal)
24890                         ) {
24891                         // it's a combo
24892                         var sd = { };
24893                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24894                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24895                         field.setFromData(sd);
24896                         
24897                     } else {
24898                         field.setValue(values[id]);
24899                     }
24900                     
24901                     
24902                     if(this.trackResetOnLoad){
24903                         field.originalValue = field.getValue();
24904                     }
24905                 }
24906             }
24907         }
24908         this.resetHasChanged();
24909         
24910         
24911         Roo.each(this.childForms || [], function (f) {
24912             f.setValues(values);
24913             f.resetHasChanged();
24914         });
24915                 
24916         return this;
24917     },
24918  
24919     /**
24920      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24921      * they are returned as an array.
24922      * @param {Boolean} asString
24923      * @return {Object}
24924      */
24925     getValues : function(asString){
24926         if (this.childForms) {
24927             // copy values from the child forms
24928             Roo.each(this.childForms, function (f) {
24929                 this.setValues(f.getValues());
24930             }, this);
24931         }
24932         
24933         // use formdata
24934         if (typeof(FormData) != 'undefined' && asString !== true) {
24935             var fd = (new FormData(this.el.dom)).entries();
24936             var ret = {};
24937             var ent = fd.next();
24938             while (!ent.done) {
24939                 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
24940                 ent = fd.next();
24941             };
24942             return ret;
24943         }
24944         
24945         
24946         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24947         if(asString === true){
24948             return fs;
24949         }
24950         return Roo.urlDecode(fs);
24951     },
24952     
24953     /**
24954      * Returns the fields in this form as an object with key/value pairs. 
24955      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24956      * @return {Object}
24957      */
24958     getFieldValues : function(with_hidden)
24959     {
24960         if (this.childForms) {
24961             // copy values from the child forms
24962             // should this call getFieldValues - probably not as we do not currently copy
24963             // hidden fields when we generate..
24964             Roo.each(this.childForms, function (f) {
24965                 this.setValues(f.getValues());
24966             }, this);
24967         }
24968         
24969         var ret = {};
24970         this.items.each(function(f){
24971             if (!f.getName()) {
24972                 return;
24973             }
24974             var v = f.getValue();
24975             if (f.inputType =='radio') {
24976                 if (typeof(ret[f.getName()]) == 'undefined') {
24977                     ret[f.getName()] = ''; // empty..
24978                 }
24979                 
24980                 if (!f.el.dom.checked) {
24981                     return;
24982                     
24983                 }
24984                 v = f.el.dom.value;
24985                 
24986             }
24987             
24988             // not sure if this supported any more..
24989             if ((typeof(v) == 'object') && f.getRawValue) {
24990                 v = f.getRawValue() ; // dates..
24991             }
24992             // combo boxes where name != hiddenName...
24993             if (f.name != f.getName()) {
24994                 ret[f.name] = f.getRawValue();
24995             }
24996             ret[f.getName()] = v;
24997         });
24998         
24999         return ret;
25000     },
25001
25002     /**
25003      * Clears all invalid messages in this form.
25004      * @return {BasicForm} this
25005      */
25006     clearInvalid : function(){
25007         this.items.each(function(f){
25008            f.clearInvalid();
25009         });
25010         
25011         Roo.each(this.childForms || [], function (f) {
25012             f.clearInvalid();
25013         });
25014         
25015         
25016         return this;
25017     },
25018
25019     /**
25020      * Resets this form.
25021      * @return {BasicForm} this
25022      */
25023     reset : function(){
25024         this.items.each(function(f){
25025             f.reset();
25026         });
25027         
25028         Roo.each(this.childForms || [], function (f) {
25029             f.reset();
25030         });
25031         this.resetHasChanged();
25032         
25033         return this;
25034     },
25035
25036     /**
25037      * Add Roo.form components to this form.
25038      * @param {Field} field1
25039      * @param {Field} field2 (optional)
25040      * @param {Field} etc (optional)
25041      * @return {BasicForm} this
25042      */
25043     add : function(){
25044         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25045         return this;
25046     },
25047
25048
25049     /**
25050      * Removes a field from the items collection (does NOT remove its markup).
25051      * @param {Field} field
25052      * @return {BasicForm} this
25053      */
25054     remove : function(field){
25055         this.items.remove(field);
25056         return this;
25057     },
25058
25059     /**
25060      * Looks at the fields in this form, checks them for an id attribute,
25061      * and calls applyTo on the existing dom element with that id.
25062      * @return {BasicForm} this
25063      */
25064     render : function(){
25065         this.items.each(function(f){
25066             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25067                 f.applyTo(f.id);
25068             }
25069         });
25070         return this;
25071     },
25072
25073     /**
25074      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25075      * @param {Object} values
25076      * @return {BasicForm} this
25077      */
25078     applyToFields : function(o){
25079         this.items.each(function(f){
25080            Roo.apply(f, o);
25081         });
25082         return this;
25083     },
25084
25085     /**
25086      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25087      * @param {Object} values
25088      * @return {BasicForm} this
25089      */
25090     applyIfToFields : function(o){
25091         this.items.each(function(f){
25092            Roo.applyIf(f, o);
25093         });
25094         return this;
25095     }
25096 });
25097
25098 // back compat
25099 Roo.BasicForm = Roo.form.BasicForm;
25100
25101 Roo.apply(Roo.form.BasicForm, {
25102     
25103     popover : {
25104         
25105         padding : 5,
25106         
25107         isApplied : false,
25108         
25109         isMasked : false,
25110         
25111         form : false,
25112         
25113         target : false,
25114         
25115         intervalID : false,
25116         
25117         maskEl : false,
25118         
25119         apply : function()
25120         {
25121             if(this.isApplied){
25122                 return;
25123             }
25124             
25125             this.maskEl = {
25126                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25127                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25128                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25129                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25130             };
25131             
25132             this.maskEl.top.enableDisplayMode("block");
25133             this.maskEl.left.enableDisplayMode("block");
25134             this.maskEl.bottom.enableDisplayMode("block");
25135             this.maskEl.right.enableDisplayMode("block");
25136             
25137             Roo.get(document.body).on('click', function(){
25138                 this.unmask();
25139             }, this);
25140             
25141             Roo.get(document.body).on('touchstart', function(){
25142                 this.unmask();
25143             }, this);
25144             
25145             this.isApplied = true
25146         },
25147         
25148         mask : function(form, target)
25149         {
25150             this.form = form;
25151             
25152             this.target = target;
25153             
25154             if(!this.form.errorMask || !target.el){
25155                 return;
25156             }
25157             
25158             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25159             
25160             var ot = this.target.el.calcOffsetsTo(scrollable);
25161             
25162             var scrollTo = ot[1] - this.form.maskOffset;
25163             
25164             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25165             
25166             scrollable.scrollTo('top', scrollTo);
25167             
25168             var el = this.target.wrap || this.target.el;
25169             
25170             var box = el.getBox();
25171             
25172             this.maskEl.top.setStyle('position', 'absolute');
25173             this.maskEl.top.setStyle('z-index', 10000);
25174             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25175             this.maskEl.top.setLeft(0);
25176             this.maskEl.top.setTop(0);
25177             this.maskEl.top.show();
25178             
25179             this.maskEl.left.setStyle('position', 'absolute');
25180             this.maskEl.left.setStyle('z-index', 10000);
25181             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25182             this.maskEl.left.setLeft(0);
25183             this.maskEl.left.setTop(box.y - this.padding);
25184             this.maskEl.left.show();
25185
25186             this.maskEl.bottom.setStyle('position', 'absolute');
25187             this.maskEl.bottom.setStyle('z-index', 10000);
25188             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25189             this.maskEl.bottom.setLeft(0);
25190             this.maskEl.bottom.setTop(box.bottom + this.padding);
25191             this.maskEl.bottom.show();
25192
25193             this.maskEl.right.setStyle('position', 'absolute');
25194             this.maskEl.right.setStyle('z-index', 10000);
25195             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25196             this.maskEl.right.setLeft(box.right + this.padding);
25197             this.maskEl.right.setTop(box.y - this.padding);
25198             this.maskEl.right.show();
25199
25200             this.intervalID = window.setInterval(function() {
25201                 Roo.form.BasicForm.popover.unmask();
25202             }, 10000);
25203
25204             window.onwheel = function(){ return false;};
25205             
25206             (function(){ this.isMasked = true; }).defer(500, this);
25207             
25208         },
25209         
25210         unmask : function()
25211         {
25212             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25213                 return;
25214             }
25215             
25216             this.maskEl.top.setStyle('position', 'absolute');
25217             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25218             this.maskEl.top.hide();
25219
25220             this.maskEl.left.setStyle('position', 'absolute');
25221             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25222             this.maskEl.left.hide();
25223
25224             this.maskEl.bottom.setStyle('position', 'absolute');
25225             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25226             this.maskEl.bottom.hide();
25227
25228             this.maskEl.right.setStyle('position', 'absolute');
25229             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25230             this.maskEl.right.hide();
25231             
25232             window.onwheel = function(){ return true;};
25233             
25234             if(this.intervalID){
25235                 window.clearInterval(this.intervalID);
25236                 this.intervalID = false;
25237             }
25238             
25239             this.isMasked = false;
25240             
25241         }
25242         
25243     }
25244     
25245 });/*
25246  * Based on:
25247  * Ext JS Library 1.1.1
25248  * Copyright(c) 2006-2007, Ext JS, LLC.
25249  *
25250  * Originally Released Under LGPL - original licence link has changed is not relivant.
25251  *
25252  * Fork - LGPL
25253  * <script type="text/javascript">
25254  */
25255
25256 /**
25257  * @class Roo.form.Form
25258  * @extends Roo.form.BasicForm
25259  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25260  * @constructor
25261  * @param {Object} config Configuration options
25262  */
25263 Roo.form.Form = function(config){
25264     var xitems =  [];
25265     if (config.items) {
25266         xitems = config.items;
25267         delete config.items;
25268     }
25269    
25270     
25271     Roo.form.Form.superclass.constructor.call(this, null, config);
25272     this.url = this.url || this.action;
25273     if(!this.root){
25274         this.root = new Roo.form.Layout(Roo.applyIf({
25275             id: Roo.id()
25276         }, config));
25277     }
25278     this.active = this.root;
25279     /**
25280      * Array of all the buttons that have been added to this form via {@link addButton}
25281      * @type Array
25282      */
25283     this.buttons = [];
25284     this.allItems = [];
25285     this.addEvents({
25286         /**
25287          * @event clientvalidation
25288          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25289          * @param {Form} this
25290          * @param {Boolean} valid true if the form has passed client-side validation
25291          */
25292         clientvalidation: true,
25293         /**
25294          * @event rendered
25295          * Fires when the form is rendered
25296          * @param {Roo.form.Form} form
25297          */
25298         rendered : true
25299     });
25300     
25301     if (this.progressUrl) {
25302             // push a hidden field onto the list of fields..
25303             this.addxtype( {
25304                     xns: Roo.form, 
25305                     xtype : 'Hidden', 
25306                     name : 'UPLOAD_IDENTIFIER' 
25307             });
25308         }
25309         
25310     
25311     Roo.each(xitems, this.addxtype, this);
25312     
25313 };
25314
25315 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25316     /**
25317      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25318      */
25319     /**
25320      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25321      */
25322     /**
25323      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25324      */
25325     buttonAlign:'center',
25326
25327     /**
25328      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25329      */
25330     minButtonWidth:75,
25331
25332     /**
25333      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25334      * This property cascades to child containers if not set.
25335      */
25336     labelAlign:'left',
25337
25338     /**
25339      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25340      * fires a looping event with that state. This is required to bind buttons to the valid
25341      * state using the config value formBind:true on the button.
25342      */
25343     monitorValid : false,
25344
25345     /**
25346      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25347      */
25348     monitorPoll : 200,
25349     
25350     /**
25351      * @cfg {String} progressUrl - Url to return progress data 
25352      */
25353     
25354     progressUrl : false,
25355     /**
25356      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25357      * sending a formdata with extra parameters - eg uploaded elements.
25358      */
25359     
25360     formData : false,
25361     
25362     /**
25363      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25364      * fields are added and the column is closed. If no fields are passed the column remains open
25365      * until end() is called.
25366      * @param {Object} config The config to pass to the column
25367      * @param {Field} field1 (optional)
25368      * @param {Field} field2 (optional)
25369      * @param {Field} etc (optional)
25370      * @return Column The column container object
25371      */
25372     column : function(c){
25373         var col = new Roo.form.Column(c);
25374         this.start(col);
25375         if(arguments.length > 1){ // duplicate code required because of Opera
25376             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25377             this.end();
25378         }
25379         return col;
25380     },
25381
25382     /**
25383      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25384      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25385      * until end() is called.
25386      * @param {Object} config The config to pass to the fieldset
25387      * @param {Field} field1 (optional)
25388      * @param {Field} field2 (optional)
25389      * @param {Field} etc (optional)
25390      * @return FieldSet The fieldset container object
25391      */
25392     fieldset : function(c){
25393         var fs = new Roo.form.FieldSet(c);
25394         this.start(fs);
25395         if(arguments.length > 1){ // duplicate code required because of Opera
25396             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25397             this.end();
25398         }
25399         return fs;
25400     },
25401
25402     /**
25403      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25404      * fields are added and the container is closed. If no fields are passed the container remains open
25405      * until end() is called.
25406      * @param {Object} config The config to pass to the Layout
25407      * @param {Field} field1 (optional)
25408      * @param {Field} field2 (optional)
25409      * @param {Field} etc (optional)
25410      * @return Layout The container object
25411      */
25412     container : function(c){
25413         var l = new Roo.form.Layout(c);
25414         this.start(l);
25415         if(arguments.length > 1){ // duplicate code required because of Opera
25416             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25417             this.end();
25418         }
25419         return l;
25420     },
25421
25422     /**
25423      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25424      * @param {Object} container A Roo.form.Layout or subclass of Layout
25425      * @return {Form} this
25426      */
25427     start : function(c){
25428         // cascade label info
25429         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25430         this.active.stack.push(c);
25431         c.ownerCt = this.active;
25432         this.active = c;
25433         return this;
25434     },
25435
25436     /**
25437      * Closes the current open container
25438      * @return {Form} this
25439      */
25440     end : function(){
25441         if(this.active == this.root){
25442             return this;
25443         }
25444         this.active = this.active.ownerCt;
25445         return this;
25446     },
25447
25448     /**
25449      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25450      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25451      * as the label of the field.
25452      * @param {Field} field1
25453      * @param {Field} field2 (optional)
25454      * @param {Field} etc. (optional)
25455      * @return {Form} this
25456      */
25457     add : function(){
25458         this.active.stack.push.apply(this.active.stack, arguments);
25459         this.allItems.push.apply(this.allItems,arguments);
25460         var r = [];
25461         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25462             if(a[i].isFormField){
25463                 r.push(a[i]);
25464             }
25465         }
25466         if(r.length > 0){
25467             Roo.form.Form.superclass.add.apply(this, r);
25468         }
25469         return this;
25470     },
25471     
25472
25473     
25474     
25475     
25476      /**
25477      * Find any element that has been added to a form, using it's ID or name
25478      * This can include framesets, columns etc. along with regular fields..
25479      * @param {String} id - id or name to find.
25480      
25481      * @return {Element} e - or false if nothing found.
25482      */
25483     findbyId : function(id)
25484     {
25485         var ret = false;
25486         if (!id) {
25487             return ret;
25488         }
25489         Roo.each(this.allItems, function(f){
25490             if (f.id == id || f.name == id ){
25491                 ret = f;
25492                 return false;
25493             }
25494         });
25495         return ret;
25496     },
25497
25498     
25499     
25500     /**
25501      * Render this form into the passed container. This should only be called once!
25502      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25503      * @return {Form} this
25504      */
25505     render : function(ct)
25506     {
25507         
25508         
25509         
25510         ct = Roo.get(ct);
25511         var o = this.autoCreate || {
25512             tag: 'form',
25513             method : this.method || 'POST',
25514             id : this.id || Roo.id()
25515         };
25516         this.initEl(ct.createChild(o));
25517
25518         this.root.render(this.el);
25519         
25520        
25521              
25522         this.items.each(function(f){
25523             f.render('x-form-el-'+f.id);
25524         });
25525
25526         if(this.buttons.length > 0){
25527             // tables are required to maintain order and for correct IE layout
25528             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25529                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25530                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25531             }}, null, true);
25532             var tr = tb.getElementsByTagName('tr')[0];
25533             for(var i = 0, len = this.buttons.length; i < len; i++) {
25534                 var b = this.buttons[i];
25535                 var td = document.createElement('td');
25536                 td.className = 'x-form-btn-td';
25537                 b.render(tr.appendChild(td));
25538             }
25539         }
25540         if(this.monitorValid){ // initialize after render
25541             this.startMonitoring();
25542         }
25543         this.fireEvent('rendered', this);
25544         return this;
25545     },
25546
25547     /**
25548      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25549      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25550      * object or a valid Roo.DomHelper element config
25551      * @param {Function} handler The function called when the button is clicked
25552      * @param {Object} scope (optional) The scope of the handler function
25553      * @return {Roo.Button}
25554      */
25555     addButton : function(config, handler, scope){
25556         var bc = {
25557             handler: handler,
25558             scope: scope,
25559             minWidth: this.minButtonWidth,
25560             hideParent:true
25561         };
25562         if(typeof config == "string"){
25563             bc.text = config;
25564         }else{
25565             Roo.apply(bc, config);
25566         }
25567         var btn = new Roo.Button(null, bc);
25568         this.buttons.push(btn);
25569         return btn;
25570     },
25571
25572      /**
25573      * Adds a series of form elements (using the xtype property as the factory method.
25574      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25575      * @param {Object} config 
25576      */
25577     
25578     addxtype : function()
25579     {
25580         var ar = Array.prototype.slice.call(arguments, 0);
25581         var ret = false;
25582         for(var i = 0; i < ar.length; i++) {
25583             if (!ar[i]) {
25584                 continue; // skip -- if this happends something invalid got sent, we 
25585                 // should ignore it, as basically that interface element will not show up
25586                 // and that should be pretty obvious!!
25587             }
25588             
25589             if (Roo.form[ar[i].xtype]) {
25590                 ar[i].form = this;
25591                 var fe = Roo.factory(ar[i], Roo.form);
25592                 if (!ret) {
25593                     ret = fe;
25594                 }
25595                 fe.form = this;
25596                 if (fe.store) {
25597                     fe.store.form = this;
25598                 }
25599                 if (fe.isLayout) {  
25600                          
25601                     this.start(fe);
25602                     this.allItems.push(fe);
25603                     if (fe.items && fe.addxtype) {
25604                         fe.addxtype.apply(fe, fe.items);
25605                         delete fe.items;
25606                     }
25607                      this.end();
25608                     continue;
25609                 }
25610                 
25611                 
25612                  
25613                 this.add(fe);
25614               //  console.log('adding ' + ar[i].xtype);
25615             }
25616             if (ar[i].xtype == 'Button') {  
25617                 //console.log('adding button');
25618                 //console.log(ar[i]);
25619                 this.addButton(ar[i]);
25620                 this.allItems.push(fe);
25621                 continue;
25622             }
25623             
25624             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25625                 alert('end is not supported on xtype any more, use items');
25626             //    this.end();
25627             //    //console.log('adding end');
25628             }
25629             
25630         }
25631         return ret;
25632     },
25633     
25634     /**
25635      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25636      * option "monitorValid"
25637      */
25638     startMonitoring : function(){
25639         if(!this.bound){
25640             this.bound = true;
25641             Roo.TaskMgr.start({
25642                 run : this.bindHandler,
25643                 interval : this.monitorPoll || 200,
25644                 scope: this
25645             });
25646         }
25647     },
25648
25649     /**
25650      * Stops monitoring of the valid state of this form
25651      */
25652     stopMonitoring : function(){
25653         this.bound = false;
25654     },
25655
25656     // private
25657     bindHandler : function(){
25658         if(!this.bound){
25659             return false; // stops binding
25660         }
25661         var valid = true;
25662         this.items.each(function(f){
25663             if(!f.isValid(true)){
25664                 valid = false;
25665                 return false;
25666             }
25667         });
25668         for(var i = 0, len = this.buttons.length; i < len; i++){
25669             var btn = this.buttons[i];
25670             if(btn.formBind === true && btn.disabled === valid){
25671                 btn.setDisabled(!valid);
25672             }
25673         }
25674         this.fireEvent('clientvalidation', this, valid);
25675     }
25676     
25677     
25678     
25679     
25680     
25681     
25682     
25683     
25684 });
25685
25686
25687 // back compat
25688 Roo.Form = Roo.form.Form;
25689 /*
25690  * Based on:
25691  * Ext JS Library 1.1.1
25692  * Copyright(c) 2006-2007, Ext JS, LLC.
25693  *
25694  * Originally Released Under LGPL - original licence link has changed is not relivant.
25695  *
25696  * Fork - LGPL
25697  * <script type="text/javascript">
25698  */
25699
25700 // as we use this in bootstrap.
25701 Roo.namespace('Roo.form');
25702  /**
25703  * @class Roo.form.Action
25704  * Internal Class used to handle form actions
25705  * @constructor
25706  * @param {Roo.form.BasicForm} el The form element or its id
25707  * @param {Object} config Configuration options
25708  */
25709
25710  
25711  
25712 // define the action interface
25713 Roo.form.Action = function(form, options){
25714     this.form = form;
25715     this.options = options || {};
25716 };
25717 /**
25718  * Client Validation Failed
25719  * @const 
25720  */
25721 Roo.form.Action.CLIENT_INVALID = 'client';
25722 /**
25723  * Server Validation Failed
25724  * @const 
25725  */
25726 Roo.form.Action.SERVER_INVALID = 'server';
25727  /**
25728  * Connect to Server Failed
25729  * @const 
25730  */
25731 Roo.form.Action.CONNECT_FAILURE = 'connect';
25732 /**
25733  * Reading Data from Server Failed
25734  * @const 
25735  */
25736 Roo.form.Action.LOAD_FAILURE = 'load';
25737
25738 Roo.form.Action.prototype = {
25739     type : 'default',
25740     failureType : undefined,
25741     response : undefined,
25742     result : undefined,
25743
25744     // interface method
25745     run : function(options){
25746
25747     },
25748
25749     // interface method
25750     success : function(response){
25751
25752     },
25753
25754     // interface method
25755     handleResponse : function(response){
25756
25757     },
25758
25759     // default connection failure
25760     failure : function(response){
25761         
25762         this.response = response;
25763         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25764         this.form.afterAction(this, false);
25765     },
25766
25767     processResponse : function(response){
25768         this.response = response;
25769         if(!response.responseText){
25770             return true;
25771         }
25772         this.result = this.handleResponse(response);
25773         return this.result;
25774     },
25775
25776     // utility functions used internally
25777     getUrl : function(appendParams){
25778         var url = this.options.url || this.form.url || this.form.el.dom.action;
25779         if(appendParams){
25780             var p = this.getParams();
25781             if(p){
25782                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25783             }
25784         }
25785         return url;
25786     },
25787
25788     getMethod : function(){
25789         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25790     },
25791
25792     getParams : function(){
25793         var bp = this.form.baseParams;
25794         var p = this.options.params;
25795         if(p){
25796             if(typeof p == "object"){
25797                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25798             }else if(typeof p == 'string' && bp){
25799                 p += '&' + Roo.urlEncode(bp);
25800             }
25801         }else if(bp){
25802             p = Roo.urlEncode(bp);
25803         }
25804         return p;
25805     },
25806
25807     createCallback : function(){
25808         return {
25809             success: this.success,
25810             failure: this.failure,
25811             scope: this,
25812             timeout: (this.form.timeout*1000),
25813             upload: this.form.fileUpload ? this.success : undefined
25814         };
25815     }
25816 };
25817
25818 Roo.form.Action.Submit = function(form, options){
25819     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25820 };
25821
25822 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25823     type : 'submit',
25824
25825     haveProgress : false,
25826     uploadComplete : false,
25827     
25828     // uploadProgress indicator.
25829     uploadProgress : function()
25830     {
25831         if (!this.form.progressUrl) {
25832             return;
25833         }
25834         
25835         if (!this.haveProgress) {
25836             Roo.MessageBox.progress("Uploading", "Uploading");
25837         }
25838         if (this.uploadComplete) {
25839            Roo.MessageBox.hide();
25840            return;
25841         }
25842         
25843         this.haveProgress = true;
25844    
25845         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25846         
25847         var c = new Roo.data.Connection();
25848         c.request({
25849             url : this.form.progressUrl,
25850             params: {
25851                 id : uid
25852             },
25853             method: 'GET',
25854             success : function(req){
25855                //console.log(data);
25856                 var rdata = false;
25857                 var edata;
25858                 try  {
25859                    rdata = Roo.decode(req.responseText)
25860                 } catch (e) {
25861                     Roo.log("Invalid data from server..");
25862                     Roo.log(edata);
25863                     return;
25864                 }
25865                 if (!rdata || !rdata.success) {
25866                     Roo.log(rdata);
25867                     Roo.MessageBox.alert(Roo.encode(rdata));
25868                     return;
25869                 }
25870                 var data = rdata.data;
25871                 
25872                 if (this.uploadComplete) {
25873                    Roo.MessageBox.hide();
25874                    return;
25875                 }
25876                    
25877                 if (data){
25878                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25879                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25880                     );
25881                 }
25882                 this.uploadProgress.defer(2000,this);
25883             },
25884        
25885             failure: function(data) {
25886                 Roo.log('progress url failed ');
25887                 Roo.log(data);
25888             },
25889             scope : this
25890         });
25891            
25892     },
25893     
25894     
25895     run : function()
25896     {
25897         // run get Values on the form, so it syncs any secondary forms.
25898         this.form.getValues();
25899         
25900         var o = this.options;
25901         var method = this.getMethod();
25902         var isPost = method == 'POST';
25903         if(o.clientValidation === false || this.form.isValid()){
25904             
25905             if (this.form.progressUrl) {
25906                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25907                     (new Date() * 1) + '' + Math.random());
25908                     
25909             } 
25910             
25911             
25912             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25913                 form:this.form.el.dom,
25914                 url:this.getUrl(!isPost),
25915                 method: method,
25916                 params:isPost ? this.getParams() : null,
25917                 isUpload: this.form.fileUpload,
25918                 formData : this.form.formData
25919             }));
25920             
25921             this.uploadProgress();
25922
25923         }else if (o.clientValidation !== false){ // client validation failed
25924             this.failureType = Roo.form.Action.CLIENT_INVALID;
25925             this.form.afterAction(this, false);
25926         }
25927     },
25928
25929     success : function(response)
25930     {
25931         this.uploadComplete= true;
25932         if (this.haveProgress) {
25933             Roo.MessageBox.hide();
25934         }
25935         
25936         
25937         var result = this.processResponse(response);
25938         if(result === true || result.success){
25939             this.form.afterAction(this, true);
25940             return;
25941         }
25942         if(result.errors){
25943             this.form.markInvalid(result.errors);
25944             this.failureType = Roo.form.Action.SERVER_INVALID;
25945         }
25946         this.form.afterAction(this, false);
25947     },
25948     failure : function(response)
25949     {
25950         this.uploadComplete= true;
25951         if (this.haveProgress) {
25952             Roo.MessageBox.hide();
25953         }
25954         
25955         this.response = response;
25956         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25957         this.form.afterAction(this, false);
25958     },
25959     
25960     handleResponse : function(response){
25961         if(this.form.errorReader){
25962             var rs = this.form.errorReader.read(response);
25963             var errors = [];
25964             if(rs.records){
25965                 for(var i = 0, len = rs.records.length; i < len; i++) {
25966                     var r = rs.records[i];
25967                     errors[i] = r.data;
25968                 }
25969             }
25970             if(errors.length < 1){
25971                 errors = null;
25972             }
25973             return {
25974                 success : rs.success,
25975                 errors : errors
25976             };
25977         }
25978         var ret = false;
25979         try {
25980             ret = Roo.decode(response.responseText);
25981         } catch (e) {
25982             ret = {
25983                 success: false,
25984                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25985                 errors : []
25986             };
25987         }
25988         return ret;
25989         
25990     }
25991 });
25992
25993
25994 Roo.form.Action.Load = function(form, options){
25995     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25996     this.reader = this.form.reader;
25997 };
25998
25999 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26000     type : 'load',
26001
26002     run : function(){
26003         
26004         Roo.Ajax.request(Roo.apply(
26005                 this.createCallback(), {
26006                     method:this.getMethod(),
26007                     url:this.getUrl(false),
26008                     params:this.getParams()
26009         }));
26010     },
26011
26012     success : function(response){
26013         
26014         var result = this.processResponse(response);
26015         if(result === true || !result.success || !result.data){
26016             this.failureType = Roo.form.Action.LOAD_FAILURE;
26017             this.form.afterAction(this, false);
26018             return;
26019         }
26020         this.form.clearInvalid();
26021         this.form.setValues(result.data);
26022         this.form.afterAction(this, true);
26023     },
26024
26025     handleResponse : function(response){
26026         if(this.form.reader){
26027             var rs = this.form.reader.read(response);
26028             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26029             return {
26030                 success : rs.success,
26031                 data : data
26032             };
26033         }
26034         return Roo.decode(response.responseText);
26035     }
26036 });
26037
26038 Roo.form.Action.ACTION_TYPES = {
26039     'load' : Roo.form.Action.Load,
26040     'submit' : Roo.form.Action.Submit
26041 };/*
26042  * Based on:
26043  * Ext JS Library 1.1.1
26044  * Copyright(c) 2006-2007, Ext JS, LLC.
26045  *
26046  * Originally Released Under LGPL - original licence link has changed is not relivant.
26047  *
26048  * Fork - LGPL
26049  * <script type="text/javascript">
26050  */
26051  
26052 /**
26053  * @class Roo.form.Layout
26054  * @extends Roo.Component
26055  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26056  * @constructor
26057  * @param {Object} config Configuration options
26058  */
26059 Roo.form.Layout = function(config){
26060     var xitems = [];
26061     if (config.items) {
26062         xitems = config.items;
26063         delete config.items;
26064     }
26065     Roo.form.Layout.superclass.constructor.call(this, config);
26066     this.stack = [];
26067     Roo.each(xitems, this.addxtype, this);
26068      
26069 };
26070
26071 Roo.extend(Roo.form.Layout, Roo.Component, {
26072     /**
26073      * @cfg {String/Object} autoCreate
26074      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26075      */
26076     /**
26077      * @cfg {String/Object/Function} style
26078      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26079      * a function which returns such a specification.
26080      */
26081     /**
26082      * @cfg {String} labelAlign
26083      * Valid values are "left," "top" and "right" (defaults to "left")
26084      */
26085     /**
26086      * @cfg {Number} labelWidth
26087      * Fixed width in pixels of all field labels (defaults to undefined)
26088      */
26089     /**
26090      * @cfg {Boolean} clear
26091      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26092      */
26093     clear : true,
26094     /**
26095      * @cfg {String} labelSeparator
26096      * The separator to use after field labels (defaults to ':')
26097      */
26098     labelSeparator : ':',
26099     /**
26100      * @cfg {Boolean} hideLabels
26101      * True to suppress the display of field labels in this layout (defaults to false)
26102      */
26103     hideLabels : false,
26104
26105     // private
26106     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26107     
26108     isLayout : true,
26109     
26110     // private
26111     onRender : function(ct, position){
26112         if(this.el){ // from markup
26113             this.el = Roo.get(this.el);
26114         }else {  // generate
26115             var cfg = this.getAutoCreate();
26116             this.el = ct.createChild(cfg, position);
26117         }
26118         if(this.style){
26119             this.el.applyStyles(this.style);
26120         }
26121         if(this.labelAlign){
26122             this.el.addClass('x-form-label-'+this.labelAlign);
26123         }
26124         if(this.hideLabels){
26125             this.labelStyle = "display:none";
26126             this.elementStyle = "padding-left:0;";
26127         }else{
26128             if(typeof this.labelWidth == 'number'){
26129                 this.labelStyle = "width:"+this.labelWidth+"px;";
26130                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26131             }
26132             if(this.labelAlign == 'top'){
26133                 this.labelStyle = "width:auto;";
26134                 this.elementStyle = "padding-left:0;";
26135             }
26136         }
26137         var stack = this.stack;
26138         var slen = stack.length;
26139         if(slen > 0){
26140             if(!this.fieldTpl){
26141                 var t = new Roo.Template(
26142                     '<div class="x-form-item {5}">',
26143                         '<label for="{0}" style="{2}">{1}{4}</label>',
26144                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26145                         '</div>',
26146                     '</div><div class="x-form-clear-left"></div>'
26147                 );
26148                 t.disableFormats = true;
26149                 t.compile();
26150                 Roo.form.Layout.prototype.fieldTpl = t;
26151             }
26152             for(var i = 0; i < slen; i++) {
26153                 if(stack[i].isFormField){
26154                     this.renderField(stack[i]);
26155                 }else{
26156                     this.renderComponent(stack[i]);
26157                 }
26158             }
26159         }
26160         if(this.clear){
26161             this.el.createChild({cls:'x-form-clear'});
26162         }
26163     },
26164
26165     // private
26166     renderField : function(f){
26167         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26168                f.id, //0
26169                f.fieldLabel, //1
26170                f.labelStyle||this.labelStyle||'', //2
26171                this.elementStyle||'', //3
26172                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26173                f.itemCls||this.itemCls||''  //5
26174        ], true).getPrevSibling());
26175     },
26176
26177     // private
26178     renderComponent : function(c){
26179         c.render(c.isLayout ? this.el : this.el.createChild());    
26180     },
26181     /**
26182      * Adds a object form elements (using the xtype property as the factory method.)
26183      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26184      * @param {Object} config 
26185      */
26186     addxtype : function(o)
26187     {
26188         // create the lement.
26189         o.form = this.form;
26190         var fe = Roo.factory(o, Roo.form);
26191         this.form.allItems.push(fe);
26192         this.stack.push(fe);
26193         
26194         if (fe.isFormField) {
26195             this.form.items.add(fe);
26196         }
26197          
26198         return fe;
26199     }
26200 });
26201
26202 /**
26203  * @class Roo.form.Column
26204  * @extends Roo.form.Layout
26205  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26206  * @constructor
26207  * @param {Object} config Configuration options
26208  */
26209 Roo.form.Column = function(config){
26210     Roo.form.Column.superclass.constructor.call(this, config);
26211 };
26212
26213 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26214     /**
26215      * @cfg {Number/String} width
26216      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26217      */
26218     /**
26219      * @cfg {String/Object} autoCreate
26220      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26221      */
26222
26223     // private
26224     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26225
26226     // private
26227     onRender : function(ct, position){
26228         Roo.form.Column.superclass.onRender.call(this, ct, position);
26229         if(this.width){
26230             this.el.setWidth(this.width);
26231         }
26232     }
26233 });
26234
26235
26236 /**
26237  * @class Roo.form.Row
26238  * @extends Roo.form.Layout
26239  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26240  * @constructor
26241  * @param {Object} config Configuration options
26242  */
26243
26244  
26245 Roo.form.Row = function(config){
26246     Roo.form.Row.superclass.constructor.call(this, config);
26247 };
26248  
26249 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26250       /**
26251      * @cfg {Number/String} width
26252      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26253      */
26254     /**
26255      * @cfg {Number/String} height
26256      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26257      */
26258     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26259     
26260     padWidth : 20,
26261     // private
26262     onRender : function(ct, position){
26263         //console.log('row render');
26264         if(!this.rowTpl){
26265             var t = new Roo.Template(
26266                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26267                     '<label for="{0}" style="{2}">{1}{4}</label>',
26268                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26269                     '</div>',
26270                 '</div>'
26271             );
26272             t.disableFormats = true;
26273             t.compile();
26274             Roo.form.Layout.prototype.rowTpl = t;
26275         }
26276         this.fieldTpl = this.rowTpl;
26277         
26278         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26279         var labelWidth = 100;
26280         
26281         if ((this.labelAlign != 'top')) {
26282             if (typeof this.labelWidth == 'number') {
26283                 labelWidth = this.labelWidth
26284             }
26285             this.padWidth =  20 + labelWidth;
26286             
26287         }
26288         
26289         Roo.form.Column.superclass.onRender.call(this, ct, position);
26290         if(this.width){
26291             this.el.setWidth(this.width);
26292         }
26293         if(this.height){
26294             this.el.setHeight(this.height);
26295         }
26296     },
26297     
26298     // private
26299     renderField : function(f){
26300         f.fieldEl = this.fieldTpl.append(this.el, [
26301                f.id, f.fieldLabel,
26302                f.labelStyle||this.labelStyle||'',
26303                this.elementStyle||'',
26304                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26305                f.itemCls||this.itemCls||'',
26306                f.width ? f.width + this.padWidth : 160 + this.padWidth
26307        ],true);
26308     }
26309 });
26310  
26311
26312 /**
26313  * @class Roo.form.FieldSet
26314  * @extends Roo.form.Layout
26315  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26316  * @constructor
26317  * @param {Object} config Configuration options
26318  */
26319 Roo.form.FieldSet = function(config){
26320     Roo.form.FieldSet.superclass.constructor.call(this, config);
26321 };
26322
26323 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26324     /**
26325      * @cfg {String} legend
26326      * The text to display as the legend for the FieldSet (defaults to '')
26327      */
26328     /**
26329      * @cfg {String/Object} autoCreate
26330      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26331      */
26332
26333     // private
26334     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26335
26336     // private
26337     onRender : function(ct, position){
26338         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26339         if(this.legend){
26340             this.setLegend(this.legend);
26341         }
26342     },
26343
26344     // private
26345     setLegend : function(text){
26346         if(this.rendered){
26347             this.el.child('legend').update(text);
26348         }
26349     }
26350 });/*
26351  * Based on:
26352  * Ext JS Library 1.1.1
26353  * Copyright(c) 2006-2007, Ext JS, LLC.
26354  *
26355  * Originally Released Under LGPL - original licence link has changed is not relivant.
26356  *
26357  * Fork - LGPL
26358  * <script type="text/javascript">
26359  */
26360 /**
26361  * @class Roo.form.VTypes
26362  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26363  * @singleton
26364  */
26365 Roo.form.VTypes = function(){
26366     // closure these in so they are only created once.
26367     var alpha = /^[a-zA-Z_]+$/;
26368     var alphanum = /^[a-zA-Z0-9_]+$/;
26369     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26370     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26371
26372     // All these messages and functions are configurable
26373     return {
26374         /**
26375          * The function used to validate email addresses
26376          * @param {String} value The email address
26377          */
26378         'email' : function(v){
26379             return email.test(v);
26380         },
26381         /**
26382          * The error text to display when the email validation function returns false
26383          * @type String
26384          */
26385         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26386         /**
26387          * The keystroke filter mask to be applied on email input
26388          * @type RegExp
26389          */
26390         'emailMask' : /[a-z0-9_\.\-@]/i,
26391
26392         /**
26393          * The function used to validate URLs
26394          * @param {String} value The URL
26395          */
26396         'url' : function(v){
26397             return url.test(v);
26398         },
26399         /**
26400          * The error text to display when the url validation function returns false
26401          * @type String
26402          */
26403         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26404         
26405         /**
26406          * The function used to validate alpha values
26407          * @param {String} value The value
26408          */
26409         'alpha' : function(v){
26410             return alpha.test(v);
26411         },
26412         /**
26413          * The error text to display when the alpha validation function returns false
26414          * @type String
26415          */
26416         'alphaText' : 'This field should only contain letters and _',
26417         /**
26418          * The keystroke filter mask to be applied on alpha input
26419          * @type RegExp
26420          */
26421         'alphaMask' : /[a-z_]/i,
26422
26423         /**
26424          * The function used to validate alphanumeric values
26425          * @param {String} value The value
26426          */
26427         'alphanum' : function(v){
26428             return alphanum.test(v);
26429         },
26430         /**
26431          * The error text to display when the alphanumeric validation function returns false
26432          * @type String
26433          */
26434         'alphanumText' : 'This field should only contain letters, numbers and _',
26435         /**
26436          * The keystroke filter mask to be applied on alphanumeric input
26437          * @type RegExp
26438          */
26439         'alphanumMask' : /[a-z0-9_]/i
26440     };
26441 }();//<script type="text/javascript">
26442
26443 /**
26444  * @class Roo.form.FCKeditor
26445  * @extends Roo.form.TextArea
26446  * Wrapper around the FCKEditor http://www.fckeditor.net
26447  * @constructor
26448  * Creates a new FCKeditor
26449  * @param {Object} config Configuration options
26450  */
26451 Roo.form.FCKeditor = function(config){
26452     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26453     this.addEvents({
26454          /**
26455          * @event editorinit
26456          * Fired when the editor is initialized - you can add extra handlers here..
26457          * @param {FCKeditor} this
26458          * @param {Object} the FCK object.
26459          */
26460         editorinit : true
26461     });
26462     
26463     
26464 };
26465 Roo.form.FCKeditor.editors = { };
26466 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26467 {
26468     //defaultAutoCreate : {
26469     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26470     //},
26471     // private
26472     /**
26473      * @cfg {Object} fck options - see fck manual for details.
26474      */
26475     fckconfig : false,
26476     
26477     /**
26478      * @cfg {Object} fck toolbar set (Basic or Default)
26479      */
26480     toolbarSet : 'Basic',
26481     /**
26482      * @cfg {Object} fck BasePath
26483      */ 
26484     basePath : '/fckeditor/',
26485     
26486     
26487     frame : false,
26488     
26489     value : '',
26490     
26491    
26492     onRender : function(ct, position)
26493     {
26494         if(!this.el){
26495             this.defaultAutoCreate = {
26496                 tag: "textarea",
26497                 style:"width:300px;height:60px;",
26498                 autocomplete: "new-password"
26499             };
26500         }
26501         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26502         /*
26503         if(this.grow){
26504             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26505             if(this.preventScrollbars){
26506                 this.el.setStyle("overflow", "hidden");
26507             }
26508             this.el.setHeight(this.growMin);
26509         }
26510         */
26511         //console.log('onrender' + this.getId() );
26512         Roo.form.FCKeditor.editors[this.getId()] = this;
26513          
26514
26515         this.replaceTextarea() ;
26516         
26517     },
26518     
26519     getEditor : function() {
26520         return this.fckEditor;
26521     },
26522     /**
26523      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26524      * @param {Mixed} value The value to set
26525      */
26526     
26527     
26528     setValue : function(value)
26529     {
26530         //console.log('setValue: ' + value);
26531         
26532         if(typeof(value) == 'undefined') { // not sure why this is happending...
26533             return;
26534         }
26535         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26536         
26537         //if(!this.el || !this.getEditor()) {
26538         //    this.value = value;
26539             //this.setValue.defer(100,this,[value]);    
26540         //    return;
26541         //} 
26542         
26543         if(!this.getEditor()) {
26544             return;
26545         }
26546         
26547         this.getEditor().SetData(value);
26548         
26549         //
26550
26551     },
26552
26553     /**
26554      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26555      * @return {Mixed} value The field value
26556      */
26557     getValue : function()
26558     {
26559         
26560         if (this.frame && this.frame.dom.style.display == 'none') {
26561             return Roo.form.FCKeditor.superclass.getValue.call(this);
26562         }
26563         
26564         if(!this.el || !this.getEditor()) {
26565            
26566            // this.getValue.defer(100,this); 
26567             return this.value;
26568         }
26569        
26570         
26571         var value=this.getEditor().GetData();
26572         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26573         return Roo.form.FCKeditor.superclass.getValue.call(this);
26574         
26575
26576     },
26577
26578     /**
26579      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26580      * @return {Mixed} value The field value
26581      */
26582     getRawValue : function()
26583     {
26584         if (this.frame && this.frame.dom.style.display == 'none') {
26585             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26586         }
26587         
26588         if(!this.el || !this.getEditor()) {
26589             //this.getRawValue.defer(100,this); 
26590             return this.value;
26591             return;
26592         }
26593         
26594         
26595         
26596         var value=this.getEditor().GetData();
26597         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26598         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26599          
26600     },
26601     
26602     setSize : function(w,h) {
26603         
26604         
26605         
26606         //if (this.frame && this.frame.dom.style.display == 'none') {
26607         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26608         //    return;
26609         //}
26610         //if(!this.el || !this.getEditor()) {
26611         //    this.setSize.defer(100,this, [w,h]); 
26612         //    return;
26613         //}
26614         
26615         
26616         
26617         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26618         
26619         this.frame.dom.setAttribute('width', w);
26620         this.frame.dom.setAttribute('height', h);
26621         this.frame.setSize(w,h);
26622         
26623     },
26624     
26625     toggleSourceEdit : function(value) {
26626         
26627       
26628          
26629         this.el.dom.style.display = value ? '' : 'none';
26630         this.frame.dom.style.display = value ?  'none' : '';
26631         
26632     },
26633     
26634     
26635     focus: function(tag)
26636     {
26637         if (this.frame.dom.style.display == 'none') {
26638             return Roo.form.FCKeditor.superclass.focus.call(this);
26639         }
26640         if(!this.el || !this.getEditor()) {
26641             this.focus.defer(100,this, [tag]); 
26642             return;
26643         }
26644         
26645         
26646         
26647         
26648         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26649         this.getEditor().Focus();
26650         if (tgs.length) {
26651             if (!this.getEditor().Selection.GetSelection()) {
26652                 this.focus.defer(100,this, [tag]); 
26653                 return;
26654             }
26655             
26656             
26657             var r = this.getEditor().EditorDocument.createRange();
26658             r.setStart(tgs[0],0);
26659             r.setEnd(tgs[0],0);
26660             this.getEditor().Selection.GetSelection().removeAllRanges();
26661             this.getEditor().Selection.GetSelection().addRange(r);
26662             this.getEditor().Focus();
26663         }
26664         
26665     },
26666     
26667     
26668     
26669     replaceTextarea : function()
26670     {
26671         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26672             return ;
26673         }
26674         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26675         //{
26676             // We must check the elements firstly using the Id and then the name.
26677         var oTextarea = document.getElementById( this.getId() );
26678         
26679         var colElementsByName = document.getElementsByName( this.getId() ) ;
26680          
26681         oTextarea.style.display = 'none' ;
26682
26683         if ( oTextarea.tabIndex ) {            
26684             this.TabIndex = oTextarea.tabIndex ;
26685         }
26686         
26687         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26688         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26689         this.frame = Roo.get(this.getId() + '___Frame')
26690     },
26691     
26692     _getConfigHtml : function()
26693     {
26694         var sConfig = '' ;
26695
26696         for ( var o in this.fckconfig ) {
26697             sConfig += sConfig.length > 0  ? '&amp;' : '';
26698             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26699         }
26700
26701         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26702     },
26703     
26704     
26705     _getIFrameHtml : function()
26706     {
26707         var sFile = 'fckeditor.html' ;
26708         /* no idea what this is about..
26709         try
26710         {
26711             if ( (/fcksource=true/i).test( window.top.location.search ) )
26712                 sFile = 'fckeditor.original.html' ;
26713         }
26714         catch (e) { 
26715         */
26716
26717         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26718         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26719         
26720         
26721         var html = '<iframe id="' + this.getId() +
26722             '___Frame" src="' + sLink +
26723             '" width="' + this.width +
26724             '" height="' + this.height + '"' +
26725             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26726             ' frameborder="0" scrolling="no"></iframe>' ;
26727
26728         return html ;
26729     },
26730     
26731     _insertHtmlBefore : function( html, element )
26732     {
26733         if ( element.insertAdjacentHTML )       {
26734             // IE
26735             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26736         } else { // Gecko
26737             var oRange = document.createRange() ;
26738             oRange.setStartBefore( element ) ;
26739             var oFragment = oRange.createContextualFragment( html );
26740             element.parentNode.insertBefore( oFragment, element ) ;
26741         }
26742     }
26743     
26744     
26745   
26746     
26747     
26748     
26749     
26750
26751 });
26752
26753 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26754
26755 function FCKeditor_OnComplete(editorInstance){
26756     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26757     f.fckEditor = editorInstance;
26758     //console.log("loaded");
26759     f.fireEvent('editorinit', f, editorInstance);
26760
26761   
26762
26763  
26764
26765
26766
26767
26768
26769
26770
26771
26772
26773
26774
26775
26776
26777
26778
26779 //<script type="text/javascript">
26780 /**
26781  * @class Roo.form.GridField
26782  * @extends Roo.form.Field
26783  * Embed a grid (or editable grid into a form)
26784  * STATUS ALPHA
26785  * 
26786  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26787  * it needs 
26788  * xgrid.store = Roo.data.Store
26789  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26790  * xgrid.store.reader = Roo.data.JsonReader 
26791  * 
26792  * 
26793  * @constructor
26794  * Creates a new GridField
26795  * @param {Object} config Configuration options
26796  */
26797 Roo.form.GridField = function(config){
26798     Roo.form.GridField.superclass.constructor.call(this, config);
26799      
26800 };
26801
26802 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26803     /**
26804      * @cfg {Number} width  - used to restrict width of grid..
26805      */
26806     width : 100,
26807     /**
26808      * @cfg {Number} height - used to restrict height of grid..
26809      */
26810     height : 50,
26811      /**
26812      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26813          * 
26814          *}
26815      */
26816     xgrid : false, 
26817     /**
26818      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26819      * {tag: "input", type: "checkbox", autocomplete: "off"})
26820      */
26821    // defaultAutoCreate : { tag: 'div' },
26822     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26823     /**
26824      * @cfg {String} addTitle Text to include for adding a title.
26825      */
26826     addTitle : false,
26827     //
26828     onResize : function(){
26829         Roo.form.Field.superclass.onResize.apply(this, arguments);
26830     },
26831
26832     initEvents : function(){
26833         // Roo.form.Checkbox.superclass.initEvents.call(this);
26834         // has no events...
26835        
26836     },
26837
26838
26839     getResizeEl : function(){
26840         return this.wrap;
26841     },
26842
26843     getPositionEl : function(){
26844         return this.wrap;
26845     },
26846
26847     // private
26848     onRender : function(ct, position){
26849         
26850         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26851         var style = this.style;
26852         delete this.style;
26853         
26854         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26855         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26856         this.viewEl = this.wrap.createChild({ tag: 'div' });
26857         if (style) {
26858             this.viewEl.applyStyles(style);
26859         }
26860         if (this.width) {
26861             this.viewEl.setWidth(this.width);
26862         }
26863         if (this.height) {
26864             this.viewEl.setHeight(this.height);
26865         }
26866         //if(this.inputValue !== undefined){
26867         //this.setValue(this.value);
26868         
26869         
26870         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26871         
26872         
26873         this.grid.render();
26874         this.grid.getDataSource().on('remove', this.refreshValue, this);
26875         this.grid.getDataSource().on('update', this.refreshValue, this);
26876         this.grid.on('afteredit', this.refreshValue, this);
26877  
26878     },
26879      
26880     
26881     /**
26882      * Sets the value of the item. 
26883      * @param {String} either an object  or a string..
26884      */
26885     setValue : function(v){
26886         //this.value = v;
26887         v = v || []; // empty set..
26888         // this does not seem smart - it really only affects memoryproxy grids..
26889         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26890             var ds = this.grid.getDataSource();
26891             // assumes a json reader..
26892             var data = {}
26893             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26894             ds.loadData( data);
26895         }
26896         // clear selection so it does not get stale.
26897         if (this.grid.sm) { 
26898             this.grid.sm.clearSelections();
26899         }
26900         
26901         Roo.form.GridField.superclass.setValue.call(this, v);
26902         this.refreshValue();
26903         // should load data in the grid really....
26904     },
26905     
26906     // private
26907     refreshValue: function() {
26908          var val = [];
26909         this.grid.getDataSource().each(function(r) {
26910             val.push(r.data);
26911         });
26912         this.el.dom.value = Roo.encode(val);
26913     }
26914     
26915      
26916     
26917     
26918 });/*
26919  * Based on:
26920  * Ext JS Library 1.1.1
26921  * Copyright(c) 2006-2007, Ext JS, LLC.
26922  *
26923  * Originally Released Under LGPL - original licence link has changed is not relivant.
26924  *
26925  * Fork - LGPL
26926  * <script type="text/javascript">
26927  */
26928 /**
26929  * @class Roo.form.DisplayField
26930  * @extends Roo.form.Field
26931  * A generic Field to display non-editable data.
26932  * @cfg {Boolean} closable (true|false) default false
26933  * @constructor
26934  * Creates a new Display Field item.
26935  * @param {Object} config Configuration options
26936  */
26937 Roo.form.DisplayField = function(config){
26938     Roo.form.DisplayField.superclass.constructor.call(this, config);
26939     
26940     this.addEvents({
26941         /**
26942          * @event close
26943          * Fires after the click the close btn
26944              * @param {Roo.form.DisplayField} this
26945              */
26946         close : true
26947     });
26948 };
26949
26950 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26951     inputType:      'hidden',
26952     allowBlank:     true,
26953     readOnly:         true,
26954     
26955  
26956     /**
26957      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26958      */
26959     focusClass : undefined,
26960     /**
26961      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26962      */
26963     fieldClass: 'x-form-field',
26964     
26965      /**
26966      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26967      */
26968     valueRenderer: undefined,
26969     
26970     width: 100,
26971     /**
26972      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26973      * {tag: "input", type: "checkbox", autocomplete: "off"})
26974      */
26975      
26976  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26977  
26978     closable : false,
26979     
26980     onResize : function(){
26981         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26982         
26983     },
26984
26985     initEvents : function(){
26986         // Roo.form.Checkbox.superclass.initEvents.call(this);
26987         // has no events...
26988         
26989         if(this.closable){
26990             this.closeEl.on('click', this.onClose, this);
26991         }
26992        
26993     },
26994
26995
26996     getResizeEl : function(){
26997         return this.wrap;
26998     },
26999
27000     getPositionEl : function(){
27001         return this.wrap;
27002     },
27003
27004     // private
27005     onRender : function(ct, position){
27006         
27007         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27008         //if(this.inputValue !== undefined){
27009         this.wrap = this.el.wrap();
27010         
27011         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27012         
27013         if(this.closable){
27014             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27015         }
27016         
27017         if (this.bodyStyle) {
27018             this.viewEl.applyStyles(this.bodyStyle);
27019         }
27020         //this.viewEl.setStyle('padding', '2px');
27021         
27022         this.setValue(this.value);
27023         
27024     },
27025 /*
27026     // private
27027     initValue : Roo.emptyFn,
27028
27029   */
27030
27031         // private
27032     onClick : function(){
27033         
27034     },
27035
27036     /**
27037      * Sets the checked state of the checkbox.
27038      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27039      */
27040     setValue : function(v){
27041         this.value = v;
27042         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27043         // this might be called before we have a dom element..
27044         if (!this.viewEl) {
27045             return;
27046         }
27047         this.viewEl.dom.innerHTML = html;
27048         Roo.form.DisplayField.superclass.setValue.call(this, v);
27049
27050     },
27051     
27052     onClose : function(e)
27053     {
27054         e.preventDefault();
27055         
27056         this.fireEvent('close', this);
27057     }
27058 });/*
27059  * 
27060  * Licence- LGPL
27061  * 
27062  */
27063
27064 /**
27065  * @class Roo.form.DayPicker
27066  * @extends Roo.form.Field
27067  * A Day picker show [M] [T] [W] ....
27068  * @constructor
27069  * Creates a new Day Picker
27070  * @param {Object} config Configuration options
27071  */
27072 Roo.form.DayPicker= function(config){
27073     Roo.form.DayPicker.superclass.constructor.call(this, config);
27074      
27075 };
27076
27077 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27078     /**
27079      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27080      */
27081     focusClass : undefined,
27082     /**
27083      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27084      */
27085     fieldClass: "x-form-field",
27086    
27087     /**
27088      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27089      * {tag: "input", type: "checkbox", autocomplete: "off"})
27090      */
27091     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27092     
27093    
27094     actionMode : 'viewEl', 
27095     //
27096     // private
27097  
27098     inputType : 'hidden',
27099     
27100      
27101     inputElement: false, // real input element?
27102     basedOn: false, // ????
27103     
27104     isFormField: true, // not sure where this is needed!!!!
27105
27106     onResize : function(){
27107         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27108         if(!this.boxLabel){
27109             this.el.alignTo(this.wrap, 'c-c');
27110         }
27111     },
27112
27113     initEvents : function(){
27114         Roo.form.Checkbox.superclass.initEvents.call(this);
27115         this.el.on("click", this.onClick,  this);
27116         this.el.on("change", this.onClick,  this);
27117     },
27118
27119
27120     getResizeEl : function(){
27121         return this.wrap;
27122     },
27123
27124     getPositionEl : function(){
27125         return this.wrap;
27126     },
27127
27128     
27129     // private
27130     onRender : function(ct, position){
27131         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27132        
27133         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27134         
27135         var r1 = '<table><tr>';
27136         var r2 = '<tr class="x-form-daypick-icons">';
27137         for (var i=0; i < 7; i++) {
27138             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27139             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27140         }
27141         
27142         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27143         viewEl.select('img').on('click', this.onClick, this);
27144         this.viewEl = viewEl;   
27145         
27146         
27147         // this will not work on Chrome!!!
27148         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27149         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27150         
27151         
27152           
27153
27154     },
27155
27156     // private
27157     initValue : Roo.emptyFn,
27158
27159     /**
27160      * Returns the checked state of the checkbox.
27161      * @return {Boolean} True if checked, else false
27162      */
27163     getValue : function(){
27164         return this.el.dom.value;
27165         
27166     },
27167
27168         // private
27169     onClick : function(e){ 
27170         //this.setChecked(!this.checked);
27171         Roo.get(e.target).toggleClass('x-menu-item-checked');
27172         this.refreshValue();
27173         //if(this.el.dom.checked != this.checked){
27174         //    this.setValue(this.el.dom.checked);
27175        // }
27176     },
27177     
27178     // private
27179     refreshValue : function()
27180     {
27181         var val = '';
27182         this.viewEl.select('img',true).each(function(e,i,n)  {
27183             val += e.is(".x-menu-item-checked") ? String(n) : '';
27184         });
27185         this.setValue(val, true);
27186     },
27187
27188     /**
27189      * Sets the checked state of the checkbox.
27190      * On is always based on a string comparison between inputValue and the param.
27191      * @param {Boolean/String} value - the value to set 
27192      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27193      */
27194     setValue : function(v,suppressEvent){
27195         if (!this.el.dom) {
27196             return;
27197         }
27198         var old = this.el.dom.value ;
27199         this.el.dom.value = v;
27200         if (suppressEvent) {
27201             return ;
27202         }
27203          
27204         // update display..
27205         this.viewEl.select('img',true).each(function(e,i,n)  {
27206             
27207             var on = e.is(".x-menu-item-checked");
27208             var newv = v.indexOf(String(n)) > -1;
27209             if (on != newv) {
27210                 e.toggleClass('x-menu-item-checked');
27211             }
27212             
27213         });
27214         
27215         
27216         this.fireEvent('change', this, v, old);
27217         
27218         
27219     },
27220    
27221     // handle setting of hidden value by some other method!!?!?
27222     setFromHidden: function()
27223     {
27224         if(!this.el){
27225             return;
27226         }
27227         //console.log("SET FROM HIDDEN");
27228         //alert('setFrom hidden');
27229         this.setValue(this.el.dom.value);
27230     },
27231     
27232     onDestroy : function()
27233     {
27234         if(this.viewEl){
27235             Roo.get(this.viewEl).remove();
27236         }
27237          
27238         Roo.form.DayPicker.superclass.onDestroy.call(this);
27239     }
27240
27241 });/*
27242  * RooJS Library 1.1.1
27243  * Copyright(c) 2008-2011  Alan Knowles
27244  *
27245  * License - LGPL
27246  */
27247  
27248
27249 /**
27250  * @class Roo.form.ComboCheck
27251  * @extends Roo.form.ComboBox
27252  * A combobox for multiple select items.
27253  *
27254  * FIXME - could do with a reset button..
27255  * 
27256  * @constructor
27257  * Create a new ComboCheck
27258  * @param {Object} config Configuration options
27259  */
27260 Roo.form.ComboCheck = function(config){
27261     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27262     // should verify some data...
27263     // like
27264     // hiddenName = required..
27265     // displayField = required
27266     // valudField == required
27267     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27268     var _t = this;
27269     Roo.each(req, function(e) {
27270         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27271             throw "Roo.form.ComboCheck : missing value for: " + e;
27272         }
27273     });
27274     
27275     
27276 };
27277
27278 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27279      
27280      
27281     editable : false,
27282      
27283     selectedClass: 'x-menu-item-checked', 
27284     
27285     // private
27286     onRender : function(ct, position){
27287         var _t = this;
27288         
27289         
27290         
27291         if(!this.tpl){
27292             var cls = 'x-combo-list';
27293
27294             
27295             this.tpl =  new Roo.Template({
27296                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27297                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27298                    '<span>{' + this.displayField + '}</span>' +
27299                     '</div>' 
27300                 
27301             });
27302         }
27303  
27304         
27305         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27306         this.view.singleSelect = false;
27307         this.view.multiSelect = true;
27308         this.view.toggleSelect = true;
27309         this.pageTb.add(new Roo.Toolbar.Fill(), {
27310             
27311             text: 'Done',
27312             handler: function()
27313             {
27314                 _t.collapse();
27315             }
27316         });
27317     },
27318     
27319     onViewOver : function(e, t){
27320         // do nothing...
27321         return;
27322         
27323     },
27324     
27325     onViewClick : function(doFocus,index){
27326         return;
27327         
27328     },
27329     select: function () {
27330         //Roo.log("SELECT CALLED");
27331     },
27332      
27333     selectByValue : function(xv, scrollIntoView){
27334         var ar = this.getValueArray();
27335         var sels = [];
27336         
27337         Roo.each(ar, function(v) {
27338             if(v === undefined || v === null){
27339                 return;
27340             }
27341             var r = this.findRecord(this.valueField, v);
27342             if(r){
27343                 sels.push(this.store.indexOf(r))
27344                 
27345             }
27346         },this);
27347         this.view.select(sels);
27348         return false;
27349     },
27350     
27351     
27352     
27353     onSelect : function(record, index){
27354        // Roo.log("onselect Called");
27355        // this is only called by the clear button now..
27356         this.view.clearSelections();
27357         this.setValue('[]');
27358         if (this.value != this.valueBefore) {
27359             this.fireEvent('change', this, this.value, this.valueBefore);
27360             this.valueBefore = this.value;
27361         }
27362     },
27363     getValueArray : function()
27364     {
27365         var ar = [] ;
27366         
27367         try {
27368             //Roo.log(this.value);
27369             if (typeof(this.value) == 'undefined') {
27370                 return [];
27371             }
27372             var ar = Roo.decode(this.value);
27373             return  ar instanceof Array ? ar : []; //?? valid?
27374             
27375         } catch(e) {
27376             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27377             return [];
27378         }
27379          
27380     },
27381     expand : function ()
27382     {
27383         
27384         Roo.form.ComboCheck.superclass.expand.call(this);
27385         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27386         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27387         
27388
27389     },
27390     
27391     collapse : function(){
27392         Roo.form.ComboCheck.superclass.collapse.call(this);
27393         var sl = this.view.getSelectedIndexes();
27394         var st = this.store;
27395         var nv = [];
27396         var tv = [];
27397         var r;
27398         Roo.each(sl, function(i) {
27399             r = st.getAt(i);
27400             nv.push(r.get(this.valueField));
27401         },this);
27402         this.setValue(Roo.encode(nv));
27403         if (this.value != this.valueBefore) {
27404
27405             this.fireEvent('change', this, this.value, this.valueBefore);
27406             this.valueBefore = this.value;
27407         }
27408         
27409     },
27410     
27411     setValue : function(v){
27412         // Roo.log(v);
27413         this.value = v;
27414         
27415         var vals = this.getValueArray();
27416         var tv = [];
27417         Roo.each(vals, function(k) {
27418             var r = this.findRecord(this.valueField, k);
27419             if(r){
27420                 tv.push(r.data[this.displayField]);
27421             }else if(this.valueNotFoundText !== undefined){
27422                 tv.push( this.valueNotFoundText );
27423             }
27424         },this);
27425        // Roo.log(tv);
27426         
27427         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27428         this.hiddenField.value = v;
27429         this.value = v;
27430     }
27431     
27432 });/*
27433  * Based on:
27434  * Ext JS Library 1.1.1
27435  * Copyright(c) 2006-2007, Ext JS, LLC.
27436  *
27437  * Originally Released Under LGPL - original licence link has changed is not relivant.
27438  *
27439  * Fork - LGPL
27440  * <script type="text/javascript">
27441  */
27442  
27443 /**
27444  * @class Roo.form.Signature
27445  * @extends Roo.form.Field
27446  * Signature field.  
27447  * @constructor
27448  * 
27449  * @param {Object} config Configuration options
27450  */
27451
27452 Roo.form.Signature = function(config){
27453     Roo.form.Signature.superclass.constructor.call(this, config);
27454     
27455     this.addEvents({// not in used??
27456          /**
27457          * @event confirm
27458          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27459              * @param {Roo.form.Signature} combo This combo box
27460              */
27461         'confirm' : true,
27462         /**
27463          * @event reset
27464          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27465              * @param {Roo.form.ComboBox} combo This combo box
27466              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27467              */
27468         'reset' : true
27469     });
27470 };
27471
27472 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27473     /**
27474      * @cfg {Object} labels Label to use when rendering a form.
27475      * defaults to 
27476      * labels : { 
27477      *      clear : "Clear",
27478      *      confirm : "Confirm"
27479      *  }
27480      */
27481     labels : { 
27482         clear : "Clear",
27483         confirm : "Confirm"
27484     },
27485     /**
27486      * @cfg {Number} width The signature panel width (defaults to 300)
27487      */
27488     width: 300,
27489     /**
27490      * @cfg {Number} height The signature panel height (defaults to 100)
27491      */
27492     height : 100,
27493     /**
27494      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27495      */
27496     allowBlank : false,
27497     
27498     //private
27499     // {Object} signPanel The signature SVG panel element (defaults to {})
27500     signPanel : {},
27501     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27502     isMouseDown : false,
27503     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27504     isConfirmed : false,
27505     // {String} signatureTmp SVG mapping string (defaults to empty string)
27506     signatureTmp : '',
27507     
27508     
27509     defaultAutoCreate : { // modified by initCompnoent..
27510         tag: "input",
27511         type:"hidden"
27512     },
27513
27514     // private
27515     onRender : function(ct, position){
27516         
27517         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27518         
27519         this.wrap = this.el.wrap({
27520             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27521         });
27522         
27523         this.createToolbar(this);
27524         this.signPanel = this.wrap.createChild({
27525                 tag: 'div',
27526                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27527             }, this.el
27528         );
27529             
27530         this.svgID = Roo.id();
27531         this.svgEl = this.signPanel.createChild({
27532               xmlns : 'http://www.w3.org/2000/svg',
27533               tag : 'svg',
27534               id : this.svgID + "-svg",
27535               width: this.width,
27536               height: this.height,
27537               viewBox: '0 0 '+this.width+' '+this.height,
27538               cn : [
27539                 {
27540                     tag: "rect",
27541                     id: this.svgID + "-svg-r",
27542                     width: this.width,
27543                     height: this.height,
27544                     fill: "#ffa"
27545                 },
27546                 {
27547                     tag: "line",
27548                     id: this.svgID + "-svg-l",
27549                     x1: "0", // start
27550                     y1: (this.height*0.8), // start set the line in 80% of height
27551                     x2: this.width, // end
27552                     y2: (this.height*0.8), // end set the line in 80% of height
27553                     'stroke': "#666",
27554                     'stroke-width': "1",
27555                     'stroke-dasharray': "3",
27556                     'shape-rendering': "crispEdges",
27557                     'pointer-events': "none"
27558                 },
27559                 {
27560                     tag: "path",
27561                     id: this.svgID + "-svg-p",
27562                     'stroke': "navy",
27563                     'stroke-width': "3",
27564                     'fill': "none",
27565                     'pointer-events': 'none'
27566                 }
27567               ]
27568         });
27569         this.createSVG();
27570         this.svgBox = this.svgEl.dom.getScreenCTM();
27571     },
27572     createSVG : function(){ 
27573         var svg = this.signPanel;
27574         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27575         var t = this;
27576
27577         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27578         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27579         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27580         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27581         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27582         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27583         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27584         
27585     },
27586     isTouchEvent : function(e){
27587         return e.type.match(/^touch/);
27588     },
27589     getCoords : function (e) {
27590         var pt    = this.svgEl.dom.createSVGPoint();
27591         pt.x = e.clientX; 
27592         pt.y = e.clientY;
27593         if (this.isTouchEvent(e)) {
27594             pt.x =  e.targetTouches[0].clientX;
27595             pt.y = e.targetTouches[0].clientY;
27596         }
27597         var a = this.svgEl.dom.getScreenCTM();
27598         var b = a.inverse();
27599         var mx = pt.matrixTransform(b);
27600         return mx.x + ',' + mx.y;
27601     },
27602     //mouse event headler 
27603     down : function (e) {
27604         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27605         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27606         
27607         this.isMouseDown = true;
27608         
27609         e.preventDefault();
27610     },
27611     move : function (e) {
27612         if (this.isMouseDown) {
27613             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27614             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27615         }
27616         
27617         e.preventDefault();
27618     },
27619     up : function (e) {
27620         this.isMouseDown = false;
27621         var sp = this.signatureTmp.split(' ');
27622         
27623         if(sp.length > 1){
27624             if(!sp[sp.length-2].match(/^L/)){
27625                 sp.pop();
27626                 sp.pop();
27627                 sp.push("");
27628                 this.signatureTmp = sp.join(" ");
27629             }
27630         }
27631         if(this.getValue() != this.signatureTmp){
27632             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27633             this.isConfirmed = false;
27634         }
27635         e.preventDefault();
27636     },
27637     
27638     /**
27639      * Protected method that will not generally be called directly. It
27640      * is called when the editor creates its toolbar. Override this method if you need to
27641      * add custom toolbar buttons.
27642      * @param {HtmlEditor} editor
27643      */
27644     createToolbar : function(editor){
27645          function btn(id, toggle, handler){
27646             var xid = fid + '-'+ id ;
27647             return {
27648                 id : xid,
27649                 cmd : id,
27650                 cls : 'x-btn-icon x-edit-'+id,
27651                 enableToggle:toggle !== false,
27652                 scope: editor, // was editor...
27653                 handler:handler||editor.relayBtnCmd,
27654                 clickEvent:'mousedown',
27655                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27656                 tabIndex:-1
27657             };
27658         }
27659         
27660         
27661         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27662         this.tb = tb;
27663         this.tb.add(
27664            {
27665                 cls : ' x-signature-btn x-signature-'+id,
27666                 scope: editor, // was editor...
27667                 handler: this.reset,
27668                 clickEvent:'mousedown',
27669                 text: this.labels.clear
27670             },
27671             {
27672                  xtype : 'Fill',
27673                  xns: Roo.Toolbar
27674             }, 
27675             {
27676                 cls : '  x-signature-btn x-signature-'+id,
27677                 scope: editor, // was editor...
27678                 handler: this.confirmHandler,
27679                 clickEvent:'mousedown',
27680                 text: this.labels.confirm
27681             }
27682         );
27683     
27684     },
27685     //public
27686     /**
27687      * when user is clicked confirm then show this image.....
27688      * 
27689      * @return {String} Image Data URI
27690      */
27691     getImageDataURI : function(){
27692         var svg = this.svgEl.dom.parentNode.innerHTML;
27693         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27694         return src; 
27695     },
27696     /**
27697      * 
27698      * @return {Boolean} this.isConfirmed
27699      */
27700     getConfirmed : function(){
27701         return this.isConfirmed;
27702     },
27703     /**
27704      * 
27705      * @return {Number} this.width
27706      */
27707     getWidth : function(){
27708         return this.width;
27709     },
27710     /**
27711      * 
27712      * @return {Number} this.height
27713      */
27714     getHeight : function(){
27715         return this.height;
27716     },
27717     // private
27718     getSignature : function(){
27719         return this.signatureTmp;
27720     },
27721     // private
27722     reset : function(){
27723         this.signatureTmp = '';
27724         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27725         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27726         this.isConfirmed = false;
27727         Roo.form.Signature.superclass.reset.call(this);
27728     },
27729     setSignature : function(s){
27730         this.signatureTmp = s;
27731         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27732         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27733         this.setValue(s);
27734         this.isConfirmed = false;
27735         Roo.form.Signature.superclass.reset.call(this);
27736     }, 
27737     test : function(){
27738 //        Roo.log(this.signPanel.dom.contentWindow.up())
27739     },
27740     //private
27741     setConfirmed : function(){
27742         
27743         
27744         
27745 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27746     },
27747     // private
27748     confirmHandler : function(){
27749         if(!this.getSignature()){
27750             return;
27751         }
27752         
27753         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27754         this.setValue(this.getSignature());
27755         this.isConfirmed = true;
27756         
27757         this.fireEvent('confirm', this);
27758     },
27759     // private
27760     // Subclasses should provide the validation implementation by overriding this
27761     validateValue : function(value){
27762         if(this.allowBlank){
27763             return true;
27764         }
27765         
27766         if(this.isConfirmed){
27767             return true;
27768         }
27769         return false;
27770     }
27771 });/*
27772  * Based on:
27773  * Ext JS Library 1.1.1
27774  * Copyright(c) 2006-2007, Ext JS, LLC.
27775  *
27776  * Originally Released Under LGPL - original licence link has changed is not relivant.
27777  *
27778  * Fork - LGPL
27779  * <script type="text/javascript">
27780  */
27781  
27782
27783 /**
27784  * @class Roo.form.ComboBox
27785  * @extends Roo.form.TriggerField
27786  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27787  * @constructor
27788  * Create a new ComboBox.
27789  * @param {Object} config Configuration options
27790  */
27791 Roo.form.Select = function(config){
27792     Roo.form.Select.superclass.constructor.call(this, config);
27793      
27794 };
27795
27796 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27797     /**
27798      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27799      */
27800     /**
27801      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27802      * rendering into an Roo.Editor, defaults to false)
27803      */
27804     /**
27805      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27806      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27807      */
27808     /**
27809      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27810      */
27811     /**
27812      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27813      * the dropdown list (defaults to undefined, with no header element)
27814      */
27815
27816      /**
27817      * @cfg {String/Roo.Template} tpl The template to use to render the output
27818      */
27819      
27820     // private
27821     defaultAutoCreate : {tag: "select"  },
27822     /**
27823      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27824      */
27825     listWidth: undefined,
27826     /**
27827      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27828      * mode = 'remote' or 'text' if mode = 'local')
27829      */
27830     displayField: undefined,
27831     /**
27832      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27833      * mode = 'remote' or 'value' if mode = 'local'). 
27834      * Note: use of a valueField requires the user make a selection
27835      * in order for a value to be mapped.
27836      */
27837     valueField: undefined,
27838     
27839     
27840     /**
27841      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27842      * field's data value (defaults to the underlying DOM element's name)
27843      */
27844     hiddenName: undefined,
27845     /**
27846      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27847      */
27848     listClass: '',
27849     /**
27850      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27851      */
27852     selectedClass: 'x-combo-selected',
27853     /**
27854      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27855      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27856      * which displays a downward arrow icon).
27857      */
27858     triggerClass : 'x-form-arrow-trigger',
27859     /**
27860      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27861      */
27862     shadow:'sides',
27863     /**
27864      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27865      * anchor positions (defaults to 'tl-bl')
27866      */
27867     listAlign: 'tl-bl?',
27868     /**
27869      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27870      */
27871     maxHeight: 300,
27872     /**
27873      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27874      * query specified by the allQuery config option (defaults to 'query')
27875      */
27876     triggerAction: 'query',
27877     /**
27878      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27879      * (defaults to 4, does not apply if editable = false)
27880      */
27881     minChars : 4,
27882     /**
27883      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27884      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27885      */
27886     typeAhead: false,
27887     /**
27888      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27889      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27890      */
27891     queryDelay: 500,
27892     /**
27893      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27894      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27895      */
27896     pageSize: 0,
27897     /**
27898      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27899      * when editable = true (defaults to false)
27900      */
27901     selectOnFocus:false,
27902     /**
27903      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27904      */
27905     queryParam: 'query',
27906     /**
27907      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27908      * when mode = 'remote' (defaults to 'Loading...')
27909      */
27910     loadingText: 'Loading...',
27911     /**
27912      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27913      */
27914     resizable: false,
27915     /**
27916      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27917      */
27918     handleHeight : 8,
27919     /**
27920      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27921      * traditional select (defaults to true)
27922      */
27923     editable: true,
27924     /**
27925      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27926      */
27927     allQuery: '',
27928     /**
27929      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27930      */
27931     mode: 'remote',
27932     /**
27933      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27934      * listWidth has a higher value)
27935      */
27936     minListWidth : 70,
27937     /**
27938      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27939      * allow the user to set arbitrary text into the field (defaults to false)
27940      */
27941     forceSelection:false,
27942     /**
27943      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27944      * if typeAhead = true (defaults to 250)
27945      */
27946     typeAheadDelay : 250,
27947     /**
27948      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27949      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27950      */
27951     valueNotFoundText : undefined,
27952     
27953     /**
27954      * @cfg {String} defaultValue The value displayed after loading the store.
27955      */
27956     defaultValue: '',
27957     
27958     /**
27959      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27960      */
27961     blockFocus : false,
27962     
27963     /**
27964      * @cfg {Boolean} disableClear Disable showing of clear button.
27965      */
27966     disableClear : false,
27967     /**
27968      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
27969      */
27970     alwaysQuery : false,
27971     
27972     //private
27973     addicon : false,
27974     editicon: false,
27975     
27976     // element that contains real text value.. (when hidden is used..)
27977      
27978     // private
27979     onRender : function(ct, position){
27980         Roo.form.Field.prototype.onRender.call(this, ct, position);
27981         
27982         if(this.store){
27983             this.store.on('beforeload', this.onBeforeLoad, this);
27984             this.store.on('load', this.onLoad, this);
27985             this.store.on('loadexception', this.onLoadException, this);
27986             this.store.load({});
27987         }
27988         
27989         
27990         
27991     },
27992
27993     // private
27994     initEvents : function(){
27995         //Roo.form.ComboBox.superclass.initEvents.call(this);
27996  
27997     },
27998
27999     onDestroy : function(){
28000        
28001         if(this.store){
28002             this.store.un('beforeload', this.onBeforeLoad, this);
28003             this.store.un('load', this.onLoad, this);
28004             this.store.un('loadexception', this.onLoadException, this);
28005         }
28006         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28007     },
28008
28009     // private
28010     fireKey : function(e){
28011         if(e.isNavKeyPress() && !this.list.isVisible()){
28012             this.fireEvent("specialkey", this, e);
28013         }
28014     },
28015
28016     // private
28017     onResize: function(w, h){
28018         
28019         return; 
28020     
28021         
28022     },
28023
28024     /**
28025      * Allow or prevent the user from directly editing the field text.  If false is passed,
28026      * the user will only be able to select from the items defined in the dropdown list.  This method
28027      * is the runtime equivalent of setting the 'editable' config option at config time.
28028      * @param {Boolean} value True to allow the user to directly edit the field text
28029      */
28030     setEditable : function(value){
28031          
28032     },
28033
28034     // private
28035     onBeforeLoad : function(){
28036         
28037         Roo.log("Select before load");
28038         return;
28039     
28040         this.innerList.update(this.loadingText ?
28041                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28042         //this.restrictHeight();
28043         this.selectedIndex = -1;
28044     },
28045
28046     // private
28047     onLoad : function(){
28048
28049     
28050         var dom = this.el.dom;
28051         dom.innerHTML = '';
28052          var od = dom.ownerDocument;
28053          
28054         if (this.emptyText) {
28055             var op = od.createElement('option');
28056             op.setAttribute('value', '');
28057             op.innerHTML = String.format('{0}', this.emptyText);
28058             dom.appendChild(op);
28059         }
28060         if(this.store.getCount() > 0){
28061            
28062             var vf = this.valueField;
28063             var df = this.displayField;
28064             this.store.data.each(function(r) {
28065                 // which colmsn to use... testing - cdoe / title..
28066                 var op = od.createElement('option');
28067                 op.setAttribute('value', r.data[vf]);
28068                 op.innerHTML = String.format('{0}', r.data[df]);
28069                 dom.appendChild(op);
28070             });
28071             if (typeof(this.defaultValue != 'undefined')) {
28072                 this.setValue(this.defaultValue);
28073             }
28074             
28075              
28076         }else{
28077             //this.onEmptyResults();
28078         }
28079         //this.el.focus();
28080     },
28081     // private
28082     onLoadException : function()
28083     {
28084         dom.innerHTML = '';
28085             
28086         Roo.log("Select on load exception");
28087         return;
28088     
28089         this.collapse();
28090         Roo.log(this.store.reader.jsonData);
28091         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28092             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28093         }
28094         
28095         
28096     },
28097     // private
28098     onTypeAhead : function(){
28099          
28100     },
28101
28102     // private
28103     onSelect : function(record, index){
28104         Roo.log('on select?');
28105         return;
28106         if(this.fireEvent('beforeselect', this, record, index) !== false){
28107             this.setFromData(index > -1 ? record.data : false);
28108             this.collapse();
28109             this.fireEvent('select', this, record, index);
28110         }
28111     },
28112
28113     /**
28114      * Returns the currently selected field value or empty string if no value is set.
28115      * @return {String} value The selected value
28116      */
28117     getValue : function(){
28118         var dom = this.el.dom;
28119         this.value = dom.options[dom.selectedIndex].value;
28120         return this.value;
28121         
28122     },
28123
28124     /**
28125      * Clears any text/value currently set in the field
28126      */
28127     clearValue : function(){
28128         this.value = '';
28129         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28130         
28131     },
28132
28133     /**
28134      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28135      * will be displayed in the field.  If the value does not match the data value of an existing item,
28136      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28137      * Otherwise the field will be blank (although the value will still be set).
28138      * @param {String} value The value to match
28139      */
28140     setValue : function(v){
28141         var d = this.el.dom;
28142         for (var i =0; i < d.options.length;i++) {
28143             if (v == d.options[i].value) {
28144                 d.selectedIndex = i;
28145                 this.value = v;
28146                 return;
28147             }
28148         }
28149         this.clearValue();
28150     },
28151     /**
28152      * @property {Object} the last set data for the element
28153      */
28154     
28155     lastData : false,
28156     /**
28157      * Sets the value of the field based on a object which is related to the record format for the store.
28158      * @param {Object} value the value to set as. or false on reset?
28159      */
28160     setFromData : function(o){
28161         Roo.log('setfrom data?');
28162          
28163         
28164         
28165     },
28166     // private
28167     reset : function(){
28168         this.clearValue();
28169     },
28170     // private
28171     findRecord : function(prop, value){
28172         
28173         return false;
28174     
28175         var record;
28176         if(this.store.getCount() > 0){
28177             this.store.each(function(r){
28178                 if(r.data[prop] == value){
28179                     record = r;
28180                     return false;
28181                 }
28182                 return true;
28183             });
28184         }
28185         return record;
28186     },
28187     
28188     getName: function()
28189     {
28190         // returns hidden if it's set..
28191         if (!this.rendered) {return ''};
28192         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28193         
28194     },
28195      
28196
28197     
28198
28199     // private
28200     onEmptyResults : function(){
28201         Roo.log('empty results');
28202         //this.collapse();
28203     },
28204
28205     /**
28206      * Returns true if the dropdown list is expanded, else false.
28207      */
28208     isExpanded : function(){
28209         return false;
28210     },
28211
28212     /**
28213      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28214      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28215      * @param {String} value The data value of the item to select
28216      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28217      * selected item if it is not currently in view (defaults to true)
28218      * @return {Boolean} True if the value matched an item in the list, else false
28219      */
28220     selectByValue : function(v, scrollIntoView){
28221         Roo.log('select By Value');
28222         return false;
28223     
28224         if(v !== undefined && v !== null){
28225             var r = this.findRecord(this.valueField || this.displayField, v);
28226             if(r){
28227                 this.select(this.store.indexOf(r), scrollIntoView);
28228                 return true;
28229             }
28230         }
28231         return false;
28232     },
28233
28234     /**
28235      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28236      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28237      * @param {Number} index The zero-based index of the list item to select
28238      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28239      * selected item if it is not currently in view (defaults to true)
28240      */
28241     select : function(index, scrollIntoView){
28242         Roo.log('select ');
28243         return  ;
28244         
28245         this.selectedIndex = index;
28246         this.view.select(index);
28247         if(scrollIntoView !== false){
28248             var el = this.view.getNode(index);
28249             if(el){
28250                 this.innerList.scrollChildIntoView(el, false);
28251             }
28252         }
28253     },
28254
28255       
28256
28257     // private
28258     validateBlur : function(){
28259         
28260         return;
28261         
28262     },
28263
28264     // private
28265     initQuery : function(){
28266         this.doQuery(this.getRawValue());
28267     },
28268
28269     // private
28270     doForce : function(){
28271         if(this.el.dom.value.length > 0){
28272             this.el.dom.value =
28273                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28274              
28275         }
28276     },
28277
28278     /**
28279      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28280      * query allowing the query action to be canceled if needed.
28281      * @param {String} query The SQL query to execute
28282      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28283      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28284      * saved in the current store (defaults to false)
28285      */
28286     doQuery : function(q, forceAll){
28287         
28288         Roo.log('doQuery?');
28289         if(q === undefined || q === null){
28290             q = '';
28291         }
28292         var qe = {
28293             query: q,
28294             forceAll: forceAll,
28295             combo: this,
28296             cancel:false
28297         };
28298         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28299             return false;
28300         }
28301         q = qe.query;
28302         forceAll = qe.forceAll;
28303         if(forceAll === true || (q.length >= this.minChars)){
28304             if(this.lastQuery != q || this.alwaysQuery){
28305                 this.lastQuery = q;
28306                 if(this.mode == 'local'){
28307                     this.selectedIndex = -1;
28308                     if(forceAll){
28309                         this.store.clearFilter();
28310                     }else{
28311                         this.store.filter(this.displayField, q);
28312                     }
28313                     this.onLoad();
28314                 }else{
28315                     this.store.baseParams[this.queryParam] = q;
28316                     this.store.load({
28317                         params: this.getParams(q)
28318                     });
28319                     this.expand();
28320                 }
28321             }else{
28322                 this.selectedIndex = -1;
28323                 this.onLoad();   
28324             }
28325         }
28326     },
28327
28328     // private
28329     getParams : function(q){
28330         var p = {};
28331         //p[this.queryParam] = q;
28332         if(this.pageSize){
28333             p.start = 0;
28334             p.limit = this.pageSize;
28335         }
28336         return p;
28337     },
28338
28339     /**
28340      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28341      */
28342     collapse : function(){
28343         
28344     },
28345
28346     // private
28347     collapseIf : function(e){
28348         
28349     },
28350
28351     /**
28352      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28353      */
28354     expand : function(){
28355         
28356     } ,
28357
28358     // private
28359      
28360
28361     /** 
28362     * @cfg {Boolean} grow 
28363     * @hide 
28364     */
28365     /** 
28366     * @cfg {Number} growMin 
28367     * @hide 
28368     */
28369     /** 
28370     * @cfg {Number} growMax 
28371     * @hide 
28372     */
28373     /**
28374      * @hide
28375      * @method autoSize
28376      */
28377     
28378     setWidth : function()
28379     {
28380         
28381     },
28382     getResizeEl : function(){
28383         return this.el;
28384     }
28385 });//<script type="text/javasscript">
28386  
28387
28388 /**
28389  * @class Roo.DDView
28390  * A DnD enabled version of Roo.View.
28391  * @param {Element/String} container The Element in which to create the View.
28392  * @param {String} tpl The template string used to create the markup for each element of the View
28393  * @param {Object} config The configuration properties. These include all the config options of
28394  * {@link Roo.View} plus some specific to this class.<br>
28395  * <p>
28396  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28397  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28398  * <p>
28399  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28400 .x-view-drag-insert-above {
28401         border-top:1px dotted #3366cc;
28402 }
28403 .x-view-drag-insert-below {
28404         border-bottom:1px dotted #3366cc;
28405 }
28406 </code></pre>
28407  * 
28408  */
28409  
28410 Roo.DDView = function(container, tpl, config) {
28411     Roo.DDView.superclass.constructor.apply(this, arguments);
28412     this.getEl().setStyle("outline", "0px none");
28413     this.getEl().unselectable();
28414     if (this.dragGroup) {
28415                 this.setDraggable(this.dragGroup.split(","));
28416     }
28417     if (this.dropGroup) {
28418                 this.setDroppable(this.dropGroup.split(","));
28419     }
28420     if (this.deletable) {
28421         this.setDeletable();
28422     }
28423     this.isDirtyFlag = false;
28424         this.addEvents({
28425                 "drop" : true
28426         });
28427 };
28428
28429 Roo.extend(Roo.DDView, Roo.View, {
28430 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28431 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28432 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28433 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28434
28435         isFormField: true,
28436
28437         reset: Roo.emptyFn,
28438         
28439         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28440
28441         validate: function() {
28442                 return true;
28443         },
28444         
28445         destroy: function() {
28446                 this.purgeListeners();
28447                 this.getEl.removeAllListeners();
28448                 this.getEl().remove();
28449                 if (this.dragZone) {
28450                         if (this.dragZone.destroy) {
28451                                 this.dragZone.destroy();
28452                         }
28453                 }
28454                 if (this.dropZone) {
28455                         if (this.dropZone.destroy) {
28456                                 this.dropZone.destroy();
28457                         }
28458                 }
28459         },
28460
28461 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28462         getName: function() {
28463                 return this.name;
28464         },
28465
28466 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28467         setValue: function(v) {
28468                 if (!this.store) {
28469                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28470                 }
28471                 var data = {};
28472                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28473                 this.store.proxy = new Roo.data.MemoryProxy(data);
28474                 this.store.load();
28475         },
28476
28477 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28478         getValue: function() {
28479                 var result = '(';
28480                 this.store.each(function(rec) {
28481                         result += rec.id + ',';
28482                 });
28483                 return result.substr(0, result.length - 1) + ')';
28484         },
28485         
28486         getIds: function() {
28487                 var i = 0, result = new Array(this.store.getCount());
28488                 this.store.each(function(rec) {
28489                         result[i++] = rec.id;
28490                 });
28491                 return result;
28492         },
28493         
28494         isDirty: function() {
28495                 return this.isDirtyFlag;
28496         },
28497
28498 /**
28499  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28500  *      whole Element becomes the target, and this causes the drop gesture to append.
28501  */
28502     getTargetFromEvent : function(e) {
28503                 var target = e.getTarget();
28504                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28505                 target = target.parentNode;
28506                 }
28507                 if (!target) {
28508                         target = this.el.dom.lastChild || this.el.dom;
28509                 }
28510                 return target;
28511     },
28512
28513 /**
28514  *      Create the drag data which consists of an object which has the property "ddel" as
28515  *      the drag proxy element. 
28516  */
28517     getDragData : function(e) {
28518         var target = this.findItemFromChild(e.getTarget());
28519                 if(target) {
28520                         this.handleSelection(e);
28521                         var selNodes = this.getSelectedNodes();
28522             var dragData = {
28523                 source: this,
28524                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28525                 nodes: selNodes,
28526                 records: []
28527                         };
28528                         var selectedIndices = this.getSelectedIndexes();
28529                         for (var i = 0; i < selectedIndices.length; i++) {
28530                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28531                         }
28532                         if (selNodes.length == 1) {
28533                                 dragData.ddel = target.cloneNode(true); // the div element
28534                         } else {
28535                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28536                                 div.className = 'multi-proxy';
28537                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28538                                         div.appendChild(selNodes[i].cloneNode(true));
28539                                 }
28540                                 dragData.ddel = div;
28541                         }
28542             //console.log(dragData)
28543             //console.log(dragData.ddel.innerHTML)
28544                         return dragData;
28545                 }
28546         //console.log('nodragData')
28547                 return false;
28548     },
28549     
28550 /**     Specify to which ddGroup items in this DDView may be dragged. */
28551     setDraggable: function(ddGroup) {
28552         if (ddGroup instanceof Array) {
28553                 Roo.each(ddGroup, this.setDraggable, this);
28554                 return;
28555         }
28556         if (this.dragZone) {
28557                 this.dragZone.addToGroup(ddGroup);
28558         } else {
28559                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28560                                 containerScroll: true,
28561                                 ddGroup: ddGroup 
28562
28563                         });
28564 //                      Draggability implies selection. DragZone's mousedown selects the element.
28565                         if (!this.multiSelect) { this.singleSelect = true; }
28566
28567 //                      Wire the DragZone's handlers up to methods in *this*
28568                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28569                 }
28570     },
28571
28572 /**     Specify from which ddGroup this DDView accepts drops. */
28573     setDroppable: function(ddGroup) {
28574         if (ddGroup instanceof Array) {
28575                 Roo.each(ddGroup, this.setDroppable, this);
28576                 return;
28577         }
28578         if (this.dropZone) {
28579                 this.dropZone.addToGroup(ddGroup);
28580         } else {
28581                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28582                                 containerScroll: true,
28583                                 ddGroup: ddGroup
28584                         });
28585
28586 //                      Wire the DropZone's handlers up to methods in *this*
28587                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28588                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28589                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28590                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28591                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28592                 }
28593     },
28594
28595 /**     Decide whether to drop above or below a View node. */
28596     getDropPoint : function(e, n, dd){
28597         if (n == this.el.dom) { return "above"; }
28598                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28599                 var c = t + (b - t) / 2;
28600                 var y = Roo.lib.Event.getPageY(e);
28601                 if(y <= c) {
28602                         return "above";
28603                 }else{
28604                         return "below";
28605                 }
28606     },
28607
28608     onNodeEnter : function(n, dd, e, data){
28609                 return false;
28610     },
28611     
28612     onNodeOver : function(n, dd, e, data){
28613                 var pt = this.getDropPoint(e, n, dd);
28614                 // set the insert point style on the target node
28615                 var dragElClass = this.dropNotAllowed;
28616                 if (pt) {
28617                         var targetElClass;
28618                         if (pt == "above"){
28619                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28620                                 targetElClass = "x-view-drag-insert-above";
28621                         } else {
28622                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28623                                 targetElClass = "x-view-drag-insert-below";
28624                         }
28625                         if (this.lastInsertClass != targetElClass){
28626                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28627                                 this.lastInsertClass = targetElClass;
28628                         }
28629                 }
28630                 return dragElClass;
28631         },
28632
28633     onNodeOut : function(n, dd, e, data){
28634                 this.removeDropIndicators(n);
28635     },
28636
28637     onNodeDrop : function(n, dd, e, data){
28638         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28639                 return false;
28640         }
28641         var pt = this.getDropPoint(e, n, dd);
28642                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28643                 if (pt == "below") { insertAt++; }
28644                 for (var i = 0; i < data.records.length; i++) {
28645                         var r = data.records[i];
28646                         var dup = this.store.getById(r.id);
28647                         if (dup && (dd != this.dragZone)) {
28648                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28649                         } else {
28650                                 if (data.copy) {
28651                                         this.store.insert(insertAt++, r.copy());
28652                                 } else {
28653                                         data.source.isDirtyFlag = true;
28654                                         r.store.remove(r);
28655                                         this.store.insert(insertAt++, r);
28656                                 }
28657                                 this.isDirtyFlag = true;
28658                         }
28659                 }
28660                 this.dragZone.cachedTarget = null;
28661                 return true;
28662     },
28663
28664     removeDropIndicators : function(n){
28665                 if(n){
28666                         Roo.fly(n).removeClass([
28667                                 "x-view-drag-insert-above",
28668                                 "x-view-drag-insert-below"]);
28669                         this.lastInsertClass = "_noclass";
28670                 }
28671     },
28672
28673 /**
28674  *      Utility method. Add a delete option to the DDView's context menu.
28675  *      @param {String} imageUrl The URL of the "delete" icon image.
28676  */
28677         setDeletable: function(imageUrl) {
28678                 if (!this.singleSelect && !this.multiSelect) {
28679                         this.singleSelect = true;
28680                 }
28681                 var c = this.getContextMenu();
28682                 this.contextMenu.on("itemclick", function(item) {
28683                         switch (item.id) {
28684                                 case "delete":
28685                                         this.remove(this.getSelectedIndexes());
28686                                         break;
28687                         }
28688                 }, this);
28689                 this.contextMenu.add({
28690                         icon: imageUrl,
28691                         id: "delete",
28692                         text: 'Delete'
28693                 });
28694         },
28695         
28696 /**     Return the context menu for this DDView. */
28697         getContextMenu: function() {
28698                 if (!this.contextMenu) {
28699 //                      Create the View's context menu
28700                         this.contextMenu = new Roo.menu.Menu({
28701                                 id: this.id + "-contextmenu"
28702                         });
28703                         this.el.on("contextmenu", this.showContextMenu, this);
28704                 }
28705                 return this.contextMenu;
28706         },
28707         
28708         disableContextMenu: function() {
28709                 if (this.contextMenu) {
28710                         this.el.un("contextmenu", this.showContextMenu, this);
28711                 }
28712         },
28713
28714         showContextMenu: function(e, item) {
28715         item = this.findItemFromChild(e.getTarget());
28716                 if (item) {
28717                         e.stopEvent();
28718                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28719                         this.contextMenu.showAt(e.getXY());
28720             }
28721     },
28722
28723 /**
28724  *      Remove {@link Roo.data.Record}s at the specified indices.
28725  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28726  */
28727     remove: function(selectedIndices) {
28728                 selectedIndices = [].concat(selectedIndices);
28729                 for (var i = 0; i < selectedIndices.length; i++) {
28730                         var rec = this.store.getAt(selectedIndices[i]);
28731                         this.store.remove(rec);
28732                 }
28733     },
28734
28735 /**
28736  *      Double click fires the event, but also, if this is draggable, and there is only one other
28737  *      related DropZone, it transfers the selected node.
28738  */
28739     onDblClick : function(e){
28740         var item = this.findItemFromChild(e.getTarget());
28741         if(item){
28742             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28743                 return false;
28744             }
28745             if (this.dragGroup) {
28746                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28747                     while (targets.indexOf(this.dropZone) > -1) {
28748                             targets.remove(this.dropZone);
28749                                 }
28750                     if (targets.length == 1) {
28751                                         this.dragZone.cachedTarget = null;
28752                         var el = Roo.get(targets[0].getEl());
28753                         var box = el.getBox(true);
28754                         targets[0].onNodeDrop(el.dom, {
28755                                 target: el.dom,
28756                                 xy: [box.x, box.y + box.height - 1]
28757                         }, null, this.getDragData(e));
28758                     }
28759                 }
28760         }
28761     },
28762     
28763     handleSelection: function(e) {
28764                 this.dragZone.cachedTarget = null;
28765         var item = this.findItemFromChild(e.getTarget());
28766         if (!item) {
28767                 this.clearSelections(true);
28768                 return;
28769         }
28770                 if (item && (this.multiSelect || this.singleSelect)){
28771                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28772                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28773                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28774                                 this.unselect(item);
28775                         } else {
28776                                 this.select(item, this.multiSelect && e.ctrlKey);
28777                                 this.lastSelection = item;
28778                         }
28779                 }
28780     },
28781
28782     onItemClick : function(item, index, e){
28783                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28784                         return false;
28785                 }
28786                 return true;
28787     },
28788
28789     unselect : function(nodeInfo, suppressEvent){
28790                 var node = this.getNode(nodeInfo);
28791                 if(node && this.isSelected(node)){
28792                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28793                                 Roo.fly(node).removeClass(this.selectedClass);
28794                                 this.selections.remove(node);
28795                                 if(!suppressEvent){
28796                                         this.fireEvent("selectionchange", this, this.selections);
28797                                 }
28798                         }
28799                 }
28800     }
28801 });
28802 /*
28803  * Based on:
28804  * Ext JS Library 1.1.1
28805  * Copyright(c) 2006-2007, Ext JS, LLC.
28806  *
28807  * Originally Released Under LGPL - original licence link has changed is not relivant.
28808  *
28809  * Fork - LGPL
28810  * <script type="text/javascript">
28811  */
28812  
28813 /**
28814  * @class Roo.LayoutManager
28815  * @extends Roo.util.Observable
28816  * Base class for layout managers.
28817  */
28818 Roo.LayoutManager = function(container, config){
28819     Roo.LayoutManager.superclass.constructor.call(this);
28820     this.el = Roo.get(container);
28821     // ie scrollbar fix
28822     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28823         document.body.scroll = "no";
28824     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28825         this.el.position('relative');
28826     }
28827     this.id = this.el.id;
28828     this.el.addClass("x-layout-container");
28829     /** false to disable window resize monitoring @type Boolean */
28830     this.monitorWindowResize = true;
28831     this.regions = {};
28832     this.addEvents({
28833         /**
28834          * @event layout
28835          * Fires when a layout is performed. 
28836          * @param {Roo.LayoutManager} this
28837          */
28838         "layout" : true,
28839         /**
28840          * @event regionresized
28841          * Fires when the user resizes a region. 
28842          * @param {Roo.LayoutRegion} region The resized region
28843          * @param {Number} newSize The new size (width for east/west, height for north/south)
28844          */
28845         "regionresized" : true,
28846         /**
28847          * @event regioncollapsed
28848          * Fires when a region is collapsed. 
28849          * @param {Roo.LayoutRegion} region The collapsed region
28850          */
28851         "regioncollapsed" : true,
28852         /**
28853          * @event regionexpanded
28854          * Fires when a region is expanded.  
28855          * @param {Roo.LayoutRegion} region The expanded region
28856          */
28857         "regionexpanded" : true
28858     });
28859     this.updating = false;
28860     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28861 };
28862
28863 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28864     /**
28865      * Returns true if this layout is currently being updated
28866      * @return {Boolean}
28867      */
28868     isUpdating : function(){
28869         return this.updating; 
28870     },
28871     
28872     /**
28873      * Suspend the LayoutManager from doing auto-layouts while
28874      * making multiple add or remove calls
28875      */
28876     beginUpdate : function(){
28877         this.updating = true;    
28878     },
28879     
28880     /**
28881      * Restore auto-layouts and optionally disable the manager from performing a layout
28882      * @param {Boolean} noLayout true to disable a layout update 
28883      */
28884     endUpdate : function(noLayout){
28885         this.updating = false;
28886         if(!noLayout){
28887             this.layout();
28888         }    
28889     },
28890     
28891     layout: function(){
28892         
28893     },
28894     
28895     onRegionResized : function(region, newSize){
28896         this.fireEvent("regionresized", region, newSize);
28897         this.layout();
28898     },
28899     
28900     onRegionCollapsed : function(region){
28901         this.fireEvent("regioncollapsed", region);
28902     },
28903     
28904     onRegionExpanded : function(region){
28905         this.fireEvent("regionexpanded", region);
28906     },
28907         
28908     /**
28909      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28910      * performs box-model adjustments.
28911      * @return {Object} The size as an object {width: (the width), height: (the height)}
28912      */
28913     getViewSize : function(){
28914         var size;
28915         if(this.el.dom != document.body){
28916             size = this.el.getSize();
28917         }else{
28918             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28919         }
28920         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28921         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28922         return size;
28923     },
28924     
28925     /**
28926      * Returns the Element this layout is bound to.
28927      * @return {Roo.Element}
28928      */
28929     getEl : function(){
28930         return this.el;
28931     },
28932     
28933     /**
28934      * Returns the specified region.
28935      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28936      * @return {Roo.LayoutRegion}
28937      */
28938     getRegion : function(target){
28939         return this.regions[target.toLowerCase()];
28940     },
28941     
28942     onWindowResize : function(){
28943         if(this.monitorWindowResize){
28944             this.layout();
28945         }
28946     }
28947 });/*
28948  * Based on:
28949  * Ext JS Library 1.1.1
28950  * Copyright(c) 2006-2007, Ext JS, LLC.
28951  *
28952  * Originally Released Under LGPL - original licence link has changed is not relivant.
28953  *
28954  * Fork - LGPL
28955  * <script type="text/javascript">
28956  */
28957 /**
28958  * @class Roo.BorderLayout
28959  * @extends Roo.LayoutManager
28960  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28961  * please see: <br><br>
28962  * <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>
28963  * <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>
28964  * Example:
28965  <pre><code>
28966  var layout = new Roo.BorderLayout(document.body, {
28967     north: {
28968         initialSize: 25,
28969         titlebar: false
28970     },
28971     west: {
28972         split:true,
28973         initialSize: 200,
28974         minSize: 175,
28975         maxSize: 400,
28976         titlebar: true,
28977         collapsible: true
28978     },
28979     east: {
28980         split:true,
28981         initialSize: 202,
28982         minSize: 175,
28983         maxSize: 400,
28984         titlebar: true,
28985         collapsible: true
28986     },
28987     south: {
28988         split:true,
28989         initialSize: 100,
28990         minSize: 100,
28991         maxSize: 200,
28992         titlebar: true,
28993         collapsible: true
28994     },
28995     center: {
28996         titlebar: true,
28997         autoScroll:true,
28998         resizeTabs: true,
28999         minTabWidth: 50,
29000         preferredTabWidth: 150
29001     }
29002 });
29003
29004 // shorthand
29005 var CP = Roo.ContentPanel;
29006
29007 layout.beginUpdate();
29008 layout.add("north", new CP("north", "North"));
29009 layout.add("south", new CP("south", {title: "South", closable: true}));
29010 layout.add("west", new CP("west", {title: "West"}));
29011 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29012 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29013 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29014 layout.getRegion("center").showPanel("center1");
29015 layout.endUpdate();
29016 </code></pre>
29017
29018 <b>The container the layout is rendered into can be either the body element or any other element.
29019 If it is not the body element, the container needs to either be an absolute positioned element,
29020 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29021 the container size if it is not the body element.</b>
29022
29023 * @constructor
29024 * Create a new BorderLayout
29025 * @param {String/HTMLElement/Element} container The container this layout is bound to
29026 * @param {Object} config Configuration options
29027  */
29028 Roo.BorderLayout = function(container, config){
29029     config = config || {};
29030     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29031     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29032     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29033         var target = this.factory.validRegions[i];
29034         if(config[target]){
29035             this.addRegion(target, config[target]);
29036         }
29037     }
29038 };
29039
29040 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29041     /**
29042      * Creates and adds a new region if it doesn't already exist.
29043      * @param {String} target The target region key (north, south, east, west or center).
29044      * @param {Object} config The regions config object
29045      * @return {BorderLayoutRegion} The new region
29046      */
29047     addRegion : function(target, config){
29048         if(!this.regions[target]){
29049             var r = this.factory.create(target, this, config);
29050             this.bindRegion(target, r);
29051         }
29052         return this.regions[target];
29053     },
29054
29055     // private (kinda)
29056     bindRegion : function(name, r){
29057         this.regions[name] = r;
29058         r.on("visibilitychange", this.layout, this);
29059         r.on("paneladded", this.layout, this);
29060         r.on("panelremoved", this.layout, this);
29061         r.on("invalidated", this.layout, this);
29062         r.on("resized", this.onRegionResized, this);
29063         r.on("collapsed", this.onRegionCollapsed, this);
29064         r.on("expanded", this.onRegionExpanded, this);
29065     },
29066
29067     /**
29068      * Performs a layout update.
29069      */
29070     layout : function(){
29071         if(this.updating) {
29072             return;
29073         }
29074         var size = this.getViewSize();
29075         var w = size.width;
29076         var h = size.height;
29077         var centerW = w;
29078         var centerH = h;
29079         var centerY = 0;
29080         var centerX = 0;
29081         //var x = 0, y = 0;
29082
29083         var rs = this.regions;
29084         var north = rs["north"];
29085         var south = rs["south"]; 
29086         var west = rs["west"];
29087         var east = rs["east"];
29088         var center = rs["center"];
29089         //if(this.hideOnLayout){ // not supported anymore
29090             //c.el.setStyle("display", "none");
29091         //}
29092         if(north && north.isVisible()){
29093             var b = north.getBox();
29094             var m = north.getMargins();
29095             b.width = w - (m.left+m.right);
29096             b.x = m.left;
29097             b.y = m.top;
29098             centerY = b.height + b.y + m.bottom;
29099             centerH -= centerY;
29100             north.updateBox(this.safeBox(b));
29101         }
29102         if(south && south.isVisible()){
29103             var b = south.getBox();
29104             var m = south.getMargins();
29105             b.width = w - (m.left+m.right);
29106             b.x = m.left;
29107             var totalHeight = (b.height + m.top + m.bottom);
29108             b.y = h - totalHeight + m.top;
29109             centerH -= totalHeight;
29110             south.updateBox(this.safeBox(b));
29111         }
29112         if(west && west.isVisible()){
29113             var b = west.getBox();
29114             var m = west.getMargins();
29115             b.height = centerH - (m.top+m.bottom);
29116             b.x = m.left;
29117             b.y = centerY + m.top;
29118             var totalWidth = (b.width + m.left + m.right);
29119             centerX += totalWidth;
29120             centerW -= totalWidth;
29121             west.updateBox(this.safeBox(b));
29122         }
29123         if(east && east.isVisible()){
29124             var b = east.getBox();
29125             var m = east.getMargins();
29126             b.height = centerH - (m.top+m.bottom);
29127             var totalWidth = (b.width + m.left + m.right);
29128             b.x = w - totalWidth + m.left;
29129             b.y = centerY + m.top;
29130             centerW -= totalWidth;
29131             east.updateBox(this.safeBox(b));
29132         }
29133         if(center){
29134             var m = center.getMargins();
29135             var centerBox = {
29136                 x: centerX + m.left,
29137                 y: centerY + m.top,
29138                 width: centerW - (m.left+m.right),
29139                 height: centerH - (m.top+m.bottom)
29140             };
29141             //if(this.hideOnLayout){
29142                 //center.el.setStyle("display", "block");
29143             //}
29144             center.updateBox(this.safeBox(centerBox));
29145         }
29146         this.el.repaint();
29147         this.fireEvent("layout", this);
29148     },
29149
29150     // private
29151     safeBox : function(box){
29152         box.width = Math.max(0, box.width);
29153         box.height = Math.max(0, box.height);
29154         return box;
29155     },
29156
29157     /**
29158      * Adds a ContentPanel (or subclass) to this layout.
29159      * @param {String} target The target region key (north, south, east, west or center).
29160      * @param {Roo.ContentPanel} panel The panel to add
29161      * @return {Roo.ContentPanel} The added panel
29162      */
29163     add : function(target, panel){
29164          
29165         target = target.toLowerCase();
29166         return this.regions[target].add(panel);
29167     },
29168
29169     /**
29170      * Remove a ContentPanel (or subclass) to this layout.
29171      * @param {String} target The target region key (north, south, east, west or center).
29172      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29173      * @return {Roo.ContentPanel} The removed panel
29174      */
29175     remove : function(target, panel){
29176         target = target.toLowerCase();
29177         return this.regions[target].remove(panel);
29178     },
29179
29180     /**
29181      * Searches all regions for a panel with the specified id
29182      * @param {String} panelId
29183      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29184      */
29185     findPanel : function(panelId){
29186         var rs = this.regions;
29187         for(var target in rs){
29188             if(typeof rs[target] != "function"){
29189                 var p = rs[target].getPanel(panelId);
29190                 if(p){
29191                     return p;
29192                 }
29193             }
29194         }
29195         return null;
29196     },
29197
29198     /**
29199      * Searches all regions for a panel with the specified id and activates (shows) it.
29200      * @param {String/ContentPanel} panelId The panels id or the panel itself
29201      * @return {Roo.ContentPanel} The shown panel or null
29202      */
29203     showPanel : function(panelId) {
29204       var rs = this.regions;
29205       for(var target in rs){
29206          var r = rs[target];
29207          if(typeof r != "function"){
29208             if(r.hasPanel(panelId)){
29209                return r.showPanel(panelId);
29210             }
29211          }
29212       }
29213       return null;
29214    },
29215
29216    /**
29217      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29218      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29219      */
29220     restoreState : function(provider){
29221         if(!provider){
29222             provider = Roo.state.Manager;
29223         }
29224         var sm = new Roo.LayoutStateManager();
29225         sm.init(this, provider);
29226     },
29227
29228     /**
29229      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29230      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29231      * a valid ContentPanel config object.  Example:
29232      * <pre><code>
29233 // Create the main layout
29234 var layout = new Roo.BorderLayout('main-ct', {
29235     west: {
29236         split:true,
29237         minSize: 175,
29238         titlebar: true
29239     },
29240     center: {
29241         title:'Components'
29242     }
29243 }, 'main-ct');
29244
29245 // Create and add multiple ContentPanels at once via configs
29246 layout.batchAdd({
29247    west: {
29248        id: 'source-files',
29249        autoCreate:true,
29250        title:'Ext Source Files',
29251        autoScroll:true,
29252        fitToFrame:true
29253    },
29254    center : {
29255        el: cview,
29256        autoScroll:true,
29257        fitToFrame:true,
29258        toolbar: tb,
29259        resizeEl:'cbody'
29260    }
29261 });
29262 </code></pre>
29263      * @param {Object} regions An object containing ContentPanel configs by region name
29264      */
29265     batchAdd : function(regions){
29266         this.beginUpdate();
29267         for(var rname in regions){
29268             var lr = this.regions[rname];
29269             if(lr){
29270                 this.addTypedPanels(lr, regions[rname]);
29271             }
29272         }
29273         this.endUpdate();
29274     },
29275
29276     // private
29277     addTypedPanels : function(lr, ps){
29278         if(typeof ps == 'string'){
29279             lr.add(new Roo.ContentPanel(ps));
29280         }
29281         else if(ps instanceof Array){
29282             for(var i =0, len = ps.length; i < len; i++){
29283                 this.addTypedPanels(lr, ps[i]);
29284             }
29285         }
29286         else if(!ps.events){ // raw config?
29287             var el = ps.el;
29288             delete ps.el; // prevent conflict
29289             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29290         }
29291         else {  // panel object assumed!
29292             lr.add(ps);
29293         }
29294     },
29295     /**
29296      * Adds a xtype elements to the layout.
29297      * <pre><code>
29298
29299 layout.addxtype({
29300        xtype : 'ContentPanel',
29301        region: 'west',
29302        items: [ .... ]
29303    }
29304 );
29305
29306 layout.addxtype({
29307         xtype : 'NestedLayoutPanel',
29308         region: 'west',
29309         layout: {
29310            center: { },
29311            west: { }   
29312         },
29313         items : [ ... list of content panels or nested layout panels.. ]
29314    }
29315 );
29316 </code></pre>
29317      * @param {Object} cfg Xtype definition of item to add.
29318      */
29319     addxtype : function(cfg)
29320     {
29321         // basically accepts a pannel...
29322         // can accept a layout region..!?!?
29323         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29324         
29325         if (!cfg.xtype.match(/Panel$/)) {
29326             return false;
29327         }
29328         var ret = false;
29329         
29330         if (typeof(cfg.region) == 'undefined') {
29331             Roo.log("Failed to add Panel, region was not set");
29332             Roo.log(cfg);
29333             return false;
29334         }
29335         var region = cfg.region;
29336         delete cfg.region;
29337         
29338           
29339         var xitems = [];
29340         if (cfg.items) {
29341             xitems = cfg.items;
29342             delete cfg.items;
29343         }
29344         var nb = false;
29345         
29346         switch(cfg.xtype) 
29347         {
29348             case 'ContentPanel':  // ContentPanel (el, cfg)
29349             case 'ScrollPanel':  // ContentPanel (el, cfg)
29350             case 'ViewPanel': 
29351                 if(cfg.autoCreate) {
29352                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29353                 } else {
29354                     var el = this.el.createChild();
29355                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29356                 }
29357                 
29358                 this.add(region, ret);
29359                 break;
29360             
29361             
29362             case 'TreePanel': // our new panel!
29363                 cfg.el = this.el.createChild();
29364                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29365                 this.add(region, ret);
29366                 break;
29367             
29368             case 'NestedLayoutPanel': 
29369                 // create a new Layout (which is  a Border Layout...
29370                 var el = this.el.createChild();
29371                 var clayout = cfg.layout;
29372                 delete cfg.layout;
29373                 clayout.items   = clayout.items  || [];
29374                 // replace this exitems with the clayout ones..
29375                 xitems = clayout.items;
29376                  
29377                 
29378                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29379                     cfg.background = false;
29380                 }
29381                 var layout = new Roo.BorderLayout(el, clayout);
29382                 
29383                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29384                 //console.log('adding nested layout panel '  + cfg.toSource());
29385                 this.add(region, ret);
29386                 nb = {}; /// find first...
29387                 break;
29388                 
29389             case 'GridPanel': 
29390             
29391                 // needs grid and region
29392                 
29393                 //var el = this.getRegion(region).el.createChild();
29394                 var el = this.el.createChild();
29395                 // create the grid first...
29396                 
29397                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29398                 delete cfg.grid;
29399                 if (region == 'center' && this.active ) {
29400                     cfg.background = false;
29401                 }
29402                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29403                 
29404                 this.add(region, ret);
29405                 if (cfg.background) {
29406                     ret.on('activate', function(gp) {
29407                         if (!gp.grid.rendered) {
29408                             gp.grid.render();
29409                         }
29410                     });
29411                 } else {
29412                     grid.render();
29413                 }
29414                 break;
29415            
29416            
29417            
29418                 
29419                 
29420                 
29421             default:
29422                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29423                     
29424                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29425                     this.add(region, ret);
29426                 } else {
29427                 
29428                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29429                     return null;
29430                 }
29431                 
29432              // GridPanel (grid, cfg)
29433             
29434         }
29435         this.beginUpdate();
29436         // add children..
29437         var region = '';
29438         var abn = {};
29439         Roo.each(xitems, function(i)  {
29440             region = nb && i.region ? i.region : false;
29441             
29442             var add = ret.addxtype(i);
29443            
29444             if (region) {
29445                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29446                 if (!i.background) {
29447                     abn[region] = nb[region] ;
29448                 }
29449             }
29450             
29451         });
29452         this.endUpdate();
29453
29454         // make the last non-background panel active..
29455         //if (nb) { Roo.log(abn); }
29456         if (nb) {
29457             
29458             for(var r in abn) {
29459                 region = this.getRegion(r);
29460                 if (region) {
29461                     // tried using nb[r], but it does not work..
29462                      
29463                     region.showPanel(abn[r]);
29464                    
29465                 }
29466             }
29467         }
29468         return ret;
29469         
29470     }
29471 });
29472
29473 /**
29474  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29475  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29476  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29477  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29478  * <pre><code>
29479 // shorthand
29480 var CP = Roo.ContentPanel;
29481
29482 var layout = Roo.BorderLayout.create({
29483     north: {
29484         initialSize: 25,
29485         titlebar: false,
29486         panels: [new CP("north", "North")]
29487     },
29488     west: {
29489         split:true,
29490         initialSize: 200,
29491         minSize: 175,
29492         maxSize: 400,
29493         titlebar: true,
29494         collapsible: true,
29495         panels: [new CP("west", {title: "West"})]
29496     },
29497     east: {
29498         split:true,
29499         initialSize: 202,
29500         minSize: 175,
29501         maxSize: 400,
29502         titlebar: true,
29503         collapsible: true,
29504         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29505     },
29506     south: {
29507         split:true,
29508         initialSize: 100,
29509         minSize: 100,
29510         maxSize: 200,
29511         titlebar: true,
29512         collapsible: true,
29513         panels: [new CP("south", {title: "South", closable: true})]
29514     },
29515     center: {
29516         titlebar: true,
29517         autoScroll:true,
29518         resizeTabs: true,
29519         minTabWidth: 50,
29520         preferredTabWidth: 150,
29521         panels: [
29522             new CP("center1", {title: "Close Me", closable: true}),
29523             new CP("center2", {title: "Center Panel", closable: false})
29524         ]
29525     }
29526 }, document.body);
29527
29528 layout.getRegion("center").showPanel("center1");
29529 </code></pre>
29530  * @param config
29531  * @param targetEl
29532  */
29533 Roo.BorderLayout.create = function(config, targetEl){
29534     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29535     layout.beginUpdate();
29536     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29537     for(var j = 0, jlen = regions.length; j < jlen; j++){
29538         var lr = regions[j];
29539         if(layout.regions[lr] && config[lr].panels){
29540             var r = layout.regions[lr];
29541             var ps = config[lr].panels;
29542             layout.addTypedPanels(r, ps);
29543         }
29544     }
29545     layout.endUpdate();
29546     return layout;
29547 };
29548
29549 // private
29550 Roo.BorderLayout.RegionFactory = {
29551     // private
29552     validRegions : ["north","south","east","west","center"],
29553
29554     // private
29555     create : function(target, mgr, config){
29556         target = target.toLowerCase();
29557         if(config.lightweight || config.basic){
29558             return new Roo.BasicLayoutRegion(mgr, config, target);
29559         }
29560         switch(target){
29561             case "north":
29562                 return new Roo.NorthLayoutRegion(mgr, config);
29563             case "south":
29564                 return new Roo.SouthLayoutRegion(mgr, config);
29565             case "east":
29566                 return new Roo.EastLayoutRegion(mgr, config);
29567             case "west":
29568                 return new Roo.WestLayoutRegion(mgr, config);
29569             case "center":
29570                 return new Roo.CenterLayoutRegion(mgr, config);
29571         }
29572         throw 'Layout region "'+target+'" not supported.';
29573     }
29574 };/*
29575  * Based on:
29576  * Ext JS Library 1.1.1
29577  * Copyright(c) 2006-2007, Ext JS, LLC.
29578  *
29579  * Originally Released Under LGPL - original licence link has changed is not relivant.
29580  *
29581  * Fork - LGPL
29582  * <script type="text/javascript">
29583  */
29584  
29585 /**
29586  * @class Roo.BasicLayoutRegion
29587  * @extends Roo.util.Observable
29588  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29589  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29590  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29591  */
29592 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29593     this.mgr = mgr;
29594     this.position  = pos;
29595     this.events = {
29596         /**
29597          * @scope Roo.BasicLayoutRegion
29598          */
29599         
29600         /**
29601          * @event beforeremove
29602          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29603          * @param {Roo.LayoutRegion} this
29604          * @param {Roo.ContentPanel} panel The panel
29605          * @param {Object} e The cancel event object
29606          */
29607         "beforeremove" : true,
29608         /**
29609          * @event invalidated
29610          * Fires when the layout for this region is changed.
29611          * @param {Roo.LayoutRegion} this
29612          */
29613         "invalidated" : true,
29614         /**
29615          * @event visibilitychange
29616          * Fires when this region is shown or hidden 
29617          * @param {Roo.LayoutRegion} this
29618          * @param {Boolean} visibility true or false
29619          */
29620         "visibilitychange" : true,
29621         /**
29622          * @event paneladded
29623          * Fires when a panel is added. 
29624          * @param {Roo.LayoutRegion} this
29625          * @param {Roo.ContentPanel} panel The panel
29626          */
29627         "paneladded" : true,
29628         /**
29629          * @event panelremoved
29630          * Fires when a panel is removed. 
29631          * @param {Roo.LayoutRegion} this
29632          * @param {Roo.ContentPanel} panel The panel
29633          */
29634         "panelremoved" : true,
29635         /**
29636          * @event beforecollapse
29637          * Fires when this region before collapse.
29638          * @param {Roo.LayoutRegion} this
29639          */
29640         "beforecollapse" : true,
29641         /**
29642          * @event collapsed
29643          * Fires when this region is collapsed.
29644          * @param {Roo.LayoutRegion} this
29645          */
29646         "collapsed" : true,
29647         /**
29648          * @event expanded
29649          * Fires when this region is expanded.
29650          * @param {Roo.LayoutRegion} this
29651          */
29652         "expanded" : true,
29653         /**
29654          * @event slideshow
29655          * Fires when this region is slid into view.
29656          * @param {Roo.LayoutRegion} this
29657          */
29658         "slideshow" : true,
29659         /**
29660          * @event slidehide
29661          * Fires when this region slides out of view. 
29662          * @param {Roo.LayoutRegion} this
29663          */
29664         "slidehide" : true,
29665         /**
29666          * @event panelactivated
29667          * Fires when a panel is activated. 
29668          * @param {Roo.LayoutRegion} this
29669          * @param {Roo.ContentPanel} panel The activated panel
29670          */
29671         "panelactivated" : true,
29672         /**
29673          * @event resized
29674          * Fires when the user resizes this region. 
29675          * @param {Roo.LayoutRegion} this
29676          * @param {Number} newSize The new size (width for east/west, height for north/south)
29677          */
29678         "resized" : true
29679     };
29680     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29681     this.panels = new Roo.util.MixedCollection();
29682     this.panels.getKey = this.getPanelId.createDelegate(this);
29683     this.box = null;
29684     this.activePanel = null;
29685     // ensure listeners are added...
29686     
29687     if (config.listeners || config.events) {
29688         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29689             listeners : config.listeners || {},
29690             events : config.events || {}
29691         });
29692     }
29693     
29694     if(skipConfig !== true){
29695         this.applyConfig(config);
29696     }
29697 };
29698
29699 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29700     getPanelId : function(p){
29701         return p.getId();
29702     },
29703     
29704     applyConfig : function(config){
29705         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29706         this.config = config;
29707         
29708     },
29709     
29710     /**
29711      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29712      * the width, for horizontal (north, south) the height.
29713      * @param {Number} newSize The new width or height
29714      */
29715     resizeTo : function(newSize){
29716         var el = this.el ? this.el :
29717                  (this.activePanel ? this.activePanel.getEl() : null);
29718         if(el){
29719             switch(this.position){
29720                 case "east":
29721                 case "west":
29722                     el.setWidth(newSize);
29723                     this.fireEvent("resized", this, newSize);
29724                 break;
29725                 case "north":
29726                 case "south":
29727                     el.setHeight(newSize);
29728                     this.fireEvent("resized", this, newSize);
29729                 break;                
29730             }
29731         }
29732     },
29733     
29734     getBox : function(){
29735         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29736     },
29737     
29738     getMargins : function(){
29739         return this.margins;
29740     },
29741     
29742     updateBox : function(box){
29743         this.box = box;
29744         var el = this.activePanel.getEl();
29745         el.dom.style.left = box.x + "px";
29746         el.dom.style.top = box.y + "px";
29747         this.activePanel.setSize(box.width, box.height);
29748     },
29749     
29750     /**
29751      * Returns the container element for this region.
29752      * @return {Roo.Element}
29753      */
29754     getEl : function(){
29755         return this.activePanel;
29756     },
29757     
29758     /**
29759      * Returns true if this region is currently visible.
29760      * @return {Boolean}
29761      */
29762     isVisible : function(){
29763         return this.activePanel ? true : false;
29764     },
29765     
29766     setActivePanel : function(panel){
29767         panel = this.getPanel(panel);
29768         if(this.activePanel && this.activePanel != panel){
29769             this.activePanel.setActiveState(false);
29770             this.activePanel.getEl().setLeftTop(-10000,-10000);
29771         }
29772         this.activePanel = panel;
29773         panel.setActiveState(true);
29774         if(this.box){
29775             panel.setSize(this.box.width, this.box.height);
29776         }
29777         this.fireEvent("panelactivated", this, panel);
29778         this.fireEvent("invalidated");
29779     },
29780     
29781     /**
29782      * Show the specified panel.
29783      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29784      * @return {Roo.ContentPanel} The shown panel or null
29785      */
29786     showPanel : function(panel){
29787         if(panel = this.getPanel(panel)){
29788             this.setActivePanel(panel);
29789         }
29790         return panel;
29791     },
29792     
29793     /**
29794      * Get the active panel for this region.
29795      * @return {Roo.ContentPanel} The active panel or null
29796      */
29797     getActivePanel : function(){
29798         return this.activePanel;
29799     },
29800     
29801     /**
29802      * Add the passed ContentPanel(s)
29803      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29804      * @return {Roo.ContentPanel} The panel added (if only one was added)
29805      */
29806     add : function(panel){
29807         if(arguments.length > 1){
29808             for(var i = 0, len = arguments.length; i < len; i++) {
29809                 this.add(arguments[i]);
29810             }
29811             return null;
29812         }
29813         if(this.hasPanel(panel)){
29814             this.showPanel(panel);
29815             return panel;
29816         }
29817         var el = panel.getEl();
29818         if(el.dom.parentNode != this.mgr.el.dom){
29819             this.mgr.el.dom.appendChild(el.dom);
29820         }
29821         if(panel.setRegion){
29822             panel.setRegion(this);
29823         }
29824         this.panels.add(panel);
29825         el.setStyle("position", "absolute");
29826         if(!panel.background){
29827             this.setActivePanel(panel);
29828             if(this.config.initialSize && this.panels.getCount()==1){
29829                 this.resizeTo(this.config.initialSize);
29830             }
29831         }
29832         this.fireEvent("paneladded", this, panel);
29833         return panel;
29834     },
29835     
29836     /**
29837      * Returns true if the panel is in this region.
29838      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29839      * @return {Boolean}
29840      */
29841     hasPanel : function(panel){
29842         if(typeof panel == "object"){ // must be panel obj
29843             panel = panel.getId();
29844         }
29845         return this.getPanel(panel) ? true : false;
29846     },
29847     
29848     /**
29849      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29850      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29851      * @param {Boolean} preservePanel Overrides the config preservePanel option
29852      * @return {Roo.ContentPanel} The panel that was removed
29853      */
29854     remove : function(panel, preservePanel){
29855         panel = this.getPanel(panel);
29856         if(!panel){
29857             return null;
29858         }
29859         var e = {};
29860         this.fireEvent("beforeremove", this, panel, e);
29861         if(e.cancel === true){
29862             return null;
29863         }
29864         var panelId = panel.getId();
29865         this.panels.removeKey(panelId);
29866         return panel;
29867     },
29868     
29869     /**
29870      * Returns the panel specified or null if it's not in this region.
29871      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29872      * @return {Roo.ContentPanel}
29873      */
29874     getPanel : function(id){
29875         if(typeof id == "object"){ // must be panel obj
29876             return id;
29877         }
29878         return this.panels.get(id);
29879     },
29880     
29881     /**
29882      * Returns this regions position (north/south/east/west/center).
29883      * @return {String} 
29884      */
29885     getPosition: function(){
29886         return this.position;    
29887     }
29888 });/*
29889  * Based on:
29890  * Ext JS Library 1.1.1
29891  * Copyright(c) 2006-2007, Ext JS, LLC.
29892  *
29893  * Originally Released Under LGPL - original licence link has changed is not relivant.
29894  *
29895  * Fork - LGPL
29896  * <script type="text/javascript">
29897  */
29898  
29899 /**
29900  * @class Roo.LayoutRegion
29901  * @extends Roo.BasicLayoutRegion
29902  * This class represents a region in a layout manager.
29903  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29904  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29905  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29906  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29907  * @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})
29908  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29909  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29910  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29911  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29912  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29913  * @cfg {String}    title           The title for the region (overrides panel titles)
29914  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29915  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29916  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29917  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29918  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29919  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29920  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29921  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29922  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29923  * @cfg {Boolean}   showPin         True to show a pin button
29924  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29925  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29926  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29927  * @cfg {Number}    width           For East/West panels
29928  * @cfg {Number}    height          For North/South panels
29929  * @cfg {Boolean}   split           To show the splitter
29930  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29931  */
29932 Roo.LayoutRegion = function(mgr, config, pos){
29933     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29934     var dh = Roo.DomHelper;
29935     /** This region's container element 
29936     * @type Roo.Element */
29937     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29938     /** This region's title element 
29939     * @type Roo.Element */
29940
29941     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29942         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29943         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29944     ]}, true);
29945     this.titleEl.enableDisplayMode();
29946     /** This region's title text element 
29947     * @type HTMLElement */
29948     this.titleTextEl = this.titleEl.dom.firstChild;
29949     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29950     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29951     this.closeBtn.enableDisplayMode();
29952     this.closeBtn.on("click", this.closeClicked, this);
29953     this.closeBtn.hide();
29954
29955     this.createBody(config);
29956     this.visible = true;
29957     this.collapsed = false;
29958
29959     if(config.hideWhenEmpty){
29960         this.hide();
29961         this.on("paneladded", this.validateVisibility, this);
29962         this.on("panelremoved", this.validateVisibility, this);
29963     }
29964     this.applyConfig(config);
29965 };
29966
29967 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29968
29969     createBody : function(){
29970         /** This region's body element 
29971         * @type Roo.Element */
29972         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29973     },
29974
29975     applyConfig : function(c){
29976         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29977             var dh = Roo.DomHelper;
29978             if(c.titlebar !== false){
29979                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29980                 this.collapseBtn.on("click", this.collapse, this);
29981                 this.collapseBtn.enableDisplayMode();
29982
29983                 if(c.showPin === true || this.showPin){
29984                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29985                     this.stickBtn.enableDisplayMode();
29986                     this.stickBtn.on("click", this.expand, this);
29987                     this.stickBtn.hide();
29988                 }
29989             }
29990             /** This region's collapsed element
29991             * @type Roo.Element */
29992             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29993                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29994             ]}, true);
29995             if(c.floatable !== false){
29996                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29997                this.collapsedEl.on("click", this.collapseClick, this);
29998             }
29999
30000             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30001                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30002                    id: "message", unselectable: "on", style:{"float":"left"}});
30003                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30004              }
30005             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30006             this.expandBtn.on("click", this.expand, this);
30007         }
30008         if(this.collapseBtn){
30009             this.collapseBtn.setVisible(c.collapsible == true);
30010         }
30011         this.cmargins = c.cmargins || this.cmargins ||
30012                          (this.position == "west" || this.position == "east" ?
30013                              {top: 0, left: 2, right:2, bottom: 0} :
30014                              {top: 2, left: 0, right:0, bottom: 2});
30015         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30016         this.bottomTabs = c.tabPosition != "top";
30017         this.autoScroll = c.autoScroll || false;
30018         if(this.autoScroll){
30019             this.bodyEl.setStyle("overflow", "auto");
30020         }else{
30021             this.bodyEl.setStyle("overflow", "hidden");
30022         }
30023         //if(c.titlebar !== false){
30024             if((!c.titlebar && !c.title) || c.titlebar === false){
30025                 this.titleEl.hide();
30026             }else{
30027                 this.titleEl.show();
30028                 if(c.title){
30029                     this.titleTextEl.innerHTML = c.title;
30030                 }
30031             }
30032         //}
30033         this.duration = c.duration || .30;
30034         this.slideDuration = c.slideDuration || .45;
30035         this.config = c;
30036         if(c.collapsed){
30037             this.collapse(true);
30038         }
30039         if(c.hidden){
30040             this.hide();
30041         }
30042     },
30043     /**
30044      * Returns true if this region is currently visible.
30045      * @return {Boolean}
30046      */
30047     isVisible : function(){
30048         return this.visible;
30049     },
30050
30051     /**
30052      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30053      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30054      */
30055     setCollapsedTitle : function(title){
30056         title = title || "&#160;";
30057         if(this.collapsedTitleTextEl){
30058             this.collapsedTitleTextEl.innerHTML = title;
30059         }
30060     },
30061
30062     getBox : function(){
30063         var b;
30064         if(!this.collapsed){
30065             b = this.el.getBox(false, true);
30066         }else{
30067             b = this.collapsedEl.getBox(false, true);
30068         }
30069         return b;
30070     },
30071
30072     getMargins : function(){
30073         return this.collapsed ? this.cmargins : this.margins;
30074     },
30075
30076     highlight : function(){
30077         this.el.addClass("x-layout-panel-dragover");
30078     },
30079
30080     unhighlight : function(){
30081         this.el.removeClass("x-layout-panel-dragover");
30082     },
30083
30084     updateBox : function(box){
30085         this.box = box;
30086         if(!this.collapsed){
30087             this.el.dom.style.left = box.x + "px";
30088             this.el.dom.style.top = box.y + "px";
30089             this.updateBody(box.width, box.height);
30090         }else{
30091             this.collapsedEl.dom.style.left = box.x + "px";
30092             this.collapsedEl.dom.style.top = box.y + "px";
30093             this.collapsedEl.setSize(box.width, box.height);
30094         }
30095         if(this.tabs){
30096             this.tabs.autoSizeTabs();
30097         }
30098     },
30099
30100     updateBody : function(w, h){
30101         if(w !== null){
30102             this.el.setWidth(w);
30103             w -= this.el.getBorderWidth("rl");
30104             if(this.config.adjustments){
30105                 w += this.config.adjustments[0];
30106             }
30107         }
30108         if(h !== null){
30109             this.el.setHeight(h);
30110             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30111             h -= this.el.getBorderWidth("tb");
30112             if(this.config.adjustments){
30113                 h += this.config.adjustments[1];
30114             }
30115             this.bodyEl.setHeight(h);
30116             if(this.tabs){
30117                 h = this.tabs.syncHeight(h);
30118             }
30119         }
30120         if(this.panelSize){
30121             w = w !== null ? w : this.panelSize.width;
30122             h = h !== null ? h : this.panelSize.height;
30123         }
30124         if(this.activePanel){
30125             var el = this.activePanel.getEl();
30126             w = w !== null ? w : el.getWidth();
30127             h = h !== null ? h : el.getHeight();
30128             this.panelSize = {width: w, height: h};
30129             this.activePanel.setSize(w, h);
30130         }
30131         if(Roo.isIE && this.tabs){
30132             this.tabs.el.repaint();
30133         }
30134     },
30135
30136     /**
30137      * Returns the container element for this region.
30138      * @return {Roo.Element}
30139      */
30140     getEl : function(){
30141         return this.el;
30142     },
30143
30144     /**
30145      * Hides this region.
30146      */
30147     hide : function(){
30148         if(!this.collapsed){
30149             this.el.dom.style.left = "-2000px";
30150             this.el.hide();
30151         }else{
30152             this.collapsedEl.dom.style.left = "-2000px";
30153             this.collapsedEl.hide();
30154         }
30155         this.visible = false;
30156         this.fireEvent("visibilitychange", this, false);
30157     },
30158
30159     /**
30160      * Shows this region if it was previously hidden.
30161      */
30162     show : function(){
30163         if(!this.collapsed){
30164             this.el.show();
30165         }else{
30166             this.collapsedEl.show();
30167         }
30168         this.visible = true;
30169         this.fireEvent("visibilitychange", this, true);
30170     },
30171
30172     closeClicked : function(){
30173         if(this.activePanel){
30174             this.remove(this.activePanel);
30175         }
30176     },
30177
30178     collapseClick : function(e){
30179         if(this.isSlid){
30180            e.stopPropagation();
30181            this.slideIn();
30182         }else{
30183            e.stopPropagation();
30184            this.slideOut();
30185         }
30186     },
30187
30188     /**
30189      * Collapses this region.
30190      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30191      */
30192     collapse : function(skipAnim, skipCheck){
30193         if(this.collapsed) {
30194             return;
30195         }
30196         
30197         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30198             
30199             this.collapsed = true;
30200             if(this.split){
30201                 this.split.el.hide();
30202             }
30203             if(this.config.animate && skipAnim !== true){
30204                 this.fireEvent("invalidated", this);
30205                 this.animateCollapse();
30206             }else{
30207                 this.el.setLocation(-20000,-20000);
30208                 this.el.hide();
30209                 this.collapsedEl.show();
30210                 this.fireEvent("collapsed", this);
30211                 this.fireEvent("invalidated", this);
30212             }
30213         }
30214         
30215     },
30216
30217     animateCollapse : function(){
30218         // overridden
30219     },
30220
30221     /**
30222      * Expands this region if it was previously collapsed.
30223      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30224      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30225      */
30226     expand : function(e, skipAnim){
30227         if(e) {
30228             e.stopPropagation();
30229         }
30230         if(!this.collapsed || this.el.hasActiveFx()) {
30231             return;
30232         }
30233         if(this.isSlid){
30234             this.afterSlideIn();
30235             skipAnim = true;
30236         }
30237         this.collapsed = false;
30238         if(this.config.animate && skipAnim !== true){
30239             this.animateExpand();
30240         }else{
30241             this.el.show();
30242             if(this.split){
30243                 this.split.el.show();
30244             }
30245             this.collapsedEl.setLocation(-2000,-2000);
30246             this.collapsedEl.hide();
30247             this.fireEvent("invalidated", this);
30248             this.fireEvent("expanded", this);
30249         }
30250     },
30251
30252     animateExpand : function(){
30253         // overridden
30254     },
30255
30256     initTabs : function()
30257     {
30258         this.bodyEl.setStyle("overflow", "hidden");
30259         var ts = new Roo.TabPanel(
30260                 this.bodyEl.dom,
30261                 {
30262                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30263                     disableTooltips: this.config.disableTabTips,
30264                     toolbar : this.config.toolbar
30265                 }
30266         );
30267         if(this.config.hideTabs){
30268             ts.stripWrap.setDisplayed(false);
30269         }
30270         this.tabs = ts;
30271         ts.resizeTabs = this.config.resizeTabs === true;
30272         ts.minTabWidth = this.config.minTabWidth || 40;
30273         ts.maxTabWidth = this.config.maxTabWidth || 250;
30274         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30275         ts.monitorResize = false;
30276         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30277         ts.bodyEl.addClass('x-layout-tabs-body');
30278         this.panels.each(this.initPanelAsTab, this);
30279     },
30280
30281     initPanelAsTab : function(panel){
30282         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30283                     this.config.closeOnTab && panel.isClosable());
30284         if(panel.tabTip !== undefined){
30285             ti.setTooltip(panel.tabTip);
30286         }
30287         ti.on("activate", function(){
30288               this.setActivePanel(panel);
30289         }, this);
30290         if(this.config.closeOnTab){
30291             ti.on("beforeclose", function(t, e){
30292                 e.cancel = true;
30293                 this.remove(panel);
30294             }, this);
30295         }
30296         return ti;
30297     },
30298
30299     updatePanelTitle : function(panel, title){
30300         if(this.activePanel == panel){
30301             this.updateTitle(title);
30302         }
30303         if(this.tabs){
30304             var ti = this.tabs.getTab(panel.getEl().id);
30305             ti.setText(title);
30306             if(panel.tabTip !== undefined){
30307                 ti.setTooltip(panel.tabTip);
30308             }
30309         }
30310     },
30311
30312     updateTitle : function(title){
30313         if(this.titleTextEl && !this.config.title){
30314             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30315         }
30316     },
30317
30318     setActivePanel : function(panel){
30319         panel = this.getPanel(panel);
30320         if(this.activePanel && this.activePanel != panel){
30321             this.activePanel.setActiveState(false);
30322         }
30323         this.activePanel = panel;
30324         panel.setActiveState(true);
30325         if(this.panelSize){
30326             panel.setSize(this.panelSize.width, this.panelSize.height);
30327         }
30328         if(this.closeBtn){
30329             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30330         }
30331         this.updateTitle(panel.getTitle());
30332         if(this.tabs){
30333             this.fireEvent("invalidated", this);
30334         }
30335         this.fireEvent("panelactivated", this, panel);
30336     },
30337
30338     /**
30339      * Shows the specified panel.
30340      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30341      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30342      */
30343     showPanel : function(panel)
30344     {
30345         panel = this.getPanel(panel);
30346         if(panel){
30347             if(this.tabs){
30348                 var tab = this.tabs.getTab(panel.getEl().id);
30349                 if(tab.isHidden()){
30350                     this.tabs.unhideTab(tab.id);
30351                 }
30352                 tab.activate();
30353             }else{
30354                 this.setActivePanel(panel);
30355             }
30356         }
30357         return panel;
30358     },
30359
30360     /**
30361      * Get the active panel for this region.
30362      * @return {Roo.ContentPanel} The active panel or null
30363      */
30364     getActivePanel : function(){
30365         return this.activePanel;
30366     },
30367
30368     validateVisibility : function(){
30369         if(this.panels.getCount() < 1){
30370             this.updateTitle("&#160;");
30371             this.closeBtn.hide();
30372             this.hide();
30373         }else{
30374             if(!this.isVisible()){
30375                 this.show();
30376             }
30377         }
30378     },
30379
30380     /**
30381      * Adds the passed ContentPanel(s) to this region.
30382      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30383      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30384      */
30385     add : function(panel){
30386         if(arguments.length > 1){
30387             for(var i = 0, len = arguments.length; i < len; i++) {
30388                 this.add(arguments[i]);
30389             }
30390             return null;
30391         }
30392         if(this.hasPanel(panel)){
30393             this.showPanel(panel);
30394             return panel;
30395         }
30396         panel.setRegion(this);
30397         this.panels.add(panel);
30398         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30399             this.bodyEl.dom.appendChild(panel.getEl().dom);
30400             if(panel.background !== true){
30401                 this.setActivePanel(panel);
30402             }
30403             this.fireEvent("paneladded", this, panel);
30404             return panel;
30405         }
30406         if(!this.tabs){
30407             this.initTabs();
30408         }else{
30409             this.initPanelAsTab(panel);
30410         }
30411         if(panel.background !== true){
30412             this.tabs.activate(panel.getEl().id);
30413         }
30414         this.fireEvent("paneladded", this, panel);
30415         return panel;
30416     },
30417
30418     /**
30419      * Hides the tab for the specified panel.
30420      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30421      */
30422     hidePanel : function(panel){
30423         if(this.tabs && (panel = this.getPanel(panel))){
30424             this.tabs.hideTab(panel.getEl().id);
30425         }
30426     },
30427
30428     /**
30429      * Unhides the tab for a previously hidden panel.
30430      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30431      */
30432     unhidePanel : function(panel){
30433         if(this.tabs && (panel = this.getPanel(panel))){
30434             this.tabs.unhideTab(panel.getEl().id);
30435         }
30436     },
30437
30438     clearPanels : function(){
30439         while(this.panels.getCount() > 0){
30440              this.remove(this.panels.first());
30441         }
30442     },
30443
30444     /**
30445      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30446      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30447      * @param {Boolean} preservePanel Overrides the config preservePanel option
30448      * @return {Roo.ContentPanel} The panel that was removed
30449      */
30450     remove : function(panel, preservePanel){
30451         panel = this.getPanel(panel);
30452         if(!panel){
30453             return null;
30454         }
30455         var e = {};
30456         this.fireEvent("beforeremove", this, panel, e);
30457         if(e.cancel === true){
30458             return null;
30459         }
30460         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30461         var panelId = panel.getId();
30462         this.panels.removeKey(panelId);
30463         if(preservePanel){
30464             document.body.appendChild(panel.getEl().dom);
30465         }
30466         if(this.tabs){
30467             this.tabs.removeTab(panel.getEl().id);
30468         }else if (!preservePanel){
30469             this.bodyEl.dom.removeChild(panel.getEl().dom);
30470         }
30471         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30472             var p = this.panels.first();
30473             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30474             tempEl.appendChild(p.getEl().dom);
30475             this.bodyEl.update("");
30476             this.bodyEl.dom.appendChild(p.getEl().dom);
30477             tempEl = null;
30478             this.updateTitle(p.getTitle());
30479             this.tabs = null;
30480             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30481             this.setActivePanel(p);
30482         }
30483         panel.setRegion(null);
30484         if(this.activePanel == panel){
30485             this.activePanel = null;
30486         }
30487         if(this.config.autoDestroy !== false && preservePanel !== true){
30488             try{panel.destroy();}catch(e){}
30489         }
30490         this.fireEvent("panelremoved", this, panel);
30491         return panel;
30492     },
30493
30494     /**
30495      * Returns the TabPanel component used by this region
30496      * @return {Roo.TabPanel}
30497      */
30498     getTabs : function(){
30499         return this.tabs;
30500     },
30501
30502     createTool : function(parentEl, className){
30503         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30504             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30505         btn.addClassOnOver("x-layout-tools-button-over");
30506         return btn;
30507     }
30508 });/*
30509  * Based on:
30510  * Ext JS Library 1.1.1
30511  * Copyright(c) 2006-2007, Ext JS, LLC.
30512  *
30513  * Originally Released Under LGPL - original licence link has changed is not relivant.
30514  *
30515  * Fork - LGPL
30516  * <script type="text/javascript">
30517  */
30518  
30519
30520
30521 /**
30522  * @class Roo.SplitLayoutRegion
30523  * @extends Roo.LayoutRegion
30524  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30525  */
30526 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30527     this.cursor = cursor;
30528     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30529 };
30530
30531 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30532     splitTip : "Drag to resize.",
30533     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30534     useSplitTips : false,
30535
30536     applyConfig : function(config){
30537         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30538         if(config.split){
30539             if(!this.split){
30540                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30541                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30542                 /** The SplitBar for this region 
30543                 * @type Roo.SplitBar */
30544                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30545                 this.split.on("moved", this.onSplitMove, this);
30546                 this.split.useShim = config.useShim === true;
30547                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30548                 if(this.useSplitTips){
30549                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30550                 }
30551                 if(config.collapsible){
30552                     this.split.el.on("dblclick", this.collapse,  this);
30553                 }
30554             }
30555             if(typeof config.minSize != "undefined"){
30556                 this.split.minSize = config.minSize;
30557             }
30558             if(typeof config.maxSize != "undefined"){
30559                 this.split.maxSize = config.maxSize;
30560             }
30561             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30562                 this.hideSplitter();
30563             }
30564         }
30565     },
30566
30567     getHMaxSize : function(){
30568          var cmax = this.config.maxSize || 10000;
30569          var center = this.mgr.getRegion("center");
30570          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30571     },
30572
30573     getVMaxSize : function(){
30574          var cmax = this.config.maxSize || 10000;
30575          var center = this.mgr.getRegion("center");
30576          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30577     },
30578
30579     onSplitMove : function(split, newSize){
30580         this.fireEvent("resized", this, newSize);
30581     },
30582     
30583     /** 
30584      * Returns the {@link Roo.SplitBar} for this region.
30585      * @return {Roo.SplitBar}
30586      */
30587     getSplitBar : function(){
30588         return this.split;
30589     },
30590     
30591     hide : function(){
30592         this.hideSplitter();
30593         Roo.SplitLayoutRegion.superclass.hide.call(this);
30594     },
30595
30596     hideSplitter : function(){
30597         if(this.split){
30598             this.split.el.setLocation(-2000,-2000);
30599             this.split.el.hide();
30600         }
30601     },
30602
30603     show : function(){
30604         if(this.split){
30605             this.split.el.show();
30606         }
30607         Roo.SplitLayoutRegion.superclass.show.call(this);
30608     },
30609     
30610     beforeSlide: function(){
30611         if(Roo.isGecko){// firefox overflow auto bug workaround
30612             this.bodyEl.clip();
30613             if(this.tabs) {
30614                 this.tabs.bodyEl.clip();
30615             }
30616             if(this.activePanel){
30617                 this.activePanel.getEl().clip();
30618                 
30619                 if(this.activePanel.beforeSlide){
30620                     this.activePanel.beforeSlide();
30621                 }
30622             }
30623         }
30624     },
30625     
30626     afterSlide : function(){
30627         if(Roo.isGecko){// firefox overflow auto bug workaround
30628             this.bodyEl.unclip();
30629             if(this.tabs) {
30630                 this.tabs.bodyEl.unclip();
30631             }
30632             if(this.activePanel){
30633                 this.activePanel.getEl().unclip();
30634                 if(this.activePanel.afterSlide){
30635                     this.activePanel.afterSlide();
30636                 }
30637             }
30638         }
30639     },
30640
30641     initAutoHide : function(){
30642         if(this.autoHide !== false){
30643             if(!this.autoHideHd){
30644                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30645                 this.autoHideHd = {
30646                     "mouseout": function(e){
30647                         if(!e.within(this.el, true)){
30648                             st.delay(500);
30649                         }
30650                     },
30651                     "mouseover" : function(e){
30652                         st.cancel();
30653                     },
30654                     scope : this
30655                 };
30656             }
30657             this.el.on(this.autoHideHd);
30658         }
30659     },
30660
30661     clearAutoHide : function(){
30662         if(this.autoHide !== false){
30663             this.el.un("mouseout", this.autoHideHd.mouseout);
30664             this.el.un("mouseover", this.autoHideHd.mouseover);
30665         }
30666     },
30667
30668     clearMonitor : function(){
30669         Roo.get(document).un("click", this.slideInIf, this);
30670     },
30671
30672     // these names are backwards but not changed for compat
30673     slideOut : function(){
30674         if(this.isSlid || this.el.hasActiveFx()){
30675             return;
30676         }
30677         this.isSlid = true;
30678         if(this.collapseBtn){
30679             this.collapseBtn.hide();
30680         }
30681         this.closeBtnState = this.closeBtn.getStyle('display');
30682         this.closeBtn.hide();
30683         if(this.stickBtn){
30684             this.stickBtn.show();
30685         }
30686         this.el.show();
30687         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30688         this.beforeSlide();
30689         this.el.setStyle("z-index", 10001);
30690         this.el.slideIn(this.getSlideAnchor(), {
30691             callback: function(){
30692                 this.afterSlide();
30693                 this.initAutoHide();
30694                 Roo.get(document).on("click", this.slideInIf, this);
30695                 this.fireEvent("slideshow", this);
30696             },
30697             scope: this,
30698             block: true
30699         });
30700     },
30701
30702     afterSlideIn : function(){
30703         this.clearAutoHide();
30704         this.isSlid = false;
30705         this.clearMonitor();
30706         this.el.setStyle("z-index", "");
30707         if(this.collapseBtn){
30708             this.collapseBtn.show();
30709         }
30710         this.closeBtn.setStyle('display', this.closeBtnState);
30711         if(this.stickBtn){
30712             this.stickBtn.hide();
30713         }
30714         this.fireEvent("slidehide", this);
30715     },
30716
30717     slideIn : function(cb){
30718         if(!this.isSlid || this.el.hasActiveFx()){
30719             Roo.callback(cb);
30720             return;
30721         }
30722         this.isSlid = false;
30723         this.beforeSlide();
30724         this.el.slideOut(this.getSlideAnchor(), {
30725             callback: function(){
30726                 this.el.setLeftTop(-10000, -10000);
30727                 this.afterSlide();
30728                 this.afterSlideIn();
30729                 Roo.callback(cb);
30730             },
30731             scope: this,
30732             block: true
30733         });
30734     },
30735     
30736     slideInIf : function(e){
30737         if(!e.within(this.el)){
30738             this.slideIn();
30739         }
30740     },
30741
30742     animateCollapse : function(){
30743         this.beforeSlide();
30744         this.el.setStyle("z-index", 20000);
30745         var anchor = this.getSlideAnchor();
30746         this.el.slideOut(anchor, {
30747             callback : function(){
30748                 this.el.setStyle("z-index", "");
30749                 this.collapsedEl.slideIn(anchor, {duration:.3});
30750                 this.afterSlide();
30751                 this.el.setLocation(-10000,-10000);
30752                 this.el.hide();
30753                 this.fireEvent("collapsed", this);
30754             },
30755             scope: this,
30756             block: true
30757         });
30758     },
30759
30760     animateExpand : function(){
30761         this.beforeSlide();
30762         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30763         this.el.setStyle("z-index", 20000);
30764         this.collapsedEl.hide({
30765             duration:.1
30766         });
30767         this.el.slideIn(this.getSlideAnchor(), {
30768             callback : function(){
30769                 this.el.setStyle("z-index", "");
30770                 this.afterSlide();
30771                 if(this.split){
30772                     this.split.el.show();
30773                 }
30774                 this.fireEvent("invalidated", this);
30775                 this.fireEvent("expanded", this);
30776             },
30777             scope: this,
30778             block: true
30779         });
30780     },
30781
30782     anchors : {
30783         "west" : "left",
30784         "east" : "right",
30785         "north" : "top",
30786         "south" : "bottom"
30787     },
30788
30789     sanchors : {
30790         "west" : "l",
30791         "east" : "r",
30792         "north" : "t",
30793         "south" : "b"
30794     },
30795
30796     canchors : {
30797         "west" : "tl-tr",
30798         "east" : "tr-tl",
30799         "north" : "tl-bl",
30800         "south" : "bl-tl"
30801     },
30802
30803     getAnchor : function(){
30804         return this.anchors[this.position];
30805     },
30806
30807     getCollapseAnchor : function(){
30808         return this.canchors[this.position];
30809     },
30810
30811     getSlideAnchor : function(){
30812         return this.sanchors[this.position];
30813     },
30814
30815     getAlignAdj : function(){
30816         var cm = this.cmargins;
30817         switch(this.position){
30818             case "west":
30819                 return [0, 0];
30820             break;
30821             case "east":
30822                 return [0, 0];
30823             break;
30824             case "north":
30825                 return [0, 0];
30826             break;
30827             case "south":
30828                 return [0, 0];
30829             break;
30830         }
30831     },
30832
30833     getExpandAdj : function(){
30834         var c = this.collapsedEl, cm = this.cmargins;
30835         switch(this.position){
30836             case "west":
30837                 return [-(cm.right+c.getWidth()+cm.left), 0];
30838             break;
30839             case "east":
30840                 return [cm.right+c.getWidth()+cm.left, 0];
30841             break;
30842             case "north":
30843                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30844             break;
30845             case "south":
30846                 return [0, cm.top+cm.bottom+c.getHeight()];
30847             break;
30848         }
30849     }
30850 });/*
30851  * Based on:
30852  * Ext JS Library 1.1.1
30853  * Copyright(c) 2006-2007, Ext JS, LLC.
30854  *
30855  * Originally Released Under LGPL - original licence link has changed is not relivant.
30856  *
30857  * Fork - LGPL
30858  * <script type="text/javascript">
30859  */
30860 /*
30861  * These classes are private internal classes
30862  */
30863 Roo.CenterLayoutRegion = function(mgr, config){
30864     Roo.LayoutRegion.call(this, mgr, config, "center");
30865     this.visible = true;
30866     this.minWidth = config.minWidth || 20;
30867     this.minHeight = config.minHeight || 20;
30868 };
30869
30870 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30871     hide : function(){
30872         // center panel can't be hidden
30873     },
30874     
30875     show : function(){
30876         // center panel can't be hidden
30877     },
30878     
30879     getMinWidth: function(){
30880         return this.minWidth;
30881     },
30882     
30883     getMinHeight: function(){
30884         return this.minHeight;
30885     }
30886 });
30887
30888
30889 Roo.NorthLayoutRegion = function(mgr, config){
30890     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30891     if(this.split){
30892         this.split.placement = Roo.SplitBar.TOP;
30893         this.split.orientation = Roo.SplitBar.VERTICAL;
30894         this.split.el.addClass("x-layout-split-v");
30895     }
30896     var size = config.initialSize || config.height;
30897     if(typeof size != "undefined"){
30898         this.el.setHeight(size);
30899     }
30900 };
30901 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30902     orientation: Roo.SplitBar.VERTICAL,
30903     getBox : function(){
30904         if(this.collapsed){
30905             return this.collapsedEl.getBox();
30906         }
30907         var box = this.el.getBox();
30908         if(this.split){
30909             box.height += this.split.el.getHeight();
30910         }
30911         return box;
30912     },
30913     
30914     updateBox : function(box){
30915         if(this.split && !this.collapsed){
30916             box.height -= this.split.el.getHeight();
30917             this.split.el.setLeft(box.x);
30918             this.split.el.setTop(box.y+box.height);
30919             this.split.el.setWidth(box.width);
30920         }
30921         if(this.collapsed){
30922             this.updateBody(box.width, null);
30923         }
30924         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30925     }
30926 });
30927
30928 Roo.SouthLayoutRegion = function(mgr, config){
30929     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30930     if(this.split){
30931         this.split.placement = Roo.SplitBar.BOTTOM;
30932         this.split.orientation = Roo.SplitBar.VERTICAL;
30933         this.split.el.addClass("x-layout-split-v");
30934     }
30935     var size = config.initialSize || config.height;
30936     if(typeof size != "undefined"){
30937         this.el.setHeight(size);
30938     }
30939 };
30940 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30941     orientation: Roo.SplitBar.VERTICAL,
30942     getBox : function(){
30943         if(this.collapsed){
30944             return this.collapsedEl.getBox();
30945         }
30946         var box = this.el.getBox();
30947         if(this.split){
30948             var sh = this.split.el.getHeight();
30949             box.height += sh;
30950             box.y -= sh;
30951         }
30952         return box;
30953     },
30954     
30955     updateBox : function(box){
30956         if(this.split && !this.collapsed){
30957             var sh = this.split.el.getHeight();
30958             box.height -= sh;
30959             box.y += sh;
30960             this.split.el.setLeft(box.x);
30961             this.split.el.setTop(box.y-sh);
30962             this.split.el.setWidth(box.width);
30963         }
30964         if(this.collapsed){
30965             this.updateBody(box.width, null);
30966         }
30967         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30968     }
30969 });
30970
30971 Roo.EastLayoutRegion = function(mgr, config){
30972     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30973     if(this.split){
30974         this.split.placement = Roo.SplitBar.RIGHT;
30975         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30976         this.split.el.addClass("x-layout-split-h");
30977     }
30978     var size = config.initialSize || config.width;
30979     if(typeof size != "undefined"){
30980         this.el.setWidth(size);
30981     }
30982 };
30983 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30984     orientation: Roo.SplitBar.HORIZONTAL,
30985     getBox : function(){
30986         if(this.collapsed){
30987             return this.collapsedEl.getBox();
30988         }
30989         var box = this.el.getBox();
30990         if(this.split){
30991             var sw = this.split.el.getWidth();
30992             box.width += sw;
30993             box.x -= sw;
30994         }
30995         return box;
30996     },
30997
30998     updateBox : function(box){
30999         if(this.split && !this.collapsed){
31000             var sw = this.split.el.getWidth();
31001             box.width -= sw;
31002             this.split.el.setLeft(box.x);
31003             this.split.el.setTop(box.y);
31004             this.split.el.setHeight(box.height);
31005             box.x += sw;
31006         }
31007         if(this.collapsed){
31008             this.updateBody(null, box.height);
31009         }
31010         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31011     }
31012 });
31013
31014 Roo.WestLayoutRegion = function(mgr, config){
31015     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31016     if(this.split){
31017         this.split.placement = Roo.SplitBar.LEFT;
31018         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31019         this.split.el.addClass("x-layout-split-h");
31020     }
31021     var size = config.initialSize || config.width;
31022     if(typeof size != "undefined"){
31023         this.el.setWidth(size);
31024     }
31025 };
31026 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31027     orientation: Roo.SplitBar.HORIZONTAL,
31028     getBox : function(){
31029         if(this.collapsed){
31030             return this.collapsedEl.getBox();
31031         }
31032         var box = this.el.getBox();
31033         if(this.split){
31034             box.width += this.split.el.getWidth();
31035         }
31036         return box;
31037     },
31038     
31039     updateBox : function(box){
31040         if(this.split && !this.collapsed){
31041             var sw = this.split.el.getWidth();
31042             box.width -= sw;
31043             this.split.el.setLeft(box.x+box.width);
31044             this.split.el.setTop(box.y);
31045             this.split.el.setHeight(box.height);
31046         }
31047         if(this.collapsed){
31048             this.updateBody(null, box.height);
31049         }
31050         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31051     }
31052 });
31053 /*
31054  * Based on:
31055  * Ext JS Library 1.1.1
31056  * Copyright(c) 2006-2007, Ext JS, LLC.
31057  *
31058  * Originally Released Under LGPL - original licence link has changed is not relivant.
31059  *
31060  * Fork - LGPL
31061  * <script type="text/javascript">
31062  */
31063  
31064  
31065 /*
31066  * Private internal class for reading and applying state
31067  */
31068 Roo.LayoutStateManager = function(layout){
31069      // default empty state
31070      this.state = {
31071         north: {},
31072         south: {},
31073         east: {},
31074         west: {}       
31075     };
31076 };
31077
31078 Roo.LayoutStateManager.prototype = {
31079     init : function(layout, provider){
31080         this.provider = provider;
31081         var state = provider.get(layout.id+"-layout-state");
31082         if(state){
31083             var wasUpdating = layout.isUpdating();
31084             if(!wasUpdating){
31085                 layout.beginUpdate();
31086             }
31087             for(var key in state){
31088                 if(typeof state[key] != "function"){
31089                     var rstate = state[key];
31090                     var r = layout.getRegion(key);
31091                     if(r && rstate){
31092                         if(rstate.size){
31093                             r.resizeTo(rstate.size);
31094                         }
31095                         if(rstate.collapsed == true){
31096                             r.collapse(true);
31097                         }else{
31098                             r.expand(null, true);
31099                         }
31100                     }
31101                 }
31102             }
31103             if(!wasUpdating){
31104                 layout.endUpdate();
31105             }
31106             this.state = state; 
31107         }
31108         this.layout = layout;
31109         layout.on("regionresized", this.onRegionResized, this);
31110         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31111         layout.on("regionexpanded", this.onRegionExpanded, this);
31112     },
31113     
31114     storeState : function(){
31115         this.provider.set(this.layout.id+"-layout-state", this.state);
31116     },
31117     
31118     onRegionResized : function(region, newSize){
31119         this.state[region.getPosition()].size = newSize;
31120         this.storeState();
31121     },
31122     
31123     onRegionCollapsed : function(region){
31124         this.state[region.getPosition()].collapsed = true;
31125         this.storeState();
31126     },
31127     
31128     onRegionExpanded : function(region){
31129         this.state[region.getPosition()].collapsed = false;
31130         this.storeState();
31131     }
31132 };/*
31133  * Based on:
31134  * Ext JS Library 1.1.1
31135  * Copyright(c) 2006-2007, Ext JS, LLC.
31136  *
31137  * Originally Released Under LGPL - original licence link has changed is not relivant.
31138  *
31139  * Fork - LGPL
31140  * <script type="text/javascript">
31141  */
31142 /**
31143  * @class Roo.ContentPanel
31144  * @extends Roo.util.Observable
31145  * A basic ContentPanel element.
31146  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31147  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31148  * @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
31149  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31150  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31151  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31152  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31153  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31154  * @cfg {String} title          The title for this panel
31155  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31156  * @cfg {String} url            Calls {@link #setUrl} with this value
31157  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31158  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31159  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31160  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31161
31162  * @constructor
31163  * Create a new ContentPanel.
31164  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31165  * @param {String/Object} config A string to set only the title or a config object
31166  * @param {String} content (optional) Set the HTML content for this panel
31167  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31168  */
31169 Roo.ContentPanel = function(el, config, content){
31170     
31171      
31172     /*
31173     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31174         config = el;
31175         el = Roo.id();
31176     }
31177     if (config && config.parentLayout) { 
31178         el = config.parentLayout.el.createChild(); 
31179     }
31180     */
31181     if(el.autoCreate){ // xtype is available if this is called from factory
31182         config = el;
31183         el = Roo.id();
31184     }
31185     this.el = Roo.get(el);
31186     if(!this.el && config && config.autoCreate){
31187         if(typeof config.autoCreate == "object"){
31188             if(!config.autoCreate.id){
31189                 config.autoCreate.id = config.id||el;
31190             }
31191             this.el = Roo.DomHelper.append(document.body,
31192                         config.autoCreate, true);
31193         }else{
31194             this.el = Roo.DomHelper.append(document.body,
31195                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31196         }
31197     }
31198     this.closable = false;
31199     this.loaded = false;
31200     this.active = false;
31201     if(typeof config == "string"){
31202         this.title = config;
31203     }else{
31204         Roo.apply(this, config);
31205     }
31206     
31207     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31208         this.wrapEl = this.el.wrap();
31209         this.toolbar.container = this.el.insertSibling(false, 'before');
31210         this.toolbar = new Roo.Toolbar(this.toolbar);
31211     }
31212     
31213     // xtype created footer. - not sure if will work as we normally have to render first..
31214     if (this.footer && !this.footer.el && this.footer.xtype) {
31215         if (!this.wrapEl) {
31216             this.wrapEl = this.el.wrap();
31217         }
31218     
31219         this.footer.container = this.wrapEl.createChild();
31220          
31221         this.footer = Roo.factory(this.footer, Roo);
31222         
31223     }
31224     
31225     if(this.resizeEl){
31226         this.resizeEl = Roo.get(this.resizeEl, true);
31227     }else{
31228         this.resizeEl = this.el;
31229     }
31230     // handle view.xtype
31231     
31232  
31233     
31234     
31235     this.addEvents({
31236         /**
31237          * @event activate
31238          * Fires when this panel is activated. 
31239          * @param {Roo.ContentPanel} this
31240          */
31241         "activate" : true,
31242         /**
31243          * @event deactivate
31244          * Fires when this panel is activated. 
31245          * @param {Roo.ContentPanel} this
31246          */
31247         "deactivate" : true,
31248
31249         /**
31250          * @event resize
31251          * Fires when this panel is resized if fitToFrame is true.
31252          * @param {Roo.ContentPanel} this
31253          * @param {Number} width The width after any component adjustments
31254          * @param {Number} height The height after any component adjustments
31255          */
31256         "resize" : true,
31257         
31258          /**
31259          * @event render
31260          * Fires when this tab is created
31261          * @param {Roo.ContentPanel} this
31262          */
31263         "render" : true
31264          
31265         
31266     });
31267     
31268
31269     
31270     
31271     if(this.autoScroll){
31272         this.resizeEl.setStyle("overflow", "auto");
31273     } else {
31274         // fix randome scrolling
31275         this.el.on('scroll', function() {
31276             Roo.log('fix random scolling');
31277             this.scrollTo('top',0); 
31278         });
31279     }
31280     content = content || this.content;
31281     if(content){
31282         this.setContent(content);
31283     }
31284     if(config && config.url){
31285         this.setUrl(this.url, this.params, this.loadOnce);
31286     }
31287     
31288     
31289     
31290     Roo.ContentPanel.superclass.constructor.call(this);
31291     
31292     if (this.view && typeof(this.view.xtype) != 'undefined') {
31293         this.view.el = this.el.appendChild(document.createElement("div"));
31294         this.view = Roo.factory(this.view); 
31295         this.view.render  &&  this.view.render(false, '');  
31296     }
31297     
31298     
31299     this.fireEvent('render', this);
31300 };
31301
31302 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31303     tabTip:'',
31304     setRegion : function(region){
31305         this.region = region;
31306         if(region){
31307            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31308         }else{
31309            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31310         } 
31311     },
31312     
31313     /**
31314      * Returns the toolbar for this Panel if one was configured. 
31315      * @return {Roo.Toolbar} 
31316      */
31317     getToolbar : function(){
31318         return this.toolbar;
31319     },
31320     
31321     setActiveState : function(active){
31322         this.active = active;
31323         if(!active){
31324             this.fireEvent("deactivate", this);
31325         }else{
31326             this.fireEvent("activate", this);
31327         }
31328     },
31329     /**
31330      * Updates this panel's element
31331      * @param {String} content The new content
31332      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31333     */
31334     setContent : function(content, loadScripts){
31335         this.el.update(content, loadScripts);
31336     },
31337
31338     ignoreResize : function(w, h){
31339         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31340             return true;
31341         }else{
31342             this.lastSize = {width: w, height: h};
31343             return false;
31344         }
31345     },
31346     /**
31347      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31348      * @return {Roo.UpdateManager} The UpdateManager
31349      */
31350     getUpdateManager : function(){
31351         return this.el.getUpdateManager();
31352     },
31353      /**
31354      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31355      * @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:
31356 <pre><code>
31357 panel.load({
31358     url: "your-url.php",
31359     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31360     callback: yourFunction,
31361     scope: yourObject, //(optional scope)
31362     discardUrl: false,
31363     nocache: false,
31364     text: "Loading...",
31365     timeout: 30,
31366     scripts: false
31367 });
31368 </code></pre>
31369      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31370      * 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.
31371      * @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}
31372      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31373      * @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.
31374      * @return {Roo.ContentPanel} this
31375      */
31376     load : function(){
31377         var um = this.el.getUpdateManager();
31378         um.update.apply(um, arguments);
31379         return this;
31380     },
31381
31382
31383     /**
31384      * 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.
31385      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31386      * @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)
31387      * @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)
31388      * @return {Roo.UpdateManager} The UpdateManager
31389      */
31390     setUrl : function(url, params, loadOnce){
31391         if(this.refreshDelegate){
31392             this.removeListener("activate", this.refreshDelegate);
31393         }
31394         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31395         this.on("activate", this.refreshDelegate);
31396         return this.el.getUpdateManager();
31397     },
31398     
31399     _handleRefresh : function(url, params, loadOnce){
31400         if(!loadOnce || !this.loaded){
31401             var updater = this.el.getUpdateManager();
31402             updater.update(url, params, this._setLoaded.createDelegate(this));
31403         }
31404     },
31405     
31406     _setLoaded : function(){
31407         this.loaded = true;
31408     }, 
31409     
31410     /**
31411      * Returns this panel's id
31412      * @return {String} 
31413      */
31414     getId : function(){
31415         return this.el.id;
31416     },
31417     
31418     /** 
31419      * Returns this panel's element - used by regiosn to add.
31420      * @return {Roo.Element} 
31421      */
31422     getEl : function(){
31423         return this.wrapEl || this.el;
31424     },
31425     
31426     adjustForComponents : function(width, height)
31427     {
31428         //Roo.log('adjustForComponents ');
31429         if(this.resizeEl != this.el){
31430             width -= this.el.getFrameWidth('lr');
31431             height -= this.el.getFrameWidth('tb');
31432         }
31433         if(this.toolbar){
31434             var te = this.toolbar.getEl();
31435             height -= te.getHeight();
31436             te.setWidth(width);
31437         }
31438         if(this.footer){
31439             var te = this.footer.getEl();
31440             //Roo.log("footer:" + te.getHeight());
31441             
31442             height -= te.getHeight();
31443             te.setWidth(width);
31444         }
31445         
31446         
31447         if(this.adjustments){
31448             width += this.adjustments[0];
31449             height += this.adjustments[1];
31450         }
31451         return {"width": width, "height": height};
31452     },
31453     
31454     setSize : function(width, height){
31455         if(this.fitToFrame && !this.ignoreResize(width, height)){
31456             if(this.fitContainer && this.resizeEl != this.el){
31457                 this.el.setSize(width, height);
31458             }
31459             var size = this.adjustForComponents(width, height);
31460             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31461             this.fireEvent('resize', this, size.width, size.height);
31462         }
31463     },
31464     
31465     /**
31466      * Returns this panel's title
31467      * @return {String} 
31468      */
31469     getTitle : function(){
31470         return this.title;
31471     },
31472     
31473     /**
31474      * Set this panel's title
31475      * @param {String} title
31476      */
31477     setTitle : function(title){
31478         this.title = title;
31479         if(this.region){
31480             this.region.updatePanelTitle(this, title);
31481         }
31482     },
31483     
31484     /**
31485      * Returns true is this panel was configured to be closable
31486      * @return {Boolean} 
31487      */
31488     isClosable : function(){
31489         return this.closable;
31490     },
31491     
31492     beforeSlide : function(){
31493         this.el.clip();
31494         this.resizeEl.clip();
31495     },
31496     
31497     afterSlide : function(){
31498         this.el.unclip();
31499         this.resizeEl.unclip();
31500     },
31501     
31502     /**
31503      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31504      *   Will fail silently if the {@link #setUrl} method has not been called.
31505      *   This does not activate the panel, just updates its content.
31506      */
31507     refresh : function(){
31508         if(this.refreshDelegate){
31509            this.loaded = false;
31510            this.refreshDelegate();
31511         }
31512     },
31513     
31514     /**
31515      * Destroys this panel
31516      */
31517     destroy : function(){
31518         this.el.removeAllListeners();
31519         var tempEl = document.createElement("span");
31520         tempEl.appendChild(this.el.dom);
31521         tempEl.innerHTML = "";
31522         this.el.remove();
31523         this.el = null;
31524     },
31525     
31526     /**
31527      * form - if the content panel contains a form - this is a reference to it.
31528      * @type {Roo.form.Form}
31529      */
31530     form : false,
31531     /**
31532      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31533      *    This contains a reference to it.
31534      * @type {Roo.View}
31535      */
31536     view : false,
31537     
31538       /**
31539      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31540      * <pre><code>
31541
31542 layout.addxtype({
31543        xtype : 'Form',
31544        items: [ .... ]
31545    }
31546 );
31547
31548 </code></pre>
31549      * @param {Object} cfg Xtype definition of item to add.
31550      */
31551     
31552     addxtype : function(cfg) {
31553         // add form..
31554         if (cfg.xtype.match(/^Form$/)) {
31555             
31556             var el;
31557             //if (this.footer) {
31558             //    el = this.footer.container.insertSibling(false, 'before');
31559             //} else {
31560                 el = this.el.createChild();
31561             //}
31562
31563             this.form = new  Roo.form.Form(cfg);
31564             
31565             
31566             if ( this.form.allItems.length) {
31567                 this.form.render(el.dom);
31568             }
31569             return this.form;
31570         }
31571         // should only have one of theses..
31572         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31573             // views.. should not be just added - used named prop 'view''
31574             
31575             cfg.el = this.el.appendChild(document.createElement("div"));
31576             // factory?
31577             
31578             var ret = new Roo.factory(cfg);
31579              
31580              ret.render && ret.render(false, ''); // render blank..
31581             this.view = ret;
31582             return ret;
31583         }
31584         return false;
31585     }
31586 });
31587
31588 /**
31589  * @class Roo.GridPanel
31590  * @extends Roo.ContentPanel
31591  * @constructor
31592  * Create a new GridPanel.
31593  * @param {Roo.grid.Grid} grid The grid for this panel
31594  * @param {String/Object} config A string to set only the panel's title, or a config object
31595  */
31596 Roo.GridPanel = function(grid, config){
31597     
31598   
31599     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31600         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31601         
31602     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31603     
31604     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31605     
31606     if(this.toolbar){
31607         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31608     }
31609     // xtype created footer. - not sure if will work as we normally have to render first..
31610     if (this.footer && !this.footer.el && this.footer.xtype) {
31611         
31612         this.footer.container = this.grid.getView().getFooterPanel(true);
31613         this.footer.dataSource = this.grid.dataSource;
31614         this.footer = Roo.factory(this.footer, Roo);
31615         
31616     }
31617     
31618     grid.monitorWindowResize = false; // turn off autosizing
31619     grid.autoHeight = false;
31620     grid.autoWidth = false;
31621     this.grid = grid;
31622     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31623 };
31624
31625 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31626     getId : function(){
31627         return this.grid.id;
31628     },
31629     
31630     /**
31631      * Returns the grid for this panel
31632      * @return {Roo.grid.Grid} 
31633      */
31634     getGrid : function(){
31635         return this.grid;    
31636     },
31637     
31638     setSize : function(width, height){
31639         if(!this.ignoreResize(width, height)){
31640             var grid = this.grid;
31641             var size = this.adjustForComponents(width, height);
31642             grid.getGridEl().setSize(size.width, size.height);
31643             grid.autoSize();
31644         }
31645     },
31646     
31647     beforeSlide : function(){
31648         this.grid.getView().scroller.clip();
31649     },
31650     
31651     afterSlide : function(){
31652         this.grid.getView().scroller.unclip();
31653     },
31654     
31655     destroy : function(){
31656         this.grid.destroy();
31657         delete this.grid;
31658         Roo.GridPanel.superclass.destroy.call(this); 
31659     }
31660 });
31661
31662
31663 /**
31664  * @class Roo.NestedLayoutPanel
31665  * @extends Roo.ContentPanel
31666  * @constructor
31667  * Create a new NestedLayoutPanel.
31668  * 
31669  * 
31670  * @param {Roo.BorderLayout} layout The layout for this panel
31671  * @param {String/Object} config A string to set only the title or a config object
31672  */
31673 Roo.NestedLayoutPanel = function(layout, config)
31674 {
31675     // construct with only one argument..
31676     /* FIXME - implement nicer consturctors
31677     if (layout.layout) {
31678         config = layout;
31679         layout = config.layout;
31680         delete config.layout;
31681     }
31682     if (layout.xtype && !layout.getEl) {
31683         // then layout needs constructing..
31684         layout = Roo.factory(layout, Roo);
31685     }
31686     */
31687     
31688     
31689     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31690     
31691     layout.monitorWindowResize = false; // turn off autosizing
31692     this.layout = layout;
31693     this.layout.getEl().addClass("x-layout-nested-layout");
31694     
31695     
31696     
31697     
31698 };
31699
31700 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31701
31702     setSize : function(width, height){
31703         if(!this.ignoreResize(width, height)){
31704             var size = this.adjustForComponents(width, height);
31705             var el = this.layout.getEl();
31706             el.setSize(size.width, size.height);
31707             var touch = el.dom.offsetWidth;
31708             this.layout.layout();
31709             // ie requires a double layout on the first pass
31710             if(Roo.isIE && !this.initialized){
31711                 this.initialized = true;
31712                 this.layout.layout();
31713             }
31714         }
31715     },
31716     
31717     // activate all subpanels if not currently active..
31718     
31719     setActiveState : function(active){
31720         this.active = active;
31721         if(!active){
31722             this.fireEvent("deactivate", this);
31723             return;
31724         }
31725         
31726         this.fireEvent("activate", this);
31727         // not sure if this should happen before or after..
31728         if (!this.layout) {
31729             return; // should not happen..
31730         }
31731         var reg = false;
31732         for (var r in this.layout.regions) {
31733             reg = this.layout.getRegion(r);
31734             if (reg.getActivePanel()) {
31735                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31736                 reg.setActivePanel(reg.getActivePanel());
31737                 continue;
31738             }
31739             if (!reg.panels.length) {
31740                 continue;
31741             }
31742             reg.showPanel(reg.getPanel(0));
31743         }
31744         
31745         
31746         
31747         
31748     },
31749     
31750     /**
31751      * Returns the nested BorderLayout for this panel
31752      * @return {Roo.BorderLayout} 
31753      */
31754     getLayout : function(){
31755         return this.layout;
31756     },
31757     
31758      /**
31759      * Adds a xtype elements to the layout of the nested panel
31760      * <pre><code>
31761
31762 panel.addxtype({
31763        xtype : 'ContentPanel',
31764        region: 'west',
31765        items: [ .... ]
31766    }
31767 );
31768
31769 panel.addxtype({
31770         xtype : 'NestedLayoutPanel',
31771         region: 'west',
31772         layout: {
31773            center: { },
31774            west: { }   
31775         },
31776         items : [ ... list of content panels or nested layout panels.. ]
31777    }
31778 );
31779 </code></pre>
31780      * @param {Object} cfg Xtype definition of item to add.
31781      */
31782     addxtype : function(cfg) {
31783         return this.layout.addxtype(cfg);
31784     
31785     }
31786 });
31787
31788 Roo.ScrollPanel = function(el, config, content){
31789     config = config || {};
31790     config.fitToFrame = true;
31791     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31792     
31793     this.el.dom.style.overflow = "hidden";
31794     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31795     this.el.removeClass("x-layout-inactive-content");
31796     this.el.on("mousewheel", this.onWheel, this);
31797
31798     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31799     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31800     up.unselectable(); down.unselectable();
31801     up.on("click", this.scrollUp, this);
31802     down.on("click", this.scrollDown, this);
31803     up.addClassOnOver("x-scroller-btn-over");
31804     down.addClassOnOver("x-scroller-btn-over");
31805     up.addClassOnClick("x-scroller-btn-click");
31806     down.addClassOnClick("x-scroller-btn-click");
31807     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31808
31809     this.resizeEl = this.el;
31810     this.el = wrap; this.up = up; this.down = down;
31811 };
31812
31813 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31814     increment : 100,
31815     wheelIncrement : 5,
31816     scrollUp : function(){
31817         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31818     },
31819
31820     scrollDown : function(){
31821         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31822     },
31823
31824     afterScroll : function(){
31825         var el = this.resizeEl;
31826         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31827         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31828         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31829     },
31830
31831     setSize : function(){
31832         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31833         this.afterScroll();
31834     },
31835
31836     onWheel : function(e){
31837         var d = e.getWheelDelta();
31838         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31839         this.afterScroll();
31840         e.stopEvent();
31841     },
31842
31843     setContent : function(content, loadScripts){
31844         this.resizeEl.update(content, loadScripts);
31845     }
31846
31847 });
31848
31849
31850
31851
31852
31853
31854
31855
31856
31857 /**
31858  * @class Roo.TreePanel
31859  * @extends Roo.ContentPanel
31860  * @constructor
31861  * Create a new TreePanel. - defaults to fit/scoll contents.
31862  * @param {String/Object} config A string to set only the panel's title, or a config object
31863  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31864  */
31865 Roo.TreePanel = function(config){
31866     var el = config.el;
31867     var tree = config.tree;
31868     delete config.tree; 
31869     delete config.el; // hopefull!
31870     
31871     // wrapper for IE7 strict & safari scroll issue
31872     
31873     var treeEl = el.createChild();
31874     config.resizeEl = treeEl;
31875     
31876     
31877     
31878     Roo.TreePanel.superclass.constructor.call(this, el, config);
31879  
31880  
31881     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31882     //console.log(tree);
31883     this.on('activate', function()
31884     {
31885         if (this.tree.rendered) {
31886             return;
31887         }
31888         //console.log('render tree');
31889         this.tree.render();
31890     });
31891     // this should not be needed.. - it's actually the 'el' that resizes?
31892     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31893     
31894     //this.on('resize',  function (cp, w, h) {
31895     //        this.tree.innerCt.setWidth(w);
31896     //        this.tree.innerCt.setHeight(h);
31897     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31898     //});
31899
31900         
31901     
31902 };
31903
31904 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31905     fitToFrame : true,
31906     autoScroll : true
31907 });
31908
31909
31910
31911
31912
31913
31914
31915
31916
31917
31918
31919 /*
31920  * Based on:
31921  * Ext JS Library 1.1.1
31922  * Copyright(c) 2006-2007, Ext JS, LLC.
31923  *
31924  * Originally Released Under LGPL - original licence link has changed is not relivant.
31925  *
31926  * Fork - LGPL
31927  * <script type="text/javascript">
31928  */
31929  
31930
31931 /**
31932  * @class Roo.ReaderLayout
31933  * @extends Roo.BorderLayout
31934  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31935  * center region containing two nested regions (a top one for a list view and one for item preview below),
31936  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31937  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31938  * expedites the setup of the overall layout and regions for this common application style.
31939  * Example:
31940  <pre><code>
31941 var reader = new Roo.ReaderLayout();
31942 var CP = Roo.ContentPanel;  // shortcut for adding
31943
31944 reader.beginUpdate();
31945 reader.add("north", new CP("north", "North"));
31946 reader.add("west", new CP("west", {title: "West"}));
31947 reader.add("east", new CP("east", {title: "East"}));
31948
31949 reader.regions.listView.add(new CP("listView", "List"));
31950 reader.regions.preview.add(new CP("preview", "Preview"));
31951 reader.endUpdate();
31952 </code></pre>
31953 * @constructor
31954 * Create a new ReaderLayout
31955 * @param {Object} config Configuration options
31956 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31957 * document.body if omitted)
31958 */
31959 Roo.ReaderLayout = function(config, renderTo){
31960     var c = config || {size:{}};
31961     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31962         north: c.north !== false ? Roo.apply({
31963             split:false,
31964             initialSize: 32,
31965             titlebar: false
31966         }, c.north) : false,
31967         west: c.west !== false ? Roo.apply({
31968             split:true,
31969             initialSize: 200,
31970             minSize: 175,
31971             maxSize: 400,
31972             titlebar: true,
31973             collapsible: true,
31974             animate: true,
31975             margins:{left:5,right:0,bottom:5,top:5},
31976             cmargins:{left:5,right:5,bottom:5,top:5}
31977         }, c.west) : false,
31978         east: c.east !== false ? Roo.apply({
31979             split:true,
31980             initialSize: 200,
31981             minSize: 175,
31982             maxSize: 400,
31983             titlebar: true,
31984             collapsible: true,
31985             animate: true,
31986             margins:{left:0,right:5,bottom:5,top:5},
31987             cmargins:{left:5,right:5,bottom:5,top:5}
31988         }, c.east) : false,
31989         center: Roo.apply({
31990             tabPosition: 'top',
31991             autoScroll:false,
31992             closeOnTab: true,
31993             titlebar:false,
31994             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31995         }, c.center)
31996     });
31997
31998     this.el.addClass('x-reader');
31999
32000     this.beginUpdate();
32001
32002     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32003         south: c.preview !== false ? Roo.apply({
32004             split:true,
32005             initialSize: 200,
32006             minSize: 100,
32007             autoScroll:true,
32008             collapsible:true,
32009             titlebar: true,
32010             cmargins:{top:5,left:0, right:0, bottom:0}
32011         }, c.preview) : false,
32012         center: Roo.apply({
32013             autoScroll:false,
32014             titlebar:false,
32015             minHeight:200
32016         }, c.listView)
32017     });
32018     this.add('center', new Roo.NestedLayoutPanel(inner,
32019             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32020
32021     this.endUpdate();
32022
32023     this.regions.preview = inner.getRegion('south');
32024     this.regions.listView = inner.getRegion('center');
32025 };
32026
32027 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32028  * Based on:
32029  * Ext JS Library 1.1.1
32030  * Copyright(c) 2006-2007, Ext JS, LLC.
32031  *
32032  * Originally Released Under LGPL - original licence link has changed is not relivant.
32033  *
32034  * Fork - LGPL
32035  * <script type="text/javascript">
32036  */
32037  
32038 /**
32039  * @class Roo.grid.Grid
32040  * @extends Roo.util.Observable
32041  * This class represents the primary interface of a component based grid control.
32042  * <br><br>Usage:<pre><code>
32043  var grid = new Roo.grid.Grid("my-container-id", {
32044      ds: myDataStore,
32045      cm: myColModel,
32046      selModel: mySelectionModel,
32047      autoSizeColumns: true,
32048      monitorWindowResize: false,
32049      trackMouseOver: true
32050  });
32051  // set any options
32052  grid.render();
32053  * </code></pre>
32054  * <b>Common Problems:</b><br/>
32055  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32056  * element will correct this<br/>
32057  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32058  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32059  * are unpredictable.<br/>
32060  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32061  * grid to calculate dimensions/offsets.<br/>
32062   * @constructor
32063  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32064  * The container MUST have some type of size defined for the grid to fill. The container will be
32065  * automatically set to position relative if it isn't already.
32066  * @param {Object} config A config object that sets properties on this grid.
32067  */
32068 Roo.grid.Grid = function(container, config){
32069         // initialize the container
32070         this.container = Roo.get(container);
32071         this.container.update("");
32072         this.container.setStyle("overflow", "hidden");
32073     this.container.addClass('x-grid-container');
32074
32075     this.id = this.container.id;
32076
32077     Roo.apply(this, config);
32078     // check and correct shorthanded configs
32079     if(this.ds){
32080         this.dataSource = this.ds;
32081         delete this.ds;
32082     }
32083     if(this.cm){
32084         this.colModel = this.cm;
32085         delete this.cm;
32086     }
32087     if(this.sm){
32088         this.selModel = this.sm;
32089         delete this.sm;
32090     }
32091
32092     if (this.selModel) {
32093         this.selModel = Roo.factory(this.selModel, Roo.grid);
32094         this.sm = this.selModel;
32095         this.sm.xmodule = this.xmodule || false;
32096     }
32097     if (typeof(this.colModel.config) == 'undefined') {
32098         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32099         this.cm = this.colModel;
32100         this.cm.xmodule = this.xmodule || false;
32101     }
32102     if (this.dataSource) {
32103         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32104         this.ds = this.dataSource;
32105         this.ds.xmodule = this.xmodule || false;
32106          
32107     }
32108     
32109     
32110     
32111     if(this.width){
32112         this.container.setWidth(this.width);
32113     }
32114
32115     if(this.height){
32116         this.container.setHeight(this.height);
32117     }
32118     /** @private */
32119         this.addEvents({
32120         // raw events
32121         /**
32122          * @event click
32123          * The raw click event for the entire grid.
32124          * @param {Roo.EventObject} e
32125          */
32126         "click" : true,
32127         /**
32128          * @event dblclick
32129          * The raw dblclick event for the entire grid.
32130          * @param {Roo.EventObject} e
32131          */
32132         "dblclick" : true,
32133         /**
32134          * @event contextmenu
32135          * The raw contextmenu event for the entire grid.
32136          * @param {Roo.EventObject} e
32137          */
32138         "contextmenu" : true,
32139         /**
32140          * @event mousedown
32141          * The raw mousedown event for the entire grid.
32142          * @param {Roo.EventObject} e
32143          */
32144         "mousedown" : true,
32145         /**
32146          * @event mouseup
32147          * The raw mouseup event for the entire grid.
32148          * @param {Roo.EventObject} e
32149          */
32150         "mouseup" : true,
32151         /**
32152          * @event mouseover
32153          * The raw mouseover event for the entire grid.
32154          * @param {Roo.EventObject} e
32155          */
32156         "mouseover" : true,
32157         /**
32158          * @event mouseout
32159          * The raw mouseout event for the entire grid.
32160          * @param {Roo.EventObject} e
32161          */
32162         "mouseout" : true,
32163         /**
32164          * @event keypress
32165          * The raw keypress event for the entire grid.
32166          * @param {Roo.EventObject} e
32167          */
32168         "keypress" : true,
32169         /**
32170          * @event keydown
32171          * The raw keydown event for the entire grid.
32172          * @param {Roo.EventObject} e
32173          */
32174         "keydown" : true,
32175
32176         // custom events
32177
32178         /**
32179          * @event cellclick
32180          * Fires when a cell is clicked
32181          * @param {Grid} this
32182          * @param {Number} rowIndex
32183          * @param {Number} columnIndex
32184          * @param {Roo.EventObject} e
32185          */
32186         "cellclick" : true,
32187         /**
32188          * @event celldblclick
32189          * Fires when a cell is double clicked
32190          * @param {Grid} this
32191          * @param {Number} rowIndex
32192          * @param {Number} columnIndex
32193          * @param {Roo.EventObject} e
32194          */
32195         "celldblclick" : true,
32196         /**
32197          * @event rowclick
32198          * Fires when a row is clicked
32199          * @param {Grid} this
32200          * @param {Number} rowIndex
32201          * @param {Roo.EventObject} e
32202          */
32203         "rowclick" : true,
32204         /**
32205          * @event rowdblclick
32206          * Fires when a row is double clicked
32207          * @param {Grid} this
32208          * @param {Number} rowIndex
32209          * @param {Roo.EventObject} e
32210          */
32211         "rowdblclick" : true,
32212         /**
32213          * @event headerclick
32214          * Fires when a header is clicked
32215          * @param {Grid} this
32216          * @param {Number} columnIndex
32217          * @param {Roo.EventObject} e
32218          */
32219         "headerclick" : true,
32220         /**
32221          * @event headerdblclick
32222          * Fires when a header cell is double clicked
32223          * @param {Grid} this
32224          * @param {Number} columnIndex
32225          * @param {Roo.EventObject} e
32226          */
32227         "headerdblclick" : true,
32228         /**
32229          * @event rowcontextmenu
32230          * Fires when a row is right clicked
32231          * @param {Grid} this
32232          * @param {Number} rowIndex
32233          * @param {Roo.EventObject} e
32234          */
32235         "rowcontextmenu" : true,
32236         /**
32237          * @event cellcontextmenu
32238          * Fires when a cell is right clicked
32239          * @param {Grid} this
32240          * @param {Number} rowIndex
32241          * @param {Number} cellIndex
32242          * @param {Roo.EventObject} e
32243          */
32244          "cellcontextmenu" : true,
32245         /**
32246          * @event headercontextmenu
32247          * Fires when a header is right clicked
32248          * @param {Grid} this
32249          * @param {Number} columnIndex
32250          * @param {Roo.EventObject} e
32251          */
32252         "headercontextmenu" : true,
32253         /**
32254          * @event bodyscroll
32255          * Fires when the body element is scrolled
32256          * @param {Number} scrollLeft
32257          * @param {Number} scrollTop
32258          */
32259         "bodyscroll" : true,
32260         /**
32261          * @event columnresize
32262          * Fires when the user resizes a column
32263          * @param {Number} columnIndex
32264          * @param {Number} newSize
32265          */
32266         "columnresize" : true,
32267         /**
32268          * @event columnmove
32269          * Fires when the user moves a column
32270          * @param {Number} oldIndex
32271          * @param {Number} newIndex
32272          */
32273         "columnmove" : true,
32274         /**
32275          * @event startdrag
32276          * Fires when row(s) start being dragged
32277          * @param {Grid} this
32278          * @param {Roo.GridDD} dd The drag drop object
32279          * @param {event} e The raw browser event
32280          */
32281         "startdrag" : true,
32282         /**
32283          * @event enddrag
32284          * Fires when a drag operation is complete
32285          * @param {Grid} this
32286          * @param {Roo.GridDD} dd The drag drop object
32287          * @param {event} e The raw browser event
32288          */
32289         "enddrag" : true,
32290         /**
32291          * @event dragdrop
32292          * Fires when dragged row(s) are dropped on a valid DD target
32293          * @param {Grid} this
32294          * @param {Roo.GridDD} dd The drag drop object
32295          * @param {String} targetId The target drag drop object
32296          * @param {event} e The raw browser event
32297          */
32298         "dragdrop" : true,
32299         /**
32300          * @event dragover
32301          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32302          * @param {Grid} this
32303          * @param {Roo.GridDD} dd The drag drop object
32304          * @param {String} targetId The target drag drop object
32305          * @param {event} e The raw browser event
32306          */
32307         "dragover" : true,
32308         /**
32309          * @event dragenter
32310          *  Fires when the dragged row(s) first cross another DD target while being dragged
32311          * @param {Grid} this
32312          * @param {Roo.GridDD} dd The drag drop object
32313          * @param {String} targetId The target drag drop object
32314          * @param {event} e The raw browser event
32315          */
32316         "dragenter" : true,
32317         /**
32318          * @event dragout
32319          * Fires when the dragged row(s) leave another DD target while being dragged
32320          * @param {Grid} this
32321          * @param {Roo.GridDD} dd The drag drop object
32322          * @param {String} targetId The target drag drop object
32323          * @param {event} e The raw browser event
32324          */
32325         "dragout" : true,
32326         /**
32327          * @event rowclass
32328          * Fires when a row is rendered, so you can change add a style to it.
32329          * @param {GridView} gridview   The grid view
32330          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32331          */
32332         'rowclass' : true,
32333
32334         /**
32335          * @event render
32336          * Fires when the grid is rendered
32337          * @param {Grid} grid
32338          */
32339         'render' : true
32340     });
32341
32342     Roo.grid.Grid.superclass.constructor.call(this);
32343 };
32344 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32345     
32346     /**
32347      * @cfg {String} ddGroup - drag drop group.
32348      */
32349
32350     /**
32351      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32352      */
32353     minColumnWidth : 25,
32354
32355     /**
32356      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32357      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32358      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32359      */
32360     autoSizeColumns : false,
32361
32362     /**
32363      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32364      */
32365     autoSizeHeaders : true,
32366
32367     /**
32368      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32369      */
32370     monitorWindowResize : true,
32371
32372     /**
32373      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32374      * rows measured to get a columns size. Default is 0 (all rows).
32375      */
32376     maxRowsToMeasure : 0,
32377
32378     /**
32379      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32380      */
32381     trackMouseOver : true,
32382
32383     /**
32384     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32385     */
32386     
32387     /**
32388     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32389     */
32390     enableDragDrop : false,
32391     
32392     /**
32393     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32394     */
32395     enableColumnMove : true,
32396     
32397     /**
32398     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32399     */
32400     enableColumnHide : true,
32401     
32402     /**
32403     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32404     */
32405     enableRowHeightSync : false,
32406     
32407     /**
32408     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32409     */
32410     stripeRows : true,
32411     
32412     /**
32413     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32414     */
32415     autoHeight : false,
32416
32417     /**
32418      * @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.
32419      */
32420     autoExpandColumn : false,
32421
32422     /**
32423     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32424     * Default is 50.
32425     */
32426     autoExpandMin : 50,
32427
32428     /**
32429     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32430     */
32431     autoExpandMax : 1000,
32432
32433     /**
32434     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32435     */
32436     view : null,
32437
32438     /**
32439     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32440     */
32441     loadMask : false,
32442     /**
32443     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32444     */
32445     dropTarget: false,
32446     
32447    
32448     
32449     // private
32450     rendered : false,
32451
32452     /**
32453     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32454     * of a fixed width. Default is false.
32455     */
32456     /**
32457     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32458     */
32459     /**
32460      * Called once after all setup has been completed and the grid is ready to be rendered.
32461      * @return {Roo.grid.Grid} this
32462      */
32463     render : function()
32464     {
32465         var c = this.container;
32466         // try to detect autoHeight/width mode
32467         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32468             this.autoHeight = true;
32469         }
32470         var view = this.getView();
32471         view.init(this);
32472
32473         c.on("click", this.onClick, this);
32474         c.on("dblclick", this.onDblClick, this);
32475         c.on("contextmenu", this.onContextMenu, this);
32476         c.on("keydown", this.onKeyDown, this);
32477         if (Roo.isTouch) {
32478             c.on("touchstart", this.onTouchStart, this);
32479         }
32480
32481         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32482
32483         this.getSelectionModel().init(this);
32484
32485         view.render();
32486
32487         if(this.loadMask){
32488             this.loadMask = new Roo.LoadMask(this.container,
32489                     Roo.apply({store:this.dataSource}, this.loadMask));
32490         }
32491         
32492         
32493         if (this.toolbar && this.toolbar.xtype) {
32494             this.toolbar.container = this.getView().getHeaderPanel(true);
32495             this.toolbar = new Roo.Toolbar(this.toolbar);
32496         }
32497         if (this.footer && this.footer.xtype) {
32498             this.footer.dataSource = this.getDataSource();
32499             this.footer.container = this.getView().getFooterPanel(true);
32500             this.footer = Roo.factory(this.footer, Roo);
32501         }
32502         if (this.dropTarget && this.dropTarget.xtype) {
32503             delete this.dropTarget.xtype;
32504             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32505         }
32506         
32507         
32508         this.rendered = true;
32509         this.fireEvent('render', this);
32510         return this;
32511     },
32512
32513         /**
32514          * Reconfigures the grid to use a different Store and Column Model.
32515          * The View will be bound to the new objects and refreshed.
32516          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32517          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32518          */
32519     reconfigure : function(dataSource, colModel){
32520         if(this.loadMask){
32521             this.loadMask.destroy();
32522             this.loadMask = new Roo.LoadMask(this.container,
32523                     Roo.apply({store:dataSource}, this.loadMask));
32524         }
32525         this.view.bind(dataSource, colModel);
32526         this.dataSource = dataSource;
32527         this.colModel = colModel;
32528         this.view.refresh(true);
32529     },
32530
32531     // private
32532     onKeyDown : function(e){
32533         this.fireEvent("keydown", e);
32534     },
32535
32536     /**
32537      * Destroy this grid.
32538      * @param {Boolean} removeEl True to remove the element
32539      */
32540     destroy : function(removeEl, keepListeners){
32541         if(this.loadMask){
32542             this.loadMask.destroy();
32543         }
32544         var c = this.container;
32545         c.removeAllListeners();
32546         this.view.destroy();
32547         this.colModel.purgeListeners();
32548         if(!keepListeners){
32549             this.purgeListeners();
32550         }
32551         c.update("");
32552         if(removeEl === true){
32553             c.remove();
32554         }
32555     },
32556
32557     // private
32558     processEvent : function(name, e){
32559         // does this fire select???
32560         //Roo.log('grid:processEvent '  + name);
32561         
32562         if (name != 'touchstart' ) {
32563             this.fireEvent(name, e);    
32564         }
32565         
32566         var t = e.getTarget();
32567         var v = this.view;
32568         var header = v.findHeaderIndex(t);
32569         if(header !== false){
32570             var ename = name == 'touchstart' ? 'click' : name;
32571              
32572             this.fireEvent("header" + ename, this, header, e);
32573         }else{
32574             var row = v.findRowIndex(t);
32575             var cell = v.findCellIndex(t);
32576             if (name == 'touchstart') {
32577                 // first touch is always a click.
32578                 // hopefull this happens after selection is updated.?
32579                 name = false;
32580                 
32581                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32582                     var cs = this.selModel.getSelectedCell();
32583                     if (row == cs[0] && cell == cs[1]){
32584                         name = 'dblclick';
32585                     }
32586                 }
32587                 if (typeof(this.selModel.getSelections) != 'undefined') {
32588                     var cs = this.selModel.getSelections();
32589                     var ds = this.dataSource;
32590                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32591                         name = 'dblclick';
32592                     }
32593                 }
32594                 if (!name) {
32595                     return;
32596                 }
32597             }
32598             
32599             
32600             if(row !== false){
32601                 this.fireEvent("row" + name, this, row, e);
32602                 if(cell !== false){
32603                     this.fireEvent("cell" + name, this, row, cell, e);
32604                 }
32605             }
32606         }
32607     },
32608
32609     // private
32610     onClick : function(e){
32611         this.processEvent("click", e);
32612     },
32613    // private
32614     onTouchStart : function(e){
32615         this.processEvent("touchstart", e);
32616     },
32617
32618     // private
32619     onContextMenu : function(e, t){
32620         this.processEvent("contextmenu", e);
32621     },
32622
32623     // private
32624     onDblClick : function(e){
32625         this.processEvent("dblclick", e);
32626     },
32627
32628     // private
32629     walkCells : function(row, col, step, fn, scope){
32630         var cm = this.colModel, clen = cm.getColumnCount();
32631         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32632         if(step < 0){
32633             if(col < 0){
32634                 row--;
32635                 first = false;
32636             }
32637             while(row >= 0){
32638                 if(!first){
32639                     col = clen-1;
32640                 }
32641                 first = false;
32642                 while(col >= 0){
32643                     if(fn.call(scope || this, row, col, cm) === true){
32644                         return [row, col];
32645                     }
32646                     col--;
32647                 }
32648                 row--;
32649             }
32650         } else {
32651             if(col >= clen){
32652                 row++;
32653                 first = false;
32654             }
32655             while(row < rlen){
32656                 if(!first){
32657                     col = 0;
32658                 }
32659                 first = false;
32660                 while(col < clen){
32661                     if(fn.call(scope || this, row, col, cm) === true){
32662                         return [row, col];
32663                     }
32664                     col++;
32665                 }
32666                 row++;
32667             }
32668         }
32669         return null;
32670     },
32671
32672     // private
32673     getSelections : function(){
32674         return this.selModel.getSelections();
32675     },
32676
32677     /**
32678      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32679      * but if manual update is required this method will initiate it.
32680      */
32681     autoSize : function(){
32682         if(this.rendered){
32683             this.view.layout();
32684             if(this.view.adjustForScroll){
32685                 this.view.adjustForScroll();
32686             }
32687         }
32688     },
32689
32690     /**
32691      * Returns the grid's underlying element.
32692      * @return {Element} The element
32693      */
32694     getGridEl : function(){
32695         return this.container;
32696     },
32697
32698     // private for compatibility, overridden by editor grid
32699     stopEditing : function(){},
32700
32701     /**
32702      * Returns the grid's SelectionModel.
32703      * @return {SelectionModel}
32704      */
32705     getSelectionModel : function(){
32706         if(!this.selModel){
32707             this.selModel = new Roo.grid.RowSelectionModel();
32708         }
32709         return this.selModel;
32710     },
32711
32712     /**
32713      * Returns the grid's DataSource.
32714      * @return {DataSource}
32715      */
32716     getDataSource : function(){
32717         return this.dataSource;
32718     },
32719
32720     /**
32721      * Returns the grid's ColumnModel.
32722      * @return {ColumnModel}
32723      */
32724     getColumnModel : function(){
32725         return this.colModel;
32726     },
32727
32728     /**
32729      * Returns the grid's GridView object.
32730      * @return {GridView}
32731      */
32732     getView : function(){
32733         if(!this.view){
32734             this.view = new Roo.grid.GridView(this.viewConfig);
32735         }
32736         return this.view;
32737     },
32738     /**
32739      * Called to get grid's drag proxy text, by default returns this.ddText.
32740      * @return {String}
32741      */
32742     getDragDropText : function(){
32743         var count = this.selModel.getCount();
32744         return String.format(this.ddText, count, count == 1 ? '' : 's');
32745     }
32746 });
32747 /**
32748  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32749  * %0 is replaced with the number of selected rows.
32750  * @type String
32751  */
32752 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32753  * Based on:
32754  * Ext JS Library 1.1.1
32755  * Copyright(c) 2006-2007, Ext JS, LLC.
32756  *
32757  * Originally Released Under LGPL - original licence link has changed is not relivant.
32758  *
32759  * Fork - LGPL
32760  * <script type="text/javascript">
32761  */
32762  
32763 Roo.grid.AbstractGridView = function(){
32764         this.grid = null;
32765         
32766         this.events = {
32767             "beforerowremoved" : true,
32768             "beforerowsinserted" : true,
32769             "beforerefresh" : true,
32770             "rowremoved" : true,
32771             "rowsinserted" : true,
32772             "rowupdated" : true,
32773             "refresh" : true
32774         };
32775     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32776 };
32777
32778 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32779     rowClass : "x-grid-row",
32780     cellClass : "x-grid-cell",
32781     tdClass : "x-grid-td",
32782     hdClass : "x-grid-hd",
32783     splitClass : "x-grid-hd-split",
32784     
32785     init: function(grid){
32786         this.grid = grid;
32787                 var cid = this.grid.getGridEl().id;
32788         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32789         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32790         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32791         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32792         },
32793         
32794     getColumnRenderers : function(){
32795         var renderers = [];
32796         var cm = this.grid.colModel;
32797         var colCount = cm.getColumnCount();
32798         for(var i = 0; i < colCount; i++){
32799             renderers[i] = cm.getRenderer(i);
32800         }
32801         return renderers;
32802     },
32803     
32804     getColumnIds : function(){
32805         var ids = [];
32806         var cm = this.grid.colModel;
32807         var colCount = cm.getColumnCount();
32808         for(var i = 0; i < colCount; i++){
32809             ids[i] = cm.getColumnId(i);
32810         }
32811         return ids;
32812     },
32813     
32814     getDataIndexes : function(){
32815         if(!this.indexMap){
32816             this.indexMap = this.buildIndexMap();
32817         }
32818         return this.indexMap.colToData;
32819     },
32820     
32821     getColumnIndexByDataIndex : function(dataIndex){
32822         if(!this.indexMap){
32823             this.indexMap = this.buildIndexMap();
32824         }
32825         return this.indexMap.dataToCol[dataIndex];
32826     },
32827     
32828     /**
32829      * Set a css style for a column dynamically. 
32830      * @param {Number} colIndex The index of the column
32831      * @param {String} name The css property name
32832      * @param {String} value The css value
32833      */
32834     setCSSStyle : function(colIndex, name, value){
32835         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32836         Roo.util.CSS.updateRule(selector, name, value);
32837     },
32838     
32839     generateRules : function(cm){
32840         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32841         Roo.util.CSS.removeStyleSheet(rulesId);
32842         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32843             var cid = cm.getColumnId(i);
32844             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32845                          this.tdSelector, cid, " {\n}\n",
32846                          this.hdSelector, cid, " {\n}\n",
32847                          this.splitSelector, cid, " {\n}\n");
32848         }
32849         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32850     }
32851 });/*
32852  * Based on:
32853  * Ext JS Library 1.1.1
32854  * Copyright(c) 2006-2007, Ext JS, LLC.
32855  *
32856  * Originally Released Under LGPL - original licence link has changed is not relivant.
32857  *
32858  * Fork - LGPL
32859  * <script type="text/javascript">
32860  */
32861
32862 // private
32863 // This is a support class used internally by the Grid components
32864 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32865     this.grid = grid;
32866     this.view = grid.getView();
32867     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32868     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32869     if(hd2){
32870         this.setHandleElId(Roo.id(hd));
32871         this.setOuterHandleElId(Roo.id(hd2));
32872     }
32873     this.scroll = false;
32874 };
32875 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32876     maxDragWidth: 120,
32877     getDragData : function(e){
32878         var t = Roo.lib.Event.getTarget(e);
32879         var h = this.view.findHeaderCell(t);
32880         if(h){
32881             return {ddel: h.firstChild, header:h};
32882         }
32883         return false;
32884     },
32885
32886     onInitDrag : function(e){
32887         this.view.headersDisabled = true;
32888         var clone = this.dragData.ddel.cloneNode(true);
32889         clone.id = Roo.id();
32890         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32891         this.proxy.update(clone);
32892         return true;
32893     },
32894
32895     afterValidDrop : function(){
32896         var v = this.view;
32897         setTimeout(function(){
32898             v.headersDisabled = false;
32899         }, 50);
32900     },
32901
32902     afterInvalidDrop : function(){
32903         var v = this.view;
32904         setTimeout(function(){
32905             v.headersDisabled = false;
32906         }, 50);
32907     }
32908 });
32909 /*
32910  * Based on:
32911  * Ext JS Library 1.1.1
32912  * Copyright(c) 2006-2007, Ext JS, LLC.
32913  *
32914  * Originally Released Under LGPL - original licence link has changed is not relivant.
32915  *
32916  * Fork - LGPL
32917  * <script type="text/javascript">
32918  */
32919 // private
32920 // This is a support class used internally by the Grid components
32921 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32922     this.grid = grid;
32923     this.view = grid.getView();
32924     // split the proxies so they don't interfere with mouse events
32925     this.proxyTop = Roo.DomHelper.append(document.body, {
32926         cls:"col-move-top", html:"&#160;"
32927     }, true);
32928     this.proxyBottom = Roo.DomHelper.append(document.body, {
32929         cls:"col-move-bottom", html:"&#160;"
32930     }, true);
32931     this.proxyTop.hide = this.proxyBottom.hide = function(){
32932         this.setLeftTop(-100,-100);
32933         this.setStyle("visibility", "hidden");
32934     };
32935     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32936     // temporarily disabled
32937     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32938     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32939 };
32940 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32941     proxyOffsets : [-4, -9],
32942     fly: Roo.Element.fly,
32943
32944     getTargetFromEvent : function(e){
32945         var t = Roo.lib.Event.getTarget(e);
32946         var cindex = this.view.findCellIndex(t);
32947         if(cindex !== false){
32948             return this.view.getHeaderCell(cindex);
32949         }
32950         return null;
32951     },
32952
32953     nextVisible : function(h){
32954         var v = this.view, cm = this.grid.colModel;
32955         h = h.nextSibling;
32956         while(h){
32957             if(!cm.isHidden(v.getCellIndex(h))){
32958                 return h;
32959             }
32960             h = h.nextSibling;
32961         }
32962         return null;
32963     },
32964
32965     prevVisible : function(h){
32966         var v = this.view, cm = this.grid.colModel;
32967         h = h.prevSibling;
32968         while(h){
32969             if(!cm.isHidden(v.getCellIndex(h))){
32970                 return h;
32971             }
32972             h = h.prevSibling;
32973         }
32974         return null;
32975     },
32976
32977     positionIndicator : function(h, n, e){
32978         var x = Roo.lib.Event.getPageX(e);
32979         var r = Roo.lib.Dom.getRegion(n.firstChild);
32980         var px, pt, py = r.top + this.proxyOffsets[1];
32981         if((r.right - x) <= (r.right-r.left)/2){
32982             px = r.right+this.view.borderWidth;
32983             pt = "after";
32984         }else{
32985             px = r.left;
32986             pt = "before";
32987         }
32988         var oldIndex = this.view.getCellIndex(h);
32989         var newIndex = this.view.getCellIndex(n);
32990
32991         if(this.grid.colModel.isFixed(newIndex)){
32992             return false;
32993         }
32994
32995         var locked = this.grid.colModel.isLocked(newIndex);
32996
32997         if(pt == "after"){
32998             newIndex++;
32999         }
33000         if(oldIndex < newIndex){
33001             newIndex--;
33002         }
33003         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33004             return false;
33005         }
33006         px +=  this.proxyOffsets[0];
33007         this.proxyTop.setLeftTop(px, py);
33008         this.proxyTop.show();
33009         if(!this.bottomOffset){
33010             this.bottomOffset = this.view.mainHd.getHeight();
33011         }
33012         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33013         this.proxyBottom.show();
33014         return pt;
33015     },
33016
33017     onNodeEnter : function(n, dd, e, data){
33018         if(data.header != n){
33019             this.positionIndicator(data.header, n, e);
33020         }
33021     },
33022
33023     onNodeOver : function(n, dd, e, data){
33024         var result = false;
33025         if(data.header != n){
33026             result = this.positionIndicator(data.header, n, e);
33027         }
33028         if(!result){
33029             this.proxyTop.hide();
33030             this.proxyBottom.hide();
33031         }
33032         return result ? this.dropAllowed : this.dropNotAllowed;
33033     },
33034
33035     onNodeOut : function(n, dd, e, data){
33036         this.proxyTop.hide();
33037         this.proxyBottom.hide();
33038     },
33039
33040     onNodeDrop : function(n, dd, e, data){
33041         var h = data.header;
33042         if(h != n){
33043             var cm = this.grid.colModel;
33044             var x = Roo.lib.Event.getPageX(e);
33045             var r = Roo.lib.Dom.getRegion(n.firstChild);
33046             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33047             var oldIndex = this.view.getCellIndex(h);
33048             var newIndex = this.view.getCellIndex(n);
33049             var locked = cm.isLocked(newIndex);
33050             if(pt == "after"){
33051                 newIndex++;
33052             }
33053             if(oldIndex < newIndex){
33054                 newIndex--;
33055             }
33056             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33057                 return false;
33058             }
33059             cm.setLocked(oldIndex, locked, true);
33060             cm.moveColumn(oldIndex, newIndex);
33061             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33062             return true;
33063         }
33064         return false;
33065     }
33066 });
33067 /*
33068  * Based on:
33069  * Ext JS Library 1.1.1
33070  * Copyright(c) 2006-2007, Ext JS, LLC.
33071  *
33072  * Originally Released Under LGPL - original licence link has changed is not relivant.
33073  *
33074  * Fork - LGPL
33075  * <script type="text/javascript">
33076  */
33077   
33078 /**
33079  * @class Roo.grid.GridView
33080  * @extends Roo.util.Observable
33081  *
33082  * @constructor
33083  * @param {Object} config
33084  */
33085 Roo.grid.GridView = function(config){
33086     Roo.grid.GridView.superclass.constructor.call(this);
33087     this.el = null;
33088
33089     Roo.apply(this, config);
33090 };
33091
33092 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33093
33094     unselectable :  'unselectable="on"',
33095     unselectableCls :  'x-unselectable',
33096     
33097     
33098     rowClass : "x-grid-row",
33099
33100     cellClass : "x-grid-col",
33101
33102     tdClass : "x-grid-td",
33103
33104     hdClass : "x-grid-hd",
33105
33106     splitClass : "x-grid-split",
33107
33108     sortClasses : ["sort-asc", "sort-desc"],
33109
33110     enableMoveAnim : false,
33111
33112     hlColor: "C3DAF9",
33113
33114     dh : Roo.DomHelper,
33115
33116     fly : Roo.Element.fly,
33117
33118     css : Roo.util.CSS,
33119
33120     borderWidth: 1,
33121
33122     splitOffset: 3,
33123
33124     scrollIncrement : 22,
33125
33126     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33127
33128     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33129
33130     bind : function(ds, cm){
33131         if(this.ds){
33132             this.ds.un("load", this.onLoad, this);
33133             this.ds.un("datachanged", this.onDataChange, this);
33134             this.ds.un("add", this.onAdd, this);
33135             this.ds.un("remove", this.onRemove, this);
33136             this.ds.un("update", this.onUpdate, this);
33137             this.ds.un("clear", this.onClear, this);
33138         }
33139         if(ds){
33140             ds.on("load", this.onLoad, this);
33141             ds.on("datachanged", this.onDataChange, this);
33142             ds.on("add", this.onAdd, this);
33143             ds.on("remove", this.onRemove, this);
33144             ds.on("update", this.onUpdate, this);
33145             ds.on("clear", this.onClear, this);
33146         }
33147         this.ds = ds;
33148
33149         if(this.cm){
33150             this.cm.un("widthchange", this.onColWidthChange, this);
33151             this.cm.un("headerchange", this.onHeaderChange, this);
33152             this.cm.un("hiddenchange", this.onHiddenChange, this);
33153             this.cm.un("columnmoved", this.onColumnMove, this);
33154             this.cm.un("columnlockchange", this.onColumnLock, this);
33155         }
33156         if(cm){
33157             this.generateRules(cm);
33158             cm.on("widthchange", this.onColWidthChange, this);
33159             cm.on("headerchange", this.onHeaderChange, this);
33160             cm.on("hiddenchange", this.onHiddenChange, this);
33161             cm.on("columnmoved", this.onColumnMove, this);
33162             cm.on("columnlockchange", this.onColumnLock, this);
33163         }
33164         this.cm = cm;
33165     },
33166
33167     init: function(grid){
33168         Roo.grid.GridView.superclass.init.call(this, grid);
33169
33170         this.bind(grid.dataSource, grid.colModel);
33171
33172         grid.on("headerclick", this.handleHeaderClick, this);
33173
33174         if(grid.trackMouseOver){
33175             grid.on("mouseover", this.onRowOver, this);
33176             grid.on("mouseout", this.onRowOut, this);
33177         }
33178         grid.cancelTextSelection = function(){};
33179         this.gridId = grid.id;
33180
33181         var tpls = this.templates || {};
33182
33183         if(!tpls.master){
33184             tpls.master = new Roo.Template(
33185                '<div class="x-grid" hidefocus="true">',
33186                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33187                   '<div class="x-grid-topbar"></div>',
33188                   '<div class="x-grid-scroller"><div></div></div>',
33189                   '<div class="x-grid-locked">',
33190                       '<div class="x-grid-header">{lockedHeader}</div>',
33191                       '<div class="x-grid-body">{lockedBody}</div>',
33192                   "</div>",
33193                   '<div class="x-grid-viewport">',
33194                       '<div class="x-grid-header">{header}</div>',
33195                       '<div class="x-grid-body">{body}</div>',
33196                   "</div>",
33197                   '<div class="x-grid-bottombar"></div>',
33198                  
33199                   '<div class="x-grid-resize-proxy">&#160;</div>',
33200                "</div>"
33201             );
33202             tpls.master.disableformats = true;
33203         }
33204
33205         if(!tpls.header){
33206             tpls.header = new Roo.Template(
33207                '<table border="0" cellspacing="0" cellpadding="0">',
33208                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33209                "</table>{splits}"
33210             );
33211             tpls.header.disableformats = true;
33212         }
33213         tpls.header.compile();
33214
33215         if(!tpls.hcell){
33216             tpls.hcell = new Roo.Template(
33217                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33218                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33219                 "</div></td>"
33220              );
33221              tpls.hcell.disableFormats = true;
33222         }
33223         tpls.hcell.compile();
33224
33225         if(!tpls.hsplit){
33226             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33227                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33228             tpls.hsplit.disableFormats = true;
33229         }
33230         tpls.hsplit.compile();
33231
33232         if(!tpls.body){
33233             tpls.body = new Roo.Template(
33234                '<table border="0" cellspacing="0" cellpadding="0">',
33235                "<tbody>{rows}</tbody>",
33236                "</table>"
33237             );
33238             tpls.body.disableFormats = true;
33239         }
33240         tpls.body.compile();
33241
33242         if(!tpls.row){
33243             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33244             tpls.row.disableFormats = true;
33245         }
33246         tpls.row.compile();
33247
33248         if(!tpls.cell){
33249             tpls.cell = new Roo.Template(
33250                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33251                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33252                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33253                 "</td>"
33254             );
33255             tpls.cell.disableFormats = true;
33256         }
33257         tpls.cell.compile();
33258
33259         this.templates = tpls;
33260     },
33261
33262     // remap these for backwards compat
33263     onColWidthChange : function(){
33264         this.updateColumns.apply(this, arguments);
33265     },
33266     onHeaderChange : function(){
33267         this.updateHeaders.apply(this, arguments);
33268     }, 
33269     onHiddenChange : function(){
33270         this.handleHiddenChange.apply(this, arguments);
33271     },
33272     onColumnMove : function(){
33273         this.handleColumnMove.apply(this, arguments);
33274     },
33275     onColumnLock : function(){
33276         this.handleLockChange.apply(this, arguments);
33277     },
33278
33279     onDataChange : function(){
33280         this.refresh();
33281         this.updateHeaderSortState();
33282     },
33283
33284     onClear : function(){
33285         this.refresh();
33286     },
33287
33288     onUpdate : function(ds, record){
33289         this.refreshRow(record);
33290     },
33291
33292     refreshRow : function(record){
33293         var ds = this.ds, index;
33294         if(typeof record == 'number'){
33295             index = record;
33296             record = ds.getAt(index);
33297         }else{
33298             index = ds.indexOf(record);
33299         }
33300         this.insertRows(ds, index, index, true);
33301         this.onRemove(ds, record, index+1, true);
33302         this.syncRowHeights(index, index);
33303         this.layout();
33304         this.fireEvent("rowupdated", this, index, record);
33305     },
33306
33307     onAdd : function(ds, records, index){
33308         this.insertRows(ds, index, index + (records.length-1));
33309     },
33310
33311     onRemove : function(ds, record, index, isUpdate){
33312         if(isUpdate !== true){
33313             this.fireEvent("beforerowremoved", this, index, record);
33314         }
33315         var bt = this.getBodyTable(), lt = this.getLockedTable();
33316         if(bt.rows[index]){
33317             bt.firstChild.removeChild(bt.rows[index]);
33318         }
33319         if(lt.rows[index]){
33320             lt.firstChild.removeChild(lt.rows[index]);
33321         }
33322         if(isUpdate !== true){
33323             this.stripeRows(index);
33324             this.syncRowHeights(index, index);
33325             this.layout();
33326             this.fireEvent("rowremoved", this, index, record);
33327         }
33328     },
33329
33330     onLoad : function(){
33331         this.scrollToTop();
33332     },
33333
33334     /**
33335      * Scrolls the grid to the top
33336      */
33337     scrollToTop : function(){
33338         if(this.scroller){
33339             this.scroller.dom.scrollTop = 0;
33340             this.syncScroll();
33341         }
33342     },
33343
33344     /**
33345      * Gets a panel in the header of the grid that can be used for toolbars etc.
33346      * After modifying the contents of this panel a call to grid.autoSize() may be
33347      * required to register any changes in size.
33348      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33349      * @return Roo.Element
33350      */
33351     getHeaderPanel : function(doShow){
33352         if(doShow){
33353             this.headerPanel.show();
33354         }
33355         return this.headerPanel;
33356     },
33357
33358     /**
33359      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33360      * After modifying the contents of this panel a call to grid.autoSize() may be
33361      * required to register any changes in size.
33362      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33363      * @return Roo.Element
33364      */
33365     getFooterPanel : function(doShow){
33366         if(doShow){
33367             this.footerPanel.show();
33368         }
33369         return this.footerPanel;
33370     },
33371
33372     initElements : function(){
33373         var E = Roo.Element;
33374         var el = this.grid.getGridEl().dom.firstChild;
33375         var cs = el.childNodes;
33376
33377         this.el = new E(el);
33378         
33379          this.focusEl = new E(el.firstChild);
33380         this.focusEl.swallowEvent("click", true);
33381         
33382         this.headerPanel = new E(cs[1]);
33383         this.headerPanel.enableDisplayMode("block");
33384
33385         this.scroller = new E(cs[2]);
33386         this.scrollSizer = new E(this.scroller.dom.firstChild);
33387
33388         this.lockedWrap = new E(cs[3]);
33389         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33390         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33391
33392         this.mainWrap = new E(cs[4]);
33393         this.mainHd = new E(this.mainWrap.dom.firstChild);
33394         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33395
33396         this.footerPanel = new E(cs[5]);
33397         this.footerPanel.enableDisplayMode("block");
33398
33399         this.resizeProxy = new E(cs[6]);
33400
33401         this.headerSelector = String.format(
33402            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33403            this.lockedHd.id, this.mainHd.id
33404         );
33405
33406         this.splitterSelector = String.format(
33407            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33408            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33409         );
33410     },
33411     idToCssName : function(s)
33412     {
33413         return s.replace(/[^a-z0-9]+/ig, '-');
33414     },
33415
33416     getHeaderCell : function(index){
33417         return Roo.DomQuery.select(this.headerSelector)[index];
33418     },
33419
33420     getHeaderCellMeasure : function(index){
33421         return this.getHeaderCell(index).firstChild;
33422     },
33423
33424     getHeaderCellText : function(index){
33425         return this.getHeaderCell(index).firstChild.firstChild;
33426     },
33427
33428     getLockedTable : function(){
33429         return this.lockedBody.dom.firstChild;
33430     },
33431
33432     getBodyTable : function(){
33433         return this.mainBody.dom.firstChild;
33434     },
33435
33436     getLockedRow : function(index){
33437         return this.getLockedTable().rows[index];
33438     },
33439
33440     getRow : function(index){
33441         return this.getBodyTable().rows[index];
33442     },
33443
33444     getRowComposite : function(index){
33445         if(!this.rowEl){
33446             this.rowEl = new Roo.CompositeElementLite();
33447         }
33448         var els = [], lrow, mrow;
33449         if(lrow = this.getLockedRow(index)){
33450             els.push(lrow);
33451         }
33452         if(mrow = this.getRow(index)){
33453             els.push(mrow);
33454         }
33455         this.rowEl.elements = els;
33456         return this.rowEl;
33457     },
33458     /**
33459      * Gets the 'td' of the cell
33460      * 
33461      * @param {Integer} rowIndex row to select
33462      * @param {Integer} colIndex column to select
33463      * 
33464      * @return {Object} 
33465      */
33466     getCell : function(rowIndex, colIndex){
33467         var locked = this.cm.getLockedCount();
33468         var source;
33469         if(colIndex < locked){
33470             source = this.lockedBody.dom.firstChild;
33471         }else{
33472             source = this.mainBody.dom.firstChild;
33473             colIndex -= locked;
33474         }
33475         return source.rows[rowIndex].childNodes[colIndex];
33476     },
33477
33478     getCellText : function(rowIndex, colIndex){
33479         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33480     },
33481
33482     getCellBox : function(cell){
33483         var b = this.fly(cell).getBox();
33484         if(Roo.isOpera){ // opera fails to report the Y
33485             b.y = cell.offsetTop + this.mainBody.getY();
33486         }
33487         return b;
33488     },
33489
33490     getCellIndex : function(cell){
33491         var id = String(cell.className).match(this.cellRE);
33492         if(id){
33493             return parseInt(id[1], 10);
33494         }
33495         return 0;
33496     },
33497
33498     findHeaderIndex : function(n){
33499         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33500         return r ? this.getCellIndex(r) : false;
33501     },
33502
33503     findHeaderCell : function(n){
33504         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33505         return r ? r : false;
33506     },
33507
33508     findRowIndex : function(n){
33509         if(!n){
33510             return false;
33511         }
33512         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33513         return r ? r.rowIndex : false;
33514     },
33515
33516     findCellIndex : function(node){
33517         var stop = this.el.dom;
33518         while(node && node != stop){
33519             if(this.findRE.test(node.className)){
33520                 return this.getCellIndex(node);
33521             }
33522             node = node.parentNode;
33523         }
33524         return false;
33525     },
33526
33527     getColumnId : function(index){
33528         return this.cm.getColumnId(index);
33529     },
33530
33531     getSplitters : function()
33532     {
33533         if(this.splitterSelector){
33534            return Roo.DomQuery.select(this.splitterSelector);
33535         }else{
33536             return null;
33537       }
33538     },
33539
33540     getSplitter : function(index){
33541         return this.getSplitters()[index];
33542     },
33543
33544     onRowOver : function(e, t){
33545         var row;
33546         if((row = this.findRowIndex(t)) !== false){
33547             this.getRowComposite(row).addClass("x-grid-row-over");
33548         }
33549     },
33550
33551     onRowOut : function(e, t){
33552         var row;
33553         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33554             this.getRowComposite(row).removeClass("x-grid-row-over");
33555         }
33556     },
33557
33558     renderHeaders : function(){
33559         var cm = this.cm;
33560         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33561         var cb = [], lb = [], sb = [], lsb = [], p = {};
33562         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33563             p.cellId = "x-grid-hd-0-" + i;
33564             p.splitId = "x-grid-csplit-0-" + i;
33565             p.id = cm.getColumnId(i);
33566             p.value = cm.getColumnHeader(i) || "";
33567             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33568             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33569             if(!cm.isLocked(i)){
33570                 cb[cb.length] = ct.apply(p);
33571                 sb[sb.length] = st.apply(p);
33572             }else{
33573                 lb[lb.length] = ct.apply(p);
33574                 lsb[lsb.length] = st.apply(p);
33575             }
33576         }
33577         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33578                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33579     },
33580
33581     updateHeaders : function(){
33582         var html = this.renderHeaders();
33583         this.lockedHd.update(html[0]);
33584         this.mainHd.update(html[1]);
33585     },
33586
33587     /**
33588      * Focuses the specified row.
33589      * @param {Number} row The row index
33590      */
33591     focusRow : function(row)
33592     {
33593         //Roo.log('GridView.focusRow');
33594         var x = this.scroller.dom.scrollLeft;
33595         this.focusCell(row, 0, false);
33596         this.scroller.dom.scrollLeft = x;
33597     },
33598
33599     /**
33600      * Focuses the specified cell.
33601      * @param {Number} row The row index
33602      * @param {Number} col The column index
33603      * @param {Boolean} hscroll false to disable horizontal scrolling
33604      */
33605     focusCell : function(row, col, hscroll)
33606     {
33607         //Roo.log('GridView.focusCell');
33608         var el = this.ensureVisible(row, col, hscroll);
33609         this.focusEl.alignTo(el, "tl-tl");
33610         if(Roo.isGecko){
33611             this.focusEl.focus();
33612         }else{
33613             this.focusEl.focus.defer(1, this.focusEl);
33614         }
33615     },
33616
33617     /**
33618      * Scrolls the specified cell into view
33619      * @param {Number} row The row index
33620      * @param {Number} col The column index
33621      * @param {Boolean} hscroll false to disable horizontal scrolling
33622      */
33623     ensureVisible : function(row, col, hscroll)
33624     {
33625         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33626         //return null; //disable for testing.
33627         if(typeof row != "number"){
33628             row = row.rowIndex;
33629         }
33630         if(row < 0 && row >= this.ds.getCount()){
33631             return  null;
33632         }
33633         col = (col !== undefined ? col : 0);
33634         var cm = this.grid.colModel;
33635         while(cm.isHidden(col)){
33636             col++;
33637         }
33638
33639         var el = this.getCell(row, col);
33640         if(!el){
33641             return null;
33642         }
33643         var c = this.scroller.dom;
33644
33645         var ctop = parseInt(el.offsetTop, 10);
33646         var cleft = parseInt(el.offsetLeft, 10);
33647         var cbot = ctop + el.offsetHeight;
33648         var cright = cleft + el.offsetWidth;
33649         
33650         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33651         var stop = parseInt(c.scrollTop, 10);
33652         var sleft = parseInt(c.scrollLeft, 10);
33653         var sbot = stop + ch;
33654         var sright = sleft + c.clientWidth;
33655         /*
33656         Roo.log('GridView.ensureVisible:' +
33657                 ' ctop:' + ctop +
33658                 ' c.clientHeight:' + c.clientHeight +
33659                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33660                 ' stop:' + stop +
33661                 ' cbot:' + cbot +
33662                 ' sbot:' + sbot +
33663                 ' ch:' + ch  
33664                 );
33665         */
33666         if(ctop < stop){
33667              c.scrollTop = ctop;
33668             //Roo.log("set scrolltop to ctop DISABLE?");
33669         }else if(cbot > sbot){
33670             //Roo.log("set scrolltop to cbot-ch");
33671             c.scrollTop = cbot-ch;
33672         }
33673         
33674         if(hscroll !== false){
33675             if(cleft < sleft){
33676                 c.scrollLeft = cleft;
33677             }else if(cright > sright){
33678                 c.scrollLeft = cright-c.clientWidth;
33679             }
33680         }
33681          
33682         return el;
33683     },
33684
33685     updateColumns : function(){
33686         this.grid.stopEditing();
33687         var cm = this.grid.colModel, colIds = this.getColumnIds();
33688         //var totalWidth = cm.getTotalWidth();
33689         var pos = 0;
33690         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33691             //if(cm.isHidden(i)) continue;
33692             var w = cm.getColumnWidth(i);
33693             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33694             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33695         }
33696         this.updateSplitters();
33697     },
33698
33699     generateRules : function(cm){
33700         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33701         Roo.util.CSS.removeStyleSheet(rulesId);
33702         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33703             var cid = cm.getColumnId(i);
33704             var align = '';
33705             if(cm.config[i].align){
33706                 align = 'text-align:'+cm.config[i].align+';';
33707             }
33708             var hidden = '';
33709             if(cm.isHidden(i)){
33710                 hidden = 'display:none;';
33711             }
33712             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33713             ruleBuf.push(
33714                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33715                     this.hdSelector, cid, " {\n", align, width, "}\n",
33716                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33717                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33718         }
33719         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33720     },
33721
33722     updateSplitters : function(){
33723         var cm = this.cm, s = this.getSplitters();
33724         if(s){ // splitters not created yet
33725             var pos = 0, locked = true;
33726             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33727                 if(cm.isHidden(i)) {
33728                     continue;
33729                 }
33730                 var w = cm.getColumnWidth(i); // make sure it's a number
33731                 if(!cm.isLocked(i) && locked){
33732                     pos = 0;
33733                     locked = false;
33734                 }
33735                 pos += w;
33736                 s[i].style.left = (pos-this.splitOffset) + "px";
33737             }
33738         }
33739     },
33740
33741     handleHiddenChange : function(colModel, colIndex, hidden){
33742         if(hidden){
33743             this.hideColumn(colIndex);
33744         }else{
33745             this.unhideColumn(colIndex);
33746         }
33747     },
33748
33749     hideColumn : function(colIndex){
33750         var cid = this.getColumnId(colIndex);
33751         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33752         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33753         if(Roo.isSafari){
33754             this.updateHeaders();
33755         }
33756         this.updateSplitters();
33757         this.layout();
33758     },
33759
33760     unhideColumn : function(colIndex){
33761         var cid = this.getColumnId(colIndex);
33762         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33763         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33764
33765         if(Roo.isSafari){
33766             this.updateHeaders();
33767         }
33768         this.updateSplitters();
33769         this.layout();
33770     },
33771
33772     insertRows : function(dm, firstRow, lastRow, isUpdate){
33773         if(firstRow == 0 && lastRow == dm.getCount()-1){
33774             this.refresh();
33775         }else{
33776             if(!isUpdate){
33777                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33778             }
33779             var s = this.getScrollState();
33780             var markup = this.renderRows(firstRow, lastRow);
33781             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33782             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33783             this.restoreScroll(s);
33784             if(!isUpdate){
33785                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33786                 this.syncRowHeights(firstRow, lastRow);
33787                 this.stripeRows(firstRow);
33788                 this.layout();
33789             }
33790         }
33791     },
33792
33793     bufferRows : function(markup, target, index){
33794         var before = null, trows = target.rows, tbody = target.tBodies[0];
33795         if(index < trows.length){
33796             before = trows[index];
33797         }
33798         var b = document.createElement("div");
33799         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33800         var rows = b.firstChild.rows;
33801         for(var i = 0, len = rows.length; i < len; i++){
33802             if(before){
33803                 tbody.insertBefore(rows[0], before);
33804             }else{
33805                 tbody.appendChild(rows[0]);
33806             }
33807         }
33808         b.innerHTML = "";
33809         b = null;
33810     },
33811
33812     deleteRows : function(dm, firstRow, lastRow){
33813         if(dm.getRowCount()<1){
33814             this.fireEvent("beforerefresh", this);
33815             this.mainBody.update("");
33816             this.lockedBody.update("");
33817             this.fireEvent("refresh", this);
33818         }else{
33819             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33820             var bt = this.getBodyTable();
33821             var tbody = bt.firstChild;
33822             var rows = bt.rows;
33823             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33824                 tbody.removeChild(rows[firstRow]);
33825             }
33826             this.stripeRows(firstRow);
33827             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33828         }
33829     },
33830
33831     updateRows : function(dataSource, firstRow, lastRow){
33832         var s = this.getScrollState();
33833         this.refresh();
33834         this.restoreScroll(s);
33835     },
33836
33837     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33838         if(!noRefresh){
33839            this.refresh();
33840         }
33841         this.updateHeaderSortState();
33842     },
33843
33844     getScrollState : function(){
33845         
33846         var sb = this.scroller.dom;
33847         return {left: sb.scrollLeft, top: sb.scrollTop};
33848     },
33849
33850     stripeRows : function(startRow){
33851         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33852             return;
33853         }
33854         startRow = startRow || 0;
33855         var rows = this.getBodyTable().rows;
33856         var lrows = this.getLockedTable().rows;
33857         var cls = ' x-grid-row-alt ';
33858         for(var i = startRow, len = rows.length; i < len; i++){
33859             var row = rows[i], lrow = lrows[i];
33860             var isAlt = ((i+1) % 2 == 0);
33861             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33862             if(isAlt == hasAlt){
33863                 continue;
33864             }
33865             if(isAlt){
33866                 row.className += " x-grid-row-alt";
33867             }else{
33868                 row.className = row.className.replace("x-grid-row-alt", "");
33869             }
33870             if(lrow){
33871                 lrow.className = row.className;
33872             }
33873         }
33874     },
33875
33876     restoreScroll : function(state){
33877         //Roo.log('GridView.restoreScroll');
33878         var sb = this.scroller.dom;
33879         sb.scrollLeft = state.left;
33880         sb.scrollTop = state.top;
33881         this.syncScroll();
33882     },
33883
33884     syncScroll : function(){
33885         //Roo.log('GridView.syncScroll');
33886         var sb = this.scroller.dom;
33887         var sh = this.mainHd.dom;
33888         var bs = this.mainBody.dom;
33889         var lv = this.lockedBody.dom;
33890         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33891         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33892     },
33893
33894     handleScroll : function(e){
33895         this.syncScroll();
33896         var sb = this.scroller.dom;
33897         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33898         e.stopEvent();
33899     },
33900
33901     handleWheel : function(e){
33902         var d = e.getWheelDelta();
33903         this.scroller.dom.scrollTop -= d*22;
33904         // set this here to prevent jumpy scrolling on large tables
33905         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33906         e.stopEvent();
33907     },
33908
33909     renderRows : function(startRow, endRow){
33910         // pull in all the crap needed to render rows
33911         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33912         var colCount = cm.getColumnCount();
33913
33914         if(ds.getCount() < 1){
33915             return ["", ""];
33916         }
33917
33918         // build a map for all the columns
33919         var cs = [];
33920         for(var i = 0; i < colCount; i++){
33921             var name = cm.getDataIndex(i);
33922             cs[i] = {
33923                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33924                 renderer : cm.getRenderer(i),
33925                 id : cm.getColumnId(i),
33926                 locked : cm.isLocked(i),
33927                 has_editor : cm.isCellEditable(i)
33928             };
33929         }
33930
33931         startRow = startRow || 0;
33932         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33933
33934         // records to render
33935         var rs = ds.getRange(startRow, endRow);
33936
33937         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33938     },
33939
33940     // As much as I hate to duplicate code, this was branched because FireFox really hates
33941     // [].join("") on strings. The performance difference was substantial enough to
33942     // branch this function
33943     doRender : Roo.isGecko ?
33944             function(cs, rs, ds, startRow, colCount, stripe){
33945                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33946                 // buffers
33947                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33948                 
33949                 var hasListener = this.grid.hasListener('rowclass');
33950                 var rowcfg = {};
33951                 for(var j = 0, len = rs.length; j < len; j++){
33952                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33953                     for(var i = 0; i < colCount; i++){
33954                         c = cs[i];
33955                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33956                         p.id = c.id;
33957                         p.css = p.attr = "";
33958                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33959                         if(p.value == undefined || p.value === "") {
33960                             p.value = "&#160;";
33961                         }
33962                         if(c.has_editor){
33963                             p.css += ' x-grid-editable-cell';
33964                         }
33965                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33966                             p.css +=  ' x-grid-dirty-cell';
33967                         }
33968                         var markup = ct.apply(p);
33969                         if(!c.locked){
33970                             cb+= markup;
33971                         }else{
33972                             lcb+= markup;
33973                         }
33974                     }
33975                     var alt = [];
33976                     if(stripe && ((rowIndex+1) % 2 == 0)){
33977                         alt.push("x-grid-row-alt")
33978                     }
33979                     if(r.dirty){
33980                         alt.push(  " x-grid-dirty-row");
33981                     }
33982                     rp.cells = lcb;
33983                     if(this.getRowClass){
33984                         alt.push(this.getRowClass(r, rowIndex));
33985                     }
33986                     if (hasListener) {
33987                         rowcfg = {
33988                              
33989                             record: r,
33990                             rowIndex : rowIndex,
33991                             rowClass : ''
33992                         };
33993                         this.grid.fireEvent('rowclass', this, rowcfg);
33994                         alt.push(rowcfg.rowClass);
33995                     }
33996                     rp.alt = alt.join(" ");
33997                     lbuf+= rt.apply(rp);
33998                     rp.cells = cb;
33999                     buf+=  rt.apply(rp);
34000                 }
34001                 return [lbuf, buf];
34002             } :
34003             function(cs, rs, ds, startRow, colCount, stripe){
34004                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34005                 // buffers
34006                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34007                 var hasListener = this.grid.hasListener('rowclass');
34008  
34009                 var rowcfg = {};
34010                 for(var j = 0, len = rs.length; j < len; j++){
34011                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34012                     for(var i = 0; i < colCount; i++){
34013                         c = cs[i];
34014                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34015                         p.id = c.id;
34016                         p.css = p.attr = "";
34017                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34018                         if(p.value == undefined || p.value === "") {
34019                             p.value = "&#160;";
34020                         }
34021                         //Roo.log(c);
34022                          if(c.has_editor){
34023                             p.css += ' x-grid-editable-cell';
34024                         }
34025                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34026                             p.css += ' x-grid-dirty-cell' 
34027                         }
34028                         
34029                         var markup = ct.apply(p);
34030                         if(!c.locked){
34031                             cb[cb.length] = markup;
34032                         }else{
34033                             lcb[lcb.length] = markup;
34034                         }
34035                     }
34036                     var alt = [];
34037                     if(stripe && ((rowIndex+1) % 2 == 0)){
34038                         alt.push( "x-grid-row-alt");
34039                     }
34040                     if(r.dirty){
34041                         alt.push(" x-grid-dirty-row");
34042                     }
34043                     rp.cells = lcb;
34044                     if(this.getRowClass){
34045                         alt.push( this.getRowClass(r, rowIndex));
34046                     }
34047                     if (hasListener) {
34048                         rowcfg = {
34049                              
34050                             record: r,
34051                             rowIndex : rowIndex,
34052                             rowClass : ''
34053                         };
34054                         this.grid.fireEvent('rowclass', this, rowcfg);
34055                         alt.push(rowcfg.rowClass);
34056                     }
34057                     
34058                     rp.alt = alt.join(" ");
34059                     rp.cells = lcb.join("");
34060                     lbuf[lbuf.length] = rt.apply(rp);
34061                     rp.cells = cb.join("");
34062                     buf[buf.length] =  rt.apply(rp);
34063                 }
34064                 return [lbuf.join(""), buf.join("")];
34065             },
34066
34067     renderBody : function(){
34068         var markup = this.renderRows();
34069         var bt = this.templates.body;
34070         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34071     },
34072
34073     /**
34074      * Refreshes the grid
34075      * @param {Boolean} headersToo
34076      */
34077     refresh : function(headersToo){
34078         this.fireEvent("beforerefresh", this);
34079         this.grid.stopEditing();
34080         var result = this.renderBody();
34081         this.lockedBody.update(result[0]);
34082         this.mainBody.update(result[1]);
34083         if(headersToo === true){
34084             this.updateHeaders();
34085             this.updateColumns();
34086             this.updateSplitters();
34087             this.updateHeaderSortState();
34088         }
34089         this.syncRowHeights();
34090         this.layout();
34091         this.fireEvent("refresh", this);
34092     },
34093
34094     handleColumnMove : function(cm, oldIndex, newIndex){
34095         this.indexMap = null;
34096         var s = this.getScrollState();
34097         this.refresh(true);
34098         this.restoreScroll(s);
34099         this.afterMove(newIndex);
34100     },
34101
34102     afterMove : function(colIndex){
34103         if(this.enableMoveAnim && Roo.enableFx){
34104             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34105         }
34106         // if multisort - fix sortOrder, and reload..
34107         if (this.grid.dataSource.multiSort) {
34108             // the we can call sort again..
34109             var dm = this.grid.dataSource;
34110             var cm = this.grid.colModel;
34111             var so = [];
34112             for(var i = 0; i < cm.config.length; i++ ) {
34113                 
34114                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34115                     continue; // dont' bother, it's not in sort list or being set.
34116                 }
34117                 
34118                 so.push(cm.config[i].dataIndex);
34119             };
34120             dm.sortOrder = so;
34121             dm.load(dm.lastOptions);
34122             
34123             
34124         }
34125         
34126     },
34127
34128     updateCell : function(dm, rowIndex, dataIndex){
34129         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34130         if(typeof colIndex == "undefined"){ // not present in grid
34131             return;
34132         }
34133         var cm = this.grid.colModel;
34134         var cell = this.getCell(rowIndex, colIndex);
34135         var cellText = this.getCellText(rowIndex, colIndex);
34136
34137         var p = {
34138             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34139             id : cm.getColumnId(colIndex),
34140             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34141         };
34142         var renderer = cm.getRenderer(colIndex);
34143         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34144         if(typeof val == "undefined" || val === "") {
34145             val = "&#160;";
34146         }
34147         cellText.innerHTML = val;
34148         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34149         this.syncRowHeights(rowIndex, rowIndex);
34150     },
34151
34152     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34153         var maxWidth = 0;
34154         if(this.grid.autoSizeHeaders){
34155             var h = this.getHeaderCellMeasure(colIndex);
34156             maxWidth = Math.max(maxWidth, h.scrollWidth);
34157         }
34158         var tb, index;
34159         if(this.cm.isLocked(colIndex)){
34160             tb = this.getLockedTable();
34161             index = colIndex;
34162         }else{
34163             tb = this.getBodyTable();
34164             index = colIndex - this.cm.getLockedCount();
34165         }
34166         if(tb && tb.rows){
34167             var rows = tb.rows;
34168             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34169             for(var i = 0; i < stopIndex; i++){
34170                 var cell = rows[i].childNodes[index].firstChild;
34171                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34172             }
34173         }
34174         return maxWidth + /*margin for error in IE*/ 5;
34175     },
34176     /**
34177      * Autofit a column to its content.
34178      * @param {Number} colIndex
34179      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34180      */
34181      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34182          if(this.cm.isHidden(colIndex)){
34183              return; // can't calc a hidden column
34184          }
34185         if(forceMinSize){
34186             var cid = this.cm.getColumnId(colIndex);
34187             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34188            if(this.grid.autoSizeHeaders){
34189                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34190            }
34191         }
34192         var newWidth = this.calcColumnWidth(colIndex);
34193         this.cm.setColumnWidth(colIndex,
34194             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34195         if(!suppressEvent){
34196             this.grid.fireEvent("columnresize", colIndex, newWidth);
34197         }
34198     },
34199
34200     /**
34201      * Autofits all columns to their content and then expands to fit any extra space in the grid
34202      */
34203      autoSizeColumns : function(){
34204         var cm = this.grid.colModel;
34205         var colCount = cm.getColumnCount();
34206         for(var i = 0; i < colCount; i++){
34207             this.autoSizeColumn(i, true, true);
34208         }
34209         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34210             this.fitColumns();
34211         }else{
34212             this.updateColumns();
34213             this.layout();
34214         }
34215     },
34216
34217     /**
34218      * Autofits all columns to the grid's width proportionate with their current size
34219      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34220      */
34221     fitColumns : function(reserveScrollSpace){
34222         var cm = this.grid.colModel;
34223         var colCount = cm.getColumnCount();
34224         var cols = [];
34225         var width = 0;
34226         var i, w;
34227         for (i = 0; i < colCount; i++){
34228             if(!cm.isHidden(i) && !cm.isFixed(i)){
34229                 w = cm.getColumnWidth(i);
34230                 cols.push(i);
34231                 cols.push(w);
34232                 width += w;
34233             }
34234         }
34235         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34236         if(reserveScrollSpace){
34237             avail -= 17;
34238         }
34239         var frac = (avail - cm.getTotalWidth())/width;
34240         while (cols.length){
34241             w = cols.pop();
34242             i = cols.pop();
34243             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34244         }
34245         this.updateColumns();
34246         this.layout();
34247     },
34248
34249     onRowSelect : function(rowIndex){
34250         var row = this.getRowComposite(rowIndex);
34251         row.addClass("x-grid-row-selected");
34252     },
34253
34254     onRowDeselect : function(rowIndex){
34255         var row = this.getRowComposite(rowIndex);
34256         row.removeClass("x-grid-row-selected");
34257     },
34258
34259     onCellSelect : function(row, col){
34260         var cell = this.getCell(row, col);
34261         if(cell){
34262             Roo.fly(cell).addClass("x-grid-cell-selected");
34263         }
34264     },
34265
34266     onCellDeselect : function(row, col){
34267         var cell = this.getCell(row, col);
34268         if(cell){
34269             Roo.fly(cell).removeClass("x-grid-cell-selected");
34270         }
34271     },
34272
34273     updateHeaderSortState : function(){
34274         
34275         // sort state can be single { field: xxx, direction : yyy}
34276         // or   { xxx=>ASC , yyy : DESC ..... }
34277         
34278         var mstate = {};
34279         if (!this.ds.multiSort) { 
34280             var state = this.ds.getSortState();
34281             if(!state){
34282                 return;
34283             }
34284             mstate[state.field] = state.direction;
34285             // FIXME... - this is not used here.. but might be elsewhere..
34286             this.sortState = state;
34287             
34288         } else {
34289             mstate = this.ds.sortToggle;
34290         }
34291         //remove existing sort classes..
34292         
34293         var sc = this.sortClasses;
34294         var hds = this.el.select(this.headerSelector).removeClass(sc);
34295         
34296         for(var f in mstate) {
34297         
34298             var sortColumn = this.cm.findColumnIndex(f);
34299             
34300             if(sortColumn != -1){
34301                 var sortDir = mstate[f];        
34302                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34303             }
34304         }
34305         
34306          
34307         
34308     },
34309
34310
34311     handleHeaderClick : function(g, index,e){
34312         
34313         Roo.log("header click");
34314         
34315         if (Roo.isTouch) {
34316             // touch events on header are handled by context
34317             this.handleHdCtx(g,index,e);
34318             return;
34319         }
34320         
34321         
34322         if(this.headersDisabled){
34323             return;
34324         }
34325         var dm = g.dataSource, cm = g.colModel;
34326         if(!cm.isSortable(index)){
34327             return;
34328         }
34329         g.stopEditing();
34330         
34331         if (dm.multiSort) {
34332             // update the sortOrder
34333             var so = [];
34334             for(var i = 0; i < cm.config.length; i++ ) {
34335                 
34336                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34337                     continue; // dont' bother, it's not in sort list or being set.
34338                 }
34339                 
34340                 so.push(cm.config[i].dataIndex);
34341             };
34342             dm.sortOrder = so;
34343         }
34344         
34345         
34346         dm.sort(cm.getDataIndex(index));
34347     },
34348
34349
34350     destroy : function(){
34351         if(this.colMenu){
34352             this.colMenu.removeAll();
34353             Roo.menu.MenuMgr.unregister(this.colMenu);
34354             this.colMenu.getEl().remove();
34355             delete this.colMenu;
34356         }
34357         if(this.hmenu){
34358             this.hmenu.removeAll();
34359             Roo.menu.MenuMgr.unregister(this.hmenu);
34360             this.hmenu.getEl().remove();
34361             delete this.hmenu;
34362         }
34363         if(this.grid.enableColumnMove){
34364             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34365             if(dds){
34366                 for(var dd in dds){
34367                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34368                         var elid = dds[dd].dragElId;
34369                         dds[dd].unreg();
34370                         Roo.get(elid).remove();
34371                     } else if(dds[dd].config.isTarget){
34372                         dds[dd].proxyTop.remove();
34373                         dds[dd].proxyBottom.remove();
34374                         dds[dd].unreg();
34375                     }
34376                     if(Roo.dd.DDM.locationCache[dd]){
34377                         delete Roo.dd.DDM.locationCache[dd];
34378                     }
34379                 }
34380                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34381             }
34382         }
34383         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34384         this.bind(null, null);
34385         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34386     },
34387
34388     handleLockChange : function(){
34389         this.refresh(true);
34390     },
34391
34392     onDenyColumnLock : function(){
34393
34394     },
34395
34396     onDenyColumnHide : function(){
34397
34398     },
34399
34400     handleHdMenuClick : function(item){
34401         var index = this.hdCtxIndex;
34402         var cm = this.cm, ds = this.ds;
34403         switch(item.id){
34404             case "asc":
34405                 ds.sort(cm.getDataIndex(index), "ASC");
34406                 break;
34407             case "desc":
34408                 ds.sort(cm.getDataIndex(index), "DESC");
34409                 break;
34410             case "lock":
34411                 var lc = cm.getLockedCount();
34412                 if(cm.getColumnCount(true) <= lc+1){
34413                     this.onDenyColumnLock();
34414                     return;
34415                 }
34416                 if(lc != index){
34417                     cm.setLocked(index, true, true);
34418                     cm.moveColumn(index, lc);
34419                     this.grid.fireEvent("columnmove", index, lc);
34420                 }else{
34421                     cm.setLocked(index, true);
34422                 }
34423             break;
34424             case "unlock":
34425                 var lc = cm.getLockedCount();
34426                 if((lc-1) != index){
34427                     cm.setLocked(index, false, true);
34428                     cm.moveColumn(index, lc-1);
34429                     this.grid.fireEvent("columnmove", index, lc-1);
34430                 }else{
34431                     cm.setLocked(index, false);
34432                 }
34433             break;
34434             case 'wider': // used to expand cols on touch..
34435             case 'narrow':
34436                 var cw = cm.getColumnWidth(index);
34437                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34438                 cw = Math.max(0, cw);
34439                 cw = Math.min(cw,4000);
34440                 cm.setColumnWidth(index, cw);
34441                 break;
34442                 
34443             default:
34444                 index = cm.getIndexById(item.id.substr(4));
34445                 if(index != -1){
34446                     if(item.checked && cm.getColumnCount(true) <= 1){
34447                         this.onDenyColumnHide();
34448                         return false;
34449                     }
34450                     cm.setHidden(index, item.checked);
34451                 }
34452         }
34453         return true;
34454     },
34455
34456     beforeColMenuShow : function(){
34457         var cm = this.cm,  colCount = cm.getColumnCount();
34458         this.colMenu.removeAll();
34459         for(var i = 0; i < colCount; i++){
34460             this.colMenu.add(new Roo.menu.CheckItem({
34461                 id: "col-"+cm.getColumnId(i),
34462                 text: cm.getColumnHeader(i),
34463                 checked: !cm.isHidden(i),
34464                 hideOnClick:false
34465             }));
34466         }
34467     },
34468
34469     handleHdCtx : function(g, index, e){
34470         e.stopEvent();
34471         var hd = this.getHeaderCell(index);
34472         this.hdCtxIndex = index;
34473         var ms = this.hmenu.items, cm = this.cm;
34474         ms.get("asc").setDisabled(!cm.isSortable(index));
34475         ms.get("desc").setDisabled(!cm.isSortable(index));
34476         if(this.grid.enableColLock !== false){
34477             ms.get("lock").setDisabled(cm.isLocked(index));
34478             ms.get("unlock").setDisabled(!cm.isLocked(index));
34479         }
34480         this.hmenu.show(hd, "tl-bl");
34481     },
34482
34483     handleHdOver : function(e){
34484         var hd = this.findHeaderCell(e.getTarget());
34485         if(hd && !this.headersDisabled){
34486             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34487                this.fly(hd).addClass("x-grid-hd-over");
34488             }
34489         }
34490     },
34491
34492     handleHdOut : function(e){
34493         var hd = this.findHeaderCell(e.getTarget());
34494         if(hd){
34495             this.fly(hd).removeClass("x-grid-hd-over");
34496         }
34497     },
34498
34499     handleSplitDblClick : function(e, t){
34500         var i = this.getCellIndex(t);
34501         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34502             this.autoSizeColumn(i, true);
34503             this.layout();
34504         }
34505     },
34506
34507     render : function(){
34508
34509         var cm = this.cm;
34510         var colCount = cm.getColumnCount();
34511
34512         if(this.grid.monitorWindowResize === true){
34513             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34514         }
34515         var header = this.renderHeaders();
34516         var body = this.templates.body.apply({rows:""});
34517         var html = this.templates.master.apply({
34518             lockedBody: body,
34519             body: body,
34520             lockedHeader: header[0],
34521             header: header[1]
34522         });
34523
34524         //this.updateColumns();
34525
34526         this.grid.getGridEl().dom.innerHTML = html;
34527
34528         this.initElements();
34529         
34530         // a kludge to fix the random scolling effect in webkit
34531         this.el.on("scroll", function() {
34532             this.el.dom.scrollTop=0; // hopefully not recursive..
34533         },this);
34534
34535         this.scroller.on("scroll", this.handleScroll, this);
34536         this.lockedBody.on("mousewheel", this.handleWheel, this);
34537         this.mainBody.on("mousewheel", this.handleWheel, this);
34538
34539         this.mainHd.on("mouseover", this.handleHdOver, this);
34540         this.mainHd.on("mouseout", this.handleHdOut, this);
34541         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34542                 {delegate: "."+this.splitClass});
34543
34544         this.lockedHd.on("mouseover", this.handleHdOver, this);
34545         this.lockedHd.on("mouseout", this.handleHdOut, this);
34546         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34547                 {delegate: "."+this.splitClass});
34548
34549         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34550             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34551         }
34552
34553         this.updateSplitters();
34554
34555         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34556             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34557             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34558         }
34559
34560         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34561             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34562             this.hmenu.add(
34563                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34564                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34565             );
34566             if(this.grid.enableColLock !== false){
34567                 this.hmenu.add('-',
34568                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34569                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34570                 );
34571             }
34572             if (Roo.isTouch) {
34573                  this.hmenu.add('-',
34574                     {id:"wider", text: this.columnsWiderText},
34575                     {id:"narrow", text: this.columnsNarrowText }
34576                 );
34577                 
34578                  
34579             }
34580             
34581             if(this.grid.enableColumnHide !== false){
34582
34583                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34584                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34585                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34586
34587                 this.hmenu.add('-',
34588                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34589                 );
34590             }
34591             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34592
34593             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34594         }
34595
34596         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34597             this.dd = new Roo.grid.GridDragZone(this.grid, {
34598                 ddGroup : this.grid.ddGroup || 'GridDD'
34599             });
34600             
34601         }
34602
34603         /*
34604         for(var i = 0; i < colCount; i++){
34605             if(cm.isHidden(i)){
34606                 this.hideColumn(i);
34607             }
34608             if(cm.config[i].align){
34609                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34610                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34611             }
34612         }*/
34613         
34614         this.updateHeaderSortState();
34615
34616         this.beforeInitialResize();
34617         this.layout(true);
34618
34619         // two part rendering gives faster view to the user
34620         this.renderPhase2.defer(1, this);
34621     },
34622
34623     renderPhase2 : function(){
34624         // render the rows now
34625         this.refresh();
34626         if(this.grid.autoSizeColumns){
34627             this.autoSizeColumns();
34628         }
34629     },
34630
34631     beforeInitialResize : function(){
34632
34633     },
34634
34635     onColumnSplitterMoved : function(i, w){
34636         this.userResized = true;
34637         var cm = this.grid.colModel;
34638         cm.setColumnWidth(i, w, true);
34639         var cid = cm.getColumnId(i);
34640         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34641         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34642         this.updateSplitters();
34643         this.layout();
34644         this.grid.fireEvent("columnresize", i, w);
34645     },
34646
34647     syncRowHeights : function(startIndex, endIndex){
34648         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34649             startIndex = startIndex || 0;
34650             var mrows = this.getBodyTable().rows;
34651             var lrows = this.getLockedTable().rows;
34652             var len = mrows.length-1;
34653             endIndex = Math.min(endIndex || len, len);
34654             for(var i = startIndex; i <= endIndex; i++){
34655                 var m = mrows[i], l = lrows[i];
34656                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34657                 m.style.height = l.style.height = h + "px";
34658             }
34659         }
34660     },
34661
34662     layout : function(initialRender, is2ndPass){
34663         var g = this.grid;
34664         var auto = g.autoHeight;
34665         var scrollOffset = 16;
34666         var c = g.getGridEl(), cm = this.cm,
34667                 expandCol = g.autoExpandColumn,
34668                 gv = this;
34669         //c.beginMeasure();
34670
34671         if(!c.dom.offsetWidth){ // display:none?
34672             if(initialRender){
34673                 this.lockedWrap.show();
34674                 this.mainWrap.show();
34675             }
34676             return;
34677         }
34678
34679         var hasLock = this.cm.isLocked(0);
34680
34681         var tbh = this.headerPanel.getHeight();
34682         var bbh = this.footerPanel.getHeight();
34683
34684         if(auto){
34685             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34686             var newHeight = ch + c.getBorderWidth("tb");
34687             if(g.maxHeight){
34688                 newHeight = Math.min(g.maxHeight, newHeight);
34689             }
34690             c.setHeight(newHeight);
34691         }
34692
34693         if(g.autoWidth){
34694             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34695         }
34696
34697         var s = this.scroller;
34698
34699         var csize = c.getSize(true);
34700
34701         this.el.setSize(csize.width, csize.height);
34702
34703         this.headerPanel.setWidth(csize.width);
34704         this.footerPanel.setWidth(csize.width);
34705
34706         var hdHeight = this.mainHd.getHeight();
34707         var vw = csize.width;
34708         var vh = csize.height - (tbh + bbh);
34709
34710         s.setSize(vw, vh);
34711
34712         var bt = this.getBodyTable();
34713         
34714         if(cm.getLockedCount() == cm.config.length){
34715             bt = this.getLockedTable();
34716         }
34717         
34718         var ltWidth = hasLock ?
34719                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34720
34721         var scrollHeight = bt.offsetHeight;
34722         var scrollWidth = ltWidth + bt.offsetWidth;
34723         var vscroll = false, hscroll = false;
34724
34725         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34726
34727         var lw = this.lockedWrap, mw = this.mainWrap;
34728         var lb = this.lockedBody, mb = this.mainBody;
34729
34730         setTimeout(function(){
34731             var t = s.dom.offsetTop;
34732             var w = s.dom.clientWidth,
34733                 h = s.dom.clientHeight;
34734
34735             lw.setTop(t);
34736             lw.setSize(ltWidth, h);
34737
34738             mw.setLeftTop(ltWidth, t);
34739             mw.setSize(w-ltWidth, h);
34740
34741             lb.setHeight(h-hdHeight);
34742             mb.setHeight(h-hdHeight);
34743
34744             if(is2ndPass !== true && !gv.userResized && expandCol){
34745                 // high speed resize without full column calculation
34746                 
34747                 var ci = cm.getIndexById(expandCol);
34748                 if (ci < 0) {
34749                     ci = cm.findColumnIndex(expandCol);
34750                 }
34751                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34752                 var expandId = cm.getColumnId(ci);
34753                 var  tw = cm.getTotalWidth(false);
34754                 var currentWidth = cm.getColumnWidth(ci);
34755                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34756                 if(currentWidth != cw){
34757                     cm.setColumnWidth(ci, cw, true);
34758                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34759                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34760                     gv.updateSplitters();
34761                     gv.layout(false, true);
34762                 }
34763             }
34764
34765             if(initialRender){
34766                 lw.show();
34767                 mw.show();
34768             }
34769             //c.endMeasure();
34770         }, 10);
34771     },
34772
34773     onWindowResize : function(){
34774         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34775             return;
34776         }
34777         this.layout();
34778     },
34779
34780     appendFooter : function(parentEl){
34781         return null;
34782     },
34783
34784     sortAscText : "Sort Ascending",
34785     sortDescText : "Sort Descending",
34786     lockText : "Lock Column",
34787     unlockText : "Unlock Column",
34788     columnsText : "Columns",
34789  
34790     columnsWiderText : "Wider",
34791     columnsNarrowText : "Thinner"
34792 });
34793
34794
34795 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34796     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34797     this.proxy.el.addClass('x-grid3-col-dd');
34798 };
34799
34800 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34801     handleMouseDown : function(e){
34802
34803     },
34804
34805     callHandleMouseDown : function(e){
34806         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34807     }
34808 });
34809 /*
34810  * Based on:
34811  * Ext JS Library 1.1.1
34812  * Copyright(c) 2006-2007, Ext JS, LLC.
34813  *
34814  * Originally Released Under LGPL - original licence link has changed is not relivant.
34815  *
34816  * Fork - LGPL
34817  * <script type="text/javascript">
34818  */
34819  
34820 // private
34821 // This is a support class used internally by the Grid components
34822 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34823     this.grid = grid;
34824     this.view = grid.getView();
34825     this.proxy = this.view.resizeProxy;
34826     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34827         "gridSplitters" + this.grid.getGridEl().id, {
34828         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34829     });
34830     this.setHandleElId(Roo.id(hd));
34831     this.setOuterHandleElId(Roo.id(hd2));
34832     this.scroll = false;
34833 };
34834 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34835     fly: Roo.Element.fly,
34836
34837     b4StartDrag : function(x, y){
34838         this.view.headersDisabled = true;
34839         this.proxy.setHeight(this.view.mainWrap.getHeight());
34840         var w = this.cm.getColumnWidth(this.cellIndex);
34841         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34842         this.resetConstraints();
34843         this.setXConstraint(minw, 1000);
34844         this.setYConstraint(0, 0);
34845         this.minX = x - minw;
34846         this.maxX = x + 1000;
34847         this.startPos = x;
34848         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34849     },
34850
34851
34852     handleMouseDown : function(e){
34853         ev = Roo.EventObject.setEvent(e);
34854         var t = this.fly(ev.getTarget());
34855         if(t.hasClass("x-grid-split")){
34856             this.cellIndex = this.view.getCellIndex(t.dom);
34857             this.split = t.dom;
34858             this.cm = this.grid.colModel;
34859             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34860                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34861             }
34862         }
34863     },
34864
34865     endDrag : function(e){
34866         this.view.headersDisabled = false;
34867         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34868         var diff = endX - this.startPos;
34869         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34870     },
34871
34872     autoOffset : function(){
34873         this.setDelta(0,0);
34874     }
34875 });/*
34876  * Based on:
34877  * Ext JS Library 1.1.1
34878  * Copyright(c) 2006-2007, Ext JS, LLC.
34879  *
34880  * Originally Released Under LGPL - original licence link has changed is not relivant.
34881  *
34882  * Fork - LGPL
34883  * <script type="text/javascript">
34884  */
34885  
34886 // private
34887 // This is a support class used internally by the Grid components
34888 Roo.grid.GridDragZone = function(grid, config){
34889     this.view = grid.getView();
34890     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34891     if(this.view.lockedBody){
34892         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34893         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34894     }
34895     this.scroll = false;
34896     this.grid = grid;
34897     this.ddel = document.createElement('div');
34898     this.ddel.className = 'x-grid-dd-wrap';
34899 };
34900
34901 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34902     ddGroup : "GridDD",
34903
34904     getDragData : function(e){
34905         var t = Roo.lib.Event.getTarget(e);
34906         var rowIndex = this.view.findRowIndex(t);
34907         var sm = this.grid.selModel;
34908             
34909         //Roo.log(rowIndex);
34910         
34911         if (sm.getSelectedCell) {
34912             // cell selection..
34913             if (!sm.getSelectedCell()) {
34914                 return false;
34915             }
34916             if (rowIndex != sm.getSelectedCell()[0]) {
34917                 return false;
34918             }
34919         
34920         }
34921         
34922         if(rowIndex !== false){
34923             
34924             // if editorgrid.. 
34925             
34926             
34927             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34928                
34929             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34930               //  
34931             //}
34932             if (e.hasModifier()){
34933                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34934             }
34935             
34936             Roo.log("getDragData");
34937             
34938             return {
34939                 grid: this.grid,
34940                 ddel: this.ddel,
34941                 rowIndex: rowIndex,
34942                 selections:sm.getSelections ? sm.getSelections() : (
34943                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34944                 )
34945             };
34946         }
34947         return false;
34948     },
34949
34950     onInitDrag : function(e){
34951         var data = this.dragData;
34952         this.ddel.innerHTML = this.grid.getDragDropText();
34953         this.proxy.update(this.ddel);
34954         // fire start drag?
34955     },
34956
34957     afterRepair : function(){
34958         this.dragging = false;
34959     },
34960
34961     getRepairXY : function(e, data){
34962         return false;
34963     },
34964
34965     onEndDrag : function(data, e){
34966         // fire end drag?
34967     },
34968
34969     onValidDrop : function(dd, e, id){
34970         // fire drag drop?
34971         this.hideProxy();
34972     },
34973
34974     beforeInvalidDrop : function(e, id){
34975
34976     }
34977 });/*
34978  * Based on:
34979  * Ext JS Library 1.1.1
34980  * Copyright(c) 2006-2007, Ext JS, LLC.
34981  *
34982  * Originally Released Under LGPL - original licence link has changed is not relivant.
34983  *
34984  * Fork - LGPL
34985  * <script type="text/javascript">
34986  */
34987  
34988
34989 /**
34990  * @class Roo.grid.ColumnModel
34991  * @extends Roo.util.Observable
34992  * This is the default implementation of a ColumnModel used by the Grid. It defines
34993  * the columns in the grid.
34994  * <br>Usage:<br>
34995  <pre><code>
34996  var colModel = new Roo.grid.ColumnModel([
34997         {header: "Ticker", width: 60, sortable: true, locked: true},
34998         {header: "Company Name", width: 150, sortable: true},
34999         {header: "Market Cap.", width: 100, sortable: true},
35000         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35001         {header: "Employees", width: 100, sortable: true, resizable: false}
35002  ]);
35003  </code></pre>
35004  * <p>
35005  
35006  * The config options listed for this class are options which may appear in each
35007  * individual column definition.
35008  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35009  * @constructor
35010  * @param {Object} config An Array of column config objects. See this class's
35011  * config objects for details.
35012 */
35013 Roo.grid.ColumnModel = function(config){
35014         /**
35015      * The config passed into the constructor
35016      */
35017     this.config = config;
35018     this.lookup = {};
35019
35020     // if no id, create one
35021     // if the column does not have a dataIndex mapping,
35022     // map it to the order it is in the config
35023     for(var i = 0, len = config.length; i < len; i++){
35024         var c = config[i];
35025         if(typeof c.dataIndex == "undefined"){
35026             c.dataIndex = i;
35027         }
35028         if(typeof c.renderer == "string"){
35029             c.renderer = Roo.util.Format[c.renderer];
35030         }
35031         if(typeof c.id == "undefined"){
35032             c.id = Roo.id();
35033         }
35034         if(c.editor && c.editor.xtype){
35035             c.editor  = Roo.factory(c.editor, Roo.grid);
35036         }
35037         if(c.editor && c.editor.isFormField){
35038             c.editor = new Roo.grid.GridEditor(c.editor);
35039         }
35040         this.lookup[c.id] = c;
35041     }
35042
35043     /**
35044      * The width of columns which have no width specified (defaults to 100)
35045      * @type Number
35046      */
35047     this.defaultWidth = 100;
35048
35049     /**
35050      * Default sortable of columns which have no sortable specified (defaults to false)
35051      * @type Boolean
35052      */
35053     this.defaultSortable = false;
35054
35055     this.addEvents({
35056         /**
35057              * @event widthchange
35058              * Fires when the width of a column changes.
35059              * @param {ColumnModel} this
35060              * @param {Number} columnIndex The column index
35061              * @param {Number} newWidth The new width
35062              */
35063             "widthchange": true,
35064         /**
35065              * @event headerchange
35066              * Fires when the text of a header changes.
35067              * @param {ColumnModel} this
35068              * @param {Number} columnIndex The column index
35069              * @param {Number} newText The new header text
35070              */
35071             "headerchange": true,
35072         /**
35073              * @event hiddenchange
35074              * Fires when a column is hidden or "unhidden".
35075              * @param {ColumnModel} this
35076              * @param {Number} columnIndex The column index
35077              * @param {Boolean} hidden true if hidden, false otherwise
35078              */
35079             "hiddenchange": true,
35080             /**
35081          * @event columnmoved
35082          * Fires when a column is moved.
35083          * @param {ColumnModel} this
35084          * @param {Number} oldIndex
35085          * @param {Number} newIndex
35086          */
35087         "columnmoved" : true,
35088         /**
35089          * @event columlockchange
35090          * Fires when a column's locked state is changed
35091          * @param {ColumnModel} this
35092          * @param {Number} colIndex
35093          * @param {Boolean} locked true if locked
35094          */
35095         "columnlockchange" : true
35096     });
35097     Roo.grid.ColumnModel.superclass.constructor.call(this);
35098 };
35099 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35100     /**
35101      * @cfg {String} header The header text to display in the Grid view.
35102      */
35103     /**
35104      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35105      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35106      * specified, the column's index is used as an index into the Record's data Array.
35107      */
35108     /**
35109      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35110      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35111      */
35112     /**
35113      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35114      * Defaults to the value of the {@link #defaultSortable} property.
35115      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35116      */
35117     /**
35118      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35119      */
35120     /**
35121      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35122      */
35123     /**
35124      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35125      */
35126     /**
35127      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35128      */
35129     /**
35130      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35131      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35132      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35133      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35134      */
35135        /**
35136      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35137      */
35138     /**
35139      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35140      */
35141     /**
35142      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35143      */
35144     /**
35145      * @cfg {String} cursor (Optional)
35146      */
35147     /**
35148      * @cfg {String} tooltip (Optional)
35149      */
35150     /**
35151      * @cfg {Number} xs (Optional)
35152      */
35153     /**
35154      * @cfg {Number} sm (Optional)
35155      */
35156     /**
35157      * @cfg {Number} md (Optional)
35158      */
35159     /**
35160      * @cfg {Number} lg (Optional)
35161      */
35162     /**
35163      * Returns the id of the column at the specified index.
35164      * @param {Number} index The column index
35165      * @return {String} the id
35166      */
35167     getColumnId : function(index){
35168         return this.config[index].id;
35169     },
35170
35171     /**
35172      * Returns the column for a specified id.
35173      * @param {String} id The column id
35174      * @return {Object} the column
35175      */
35176     getColumnById : function(id){
35177         return this.lookup[id];
35178     },
35179
35180     
35181     /**
35182      * Returns the column for a specified dataIndex.
35183      * @param {String} dataIndex The column dataIndex
35184      * @return {Object|Boolean} the column or false if not found
35185      */
35186     getColumnByDataIndex: function(dataIndex){
35187         var index = this.findColumnIndex(dataIndex);
35188         return index > -1 ? this.config[index] : false;
35189     },
35190     
35191     /**
35192      * Returns the index for a specified column id.
35193      * @param {String} id The column id
35194      * @return {Number} the index, or -1 if not found
35195      */
35196     getIndexById : function(id){
35197         for(var i = 0, len = this.config.length; i < len; i++){
35198             if(this.config[i].id == id){
35199                 return i;
35200             }
35201         }
35202         return -1;
35203     },
35204     
35205     /**
35206      * Returns the index for a specified column dataIndex.
35207      * @param {String} dataIndex The column dataIndex
35208      * @return {Number} the index, or -1 if not found
35209      */
35210     
35211     findColumnIndex : function(dataIndex){
35212         for(var i = 0, len = this.config.length; i < len; i++){
35213             if(this.config[i].dataIndex == dataIndex){
35214                 return i;
35215             }
35216         }
35217         return -1;
35218     },
35219     
35220     
35221     moveColumn : function(oldIndex, newIndex){
35222         var c = this.config[oldIndex];
35223         this.config.splice(oldIndex, 1);
35224         this.config.splice(newIndex, 0, c);
35225         this.dataMap = null;
35226         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35227     },
35228
35229     isLocked : function(colIndex){
35230         return this.config[colIndex].locked === true;
35231     },
35232
35233     setLocked : function(colIndex, value, suppressEvent){
35234         if(this.isLocked(colIndex) == value){
35235             return;
35236         }
35237         this.config[colIndex].locked = value;
35238         if(!suppressEvent){
35239             this.fireEvent("columnlockchange", this, colIndex, value);
35240         }
35241     },
35242
35243     getTotalLockedWidth : function(){
35244         var totalWidth = 0;
35245         for(var i = 0; i < this.config.length; i++){
35246             if(this.isLocked(i) && !this.isHidden(i)){
35247                 this.totalWidth += this.getColumnWidth(i);
35248             }
35249         }
35250         return totalWidth;
35251     },
35252
35253     getLockedCount : function(){
35254         for(var i = 0, len = this.config.length; i < len; i++){
35255             if(!this.isLocked(i)){
35256                 return i;
35257             }
35258         }
35259         
35260         return this.config.length;
35261     },
35262
35263     /**
35264      * Returns the number of columns.
35265      * @return {Number}
35266      */
35267     getColumnCount : function(visibleOnly){
35268         if(visibleOnly === true){
35269             var c = 0;
35270             for(var i = 0, len = this.config.length; i < len; i++){
35271                 if(!this.isHidden(i)){
35272                     c++;
35273                 }
35274             }
35275             return c;
35276         }
35277         return this.config.length;
35278     },
35279
35280     /**
35281      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35282      * @param {Function} fn
35283      * @param {Object} scope (optional)
35284      * @return {Array} result
35285      */
35286     getColumnsBy : function(fn, scope){
35287         var r = [];
35288         for(var i = 0, len = this.config.length; i < len; i++){
35289             var c = this.config[i];
35290             if(fn.call(scope||this, c, i) === true){
35291                 r[r.length] = c;
35292             }
35293         }
35294         return r;
35295     },
35296
35297     /**
35298      * Returns true if the specified column is sortable.
35299      * @param {Number} col The column index
35300      * @return {Boolean}
35301      */
35302     isSortable : function(col){
35303         if(typeof this.config[col].sortable == "undefined"){
35304             return this.defaultSortable;
35305         }
35306         return this.config[col].sortable;
35307     },
35308
35309     /**
35310      * Returns the rendering (formatting) function defined for the column.
35311      * @param {Number} col The column index.
35312      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35313      */
35314     getRenderer : function(col){
35315         if(!this.config[col].renderer){
35316             return Roo.grid.ColumnModel.defaultRenderer;
35317         }
35318         return this.config[col].renderer;
35319     },
35320
35321     /**
35322      * Sets the rendering (formatting) function for a column.
35323      * @param {Number} col The column index
35324      * @param {Function} fn The function to use to process the cell's raw data
35325      * to return HTML markup for the grid view. The render function is called with
35326      * the following parameters:<ul>
35327      * <li>Data value.</li>
35328      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35329      * <li>css A CSS style string to apply to the table cell.</li>
35330      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35331      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35332      * <li>Row index</li>
35333      * <li>Column index</li>
35334      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35335      */
35336     setRenderer : function(col, fn){
35337         this.config[col].renderer = fn;
35338     },
35339
35340     /**
35341      * Returns the width for the specified column.
35342      * @param {Number} col The column index
35343      * @return {Number}
35344      */
35345     getColumnWidth : function(col){
35346         return this.config[col].width * 1 || this.defaultWidth;
35347     },
35348
35349     /**
35350      * Sets the width for a column.
35351      * @param {Number} col The column index
35352      * @param {Number} width The new width
35353      */
35354     setColumnWidth : function(col, width, suppressEvent){
35355         this.config[col].width = width;
35356         this.totalWidth = null;
35357         if(!suppressEvent){
35358              this.fireEvent("widthchange", this, col, width);
35359         }
35360     },
35361
35362     /**
35363      * Returns the total width of all columns.
35364      * @param {Boolean} includeHidden True to include hidden column widths
35365      * @return {Number}
35366      */
35367     getTotalWidth : function(includeHidden){
35368         if(!this.totalWidth){
35369             this.totalWidth = 0;
35370             for(var i = 0, len = this.config.length; i < len; i++){
35371                 if(includeHidden || !this.isHidden(i)){
35372                     this.totalWidth += this.getColumnWidth(i);
35373                 }
35374             }
35375         }
35376         return this.totalWidth;
35377     },
35378
35379     /**
35380      * Returns the header for the specified column.
35381      * @param {Number} col The column index
35382      * @return {String}
35383      */
35384     getColumnHeader : function(col){
35385         return this.config[col].header;
35386     },
35387
35388     /**
35389      * Sets the header for a column.
35390      * @param {Number} col The column index
35391      * @param {String} header The new header
35392      */
35393     setColumnHeader : function(col, header){
35394         this.config[col].header = header;
35395         this.fireEvent("headerchange", this, col, header);
35396     },
35397
35398     /**
35399      * Returns the tooltip for the specified column.
35400      * @param {Number} col The column index
35401      * @return {String}
35402      */
35403     getColumnTooltip : function(col){
35404             return this.config[col].tooltip;
35405     },
35406     /**
35407      * Sets the tooltip for a column.
35408      * @param {Number} col The column index
35409      * @param {String} tooltip The new tooltip
35410      */
35411     setColumnTooltip : function(col, tooltip){
35412             this.config[col].tooltip = tooltip;
35413     },
35414
35415     /**
35416      * Returns the dataIndex for the specified column.
35417      * @param {Number} col The column index
35418      * @return {Number}
35419      */
35420     getDataIndex : function(col){
35421         return this.config[col].dataIndex;
35422     },
35423
35424     /**
35425      * Sets the dataIndex for a column.
35426      * @param {Number} col The column index
35427      * @param {Number} dataIndex The new dataIndex
35428      */
35429     setDataIndex : function(col, dataIndex){
35430         this.config[col].dataIndex = dataIndex;
35431     },
35432
35433     
35434     
35435     /**
35436      * Returns true if the cell is editable.
35437      * @param {Number} colIndex The column index
35438      * @param {Number} rowIndex The row index - this is nto actually used..?
35439      * @return {Boolean}
35440      */
35441     isCellEditable : function(colIndex, rowIndex){
35442         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35443     },
35444
35445     /**
35446      * Returns the editor defined for the cell/column.
35447      * return false or null to disable editing.
35448      * @param {Number} colIndex The column index
35449      * @param {Number} rowIndex The row index
35450      * @return {Object}
35451      */
35452     getCellEditor : function(colIndex, rowIndex){
35453         return this.config[colIndex].editor;
35454     },
35455
35456     /**
35457      * Sets if a column is editable.
35458      * @param {Number} col The column index
35459      * @param {Boolean} editable True if the column is editable
35460      */
35461     setEditable : function(col, editable){
35462         this.config[col].editable = editable;
35463     },
35464
35465
35466     /**
35467      * Returns true if the column is hidden.
35468      * @param {Number} colIndex The column index
35469      * @return {Boolean}
35470      */
35471     isHidden : function(colIndex){
35472         return this.config[colIndex].hidden;
35473     },
35474
35475
35476     /**
35477      * Returns true if the column width cannot be changed
35478      */
35479     isFixed : function(colIndex){
35480         return this.config[colIndex].fixed;
35481     },
35482
35483     /**
35484      * Returns true if the column can be resized
35485      * @return {Boolean}
35486      */
35487     isResizable : function(colIndex){
35488         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35489     },
35490     /**
35491      * Sets if a column is hidden.
35492      * @param {Number} colIndex The column index
35493      * @param {Boolean} hidden True if the column is hidden
35494      */
35495     setHidden : function(colIndex, hidden){
35496         this.config[colIndex].hidden = hidden;
35497         this.totalWidth = null;
35498         this.fireEvent("hiddenchange", this, colIndex, hidden);
35499     },
35500
35501     /**
35502      * Sets the editor for a column.
35503      * @param {Number} col The column index
35504      * @param {Object} editor The editor object
35505      */
35506     setEditor : function(col, editor){
35507         this.config[col].editor = editor;
35508     }
35509 });
35510
35511 Roo.grid.ColumnModel.defaultRenderer = function(value)
35512 {
35513     if(typeof value == "object") {
35514         return value;
35515     }
35516         if(typeof value == "string" && value.length < 1){
35517             return "&#160;";
35518         }
35519     
35520         return String.format("{0}", value);
35521 };
35522
35523 // Alias for backwards compatibility
35524 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35525 /*
35526  * Based on:
35527  * Ext JS Library 1.1.1
35528  * Copyright(c) 2006-2007, Ext JS, LLC.
35529  *
35530  * Originally Released Under LGPL - original licence link has changed is not relivant.
35531  *
35532  * Fork - LGPL
35533  * <script type="text/javascript">
35534  */
35535
35536 /**
35537  * @class Roo.grid.AbstractSelectionModel
35538  * @extends Roo.util.Observable
35539  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35540  * implemented by descendant classes.  This class should not be directly instantiated.
35541  * @constructor
35542  */
35543 Roo.grid.AbstractSelectionModel = function(){
35544     this.locked = false;
35545     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35546 };
35547
35548 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35549     /** @ignore Called by the grid automatically. Do not call directly. */
35550     init : function(grid){
35551         this.grid = grid;
35552         this.initEvents();
35553     },
35554
35555     /**
35556      * Locks the selections.
35557      */
35558     lock : function(){
35559         this.locked = true;
35560     },
35561
35562     /**
35563      * Unlocks the selections.
35564      */
35565     unlock : function(){
35566         this.locked = false;
35567     },
35568
35569     /**
35570      * Returns true if the selections are locked.
35571      * @return {Boolean}
35572      */
35573     isLocked : function(){
35574         return this.locked;
35575     }
35576 });/*
35577  * Based on:
35578  * Ext JS Library 1.1.1
35579  * Copyright(c) 2006-2007, Ext JS, LLC.
35580  *
35581  * Originally Released Under LGPL - original licence link has changed is not relivant.
35582  *
35583  * Fork - LGPL
35584  * <script type="text/javascript">
35585  */
35586 /**
35587  * @extends Roo.grid.AbstractSelectionModel
35588  * @class Roo.grid.RowSelectionModel
35589  * The default SelectionModel used by {@link Roo.grid.Grid}.
35590  * It supports multiple selections and keyboard selection/navigation. 
35591  * @constructor
35592  * @param {Object} config
35593  */
35594 Roo.grid.RowSelectionModel = function(config){
35595     Roo.apply(this, config);
35596     this.selections = new Roo.util.MixedCollection(false, function(o){
35597         return o.id;
35598     });
35599
35600     this.last = false;
35601     this.lastActive = false;
35602
35603     this.addEvents({
35604         /**
35605              * @event selectionchange
35606              * Fires when the selection changes
35607              * @param {SelectionModel} this
35608              */
35609             "selectionchange" : true,
35610         /**
35611              * @event afterselectionchange
35612              * Fires after the selection changes (eg. by key press or clicking)
35613              * @param {SelectionModel} this
35614              */
35615             "afterselectionchange" : true,
35616         /**
35617              * @event beforerowselect
35618              * Fires when a row is selected being selected, return false to cancel.
35619              * @param {SelectionModel} this
35620              * @param {Number} rowIndex The selected index
35621              * @param {Boolean} keepExisting False if other selections will be cleared
35622              */
35623             "beforerowselect" : true,
35624         /**
35625              * @event rowselect
35626              * Fires when a row is selected.
35627              * @param {SelectionModel} this
35628              * @param {Number} rowIndex The selected index
35629              * @param {Roo.data.Record} r The record
35630              */
35631             "rowselect" : true,
35632         /**
35633              * @event rowdeselect
35634              * Fires when a row is deselected.
35635              * @param {SelectionModel} this
35636              * @param {Number} rowIndex The selected index
35637              */
35638         "rowdeselect" : true
35639     });
35640     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35641     this.locked = false;
35642 };
35643
35644 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35645     /**
35646      * @cfg {Boolean} singleSelect
35647      * True to allow selection of only one row at a time (defaults to false)
35648      */
35649     singleSelect : false,
35650
35651     // private
35652     initEvents : function(){
35653
35654         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35655             this.grid.on("mousedown", this.handleMouseDown, this);
35656         }else{ // allow click to work like normal
35657             this.grid.on("rowclick", this.handleDragableRowClick, this);
35658         }
35659
35660         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35661             "up" : function(e){
35662                 if(!e.shiftKey){
35663                     this.selectPrevious(e.shiftKey);
35664                 }else if(this.last !== false && this.lastActive !== false){
35665                     var last = this.last;
35666                     this.selectRange(this.last,  this.lastActive-1);
35667                     this.grid.getView().focusRow(this.lastActive);
35668                     if(last !== false){
35669                         this.last = last;
35670                     }
35671                 }else{
35672                     this.selectFirstRow();
35673                 }
35674                 this.fireEvent("afterselectionchange", this);
35675             },
35676             "down" : function(e){
35677                 if(!e.shiftKey){
35678                     this.selectNext(e.shiftKey);
35679                 }else if(this.last !== false && this.lastActive !== false){
35680                     var last = this.last;
35681                     this.selectRange(this.last,  this.lastActive+1);
35682                     this.grid.getView().focusRow(this.lastActive);
35683                     if(last !== false){
35684                         this.last = last;
35685                     }
35686                 }else{
35687                     this.selectFirstRow();
35688                 }
35689                 this.fireEvent("afterselectionchange", this);
35690             },
35691             scope: this
35692         });
35693
35694         var view = this.grid.view;
35695         view.on("refresh", this.onRefresh, this);
35696         view.on("rowupdated", this.onRowUpdated, this);
35697         view.on("rowremoved", this.onRemove, this);
35698     },
35699
35700     // private
35701     onRefresh : function(){
35702         var ds = this.grid.dataSource, i, v = this.grid.view;
35703         var s = this.selections;
35704         s.each(function(r){
35705             if((i = ds.indexOfId(r.id)) != -1){
35706                 v.onRowSelect(i);
35707                 s.add(ds.getAt(i)); // updating the selection relate data
35708             }else{
35709                 s.remove(r);
35710             }
35711         });
35712     },
35713
35714     // private
35715     onRemove : function(v, index, r){
35716         this.selections.remove(r);
35717     },
35718
35719     // private
35720     onRowUpdated : function(v, index, r){
35721         if(this.isSelected(r)){
35722             v.onRowSelect(index);
35723         }
35724     },
35725
35726     /**
35727      * Select records.
35728      * @param {Array} records The records to select
35729      * @param {Boolean} keepExisting (optional) True to keep existing selections
35730      */
35731     selectRecords : function(records, keepExisting){
35732         if(!keepExisting){
35733             this.clearSelections();
35734         }
35735         var ds = this.grid.dataSource;
35736         for(var i = 0, len = records.length; i < len; i++){
35737             this.selectRow(ds.indexOf(records[i]), true);
35738         }
35739     },
35740
35741     /**
35742      * Gets the number of selected rows.
35743      * @return {Number}
35744      */
35745     getCount : function(){
35746         return this.selections.length;
35747     },
35748
35749     /**
35750      * Selects the first row in the grid.
35751      */
35752     selectFirstRow : function(){
35753         this.selectRow(0);
35754     },
35755
35756     /**
35757      * Select the last row.
35758      * @param {Boolean} keepExisting (optional) True to keep existing selections
35759      */
35760     selectLastRow : function(keepExisting){
35761         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35762     },
35763
35764     /**
35765      * Selects the row immediately following the last selected row.
35766      * @param {Boolean} keepExisting (optional) True to keep existing selections
35767      */
35768     selectNext : function(keepExisting){
35769         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35770             this.selectRow(this.last+1, keepExisting);
35771             this.grid.getView().focusRow(this.last);
35772         }
35773     },
35774
35775     /**
35776      * Selects the row that precedes the last selected row.
35777      * @param {Boolean} keepExisting (optional) True to keep existing selections
35778      */
35779     selectPrevious : function(keepExisting){
35780         if(this.last){
35781             this.selectRow(this.last-1, keepExisting);
35782             this.grid.getView().focusRow(this.last);
35783         }
35784     },
35785
35786     /**
35787      * Returns the selected records
35788      * @return {Array} Array of selected records
35789      */
35790     getSelections : function(){
35791         return [].concat(this.selections.items);
35792     },
35793
35794     /**
35795      * Returns the first selected record.
35796      * @return {Record}
35797      */
35798     getSelected : function(){
35799         return this.selections.itemAt(0);
35800     },
35801
35802
35803     /**
35804      * Clears all selections.
35805      */
35806     clearSelections : function(fast){
35807         if(this.locked) {
35808             return;
35809         }
35810         if(fast !== true){
35811             var ds = this.grid.dataSource;
35812             var s = this.selections;
35813             s.each(function(r){
35814                 this.deselectRow(ds.indexOfId(r.id));
35815             }, this);
35816             s.clear();
35817         }else{
35818             this.selections.clear();
35819         }
35820         this.last = false;
35821     },
35822
35823
35824     /**
35825      * Selects all rows.
35826      */
35827     selectAll : function(){
35828         if(this.locked) {
35829             return;
35830         }
35831         this.selections.clear();
35832         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35833             this.selectRow(i, true);
35834         }
35835     },
35836
35837     /**
35838      * Returns True if there is a selection.
35839      * @return {Boolean}
35840      */
35841     hasSelection : function(){
35842         return this.selections.length > 0;
35843     },
35844
35845     /**
35846      * Returns True if the specified row is selected.
35847      * @param {Number/Record} record The record or index of the record to check
35848      * @return {Boolean}
35849      */
35850     isSelected : function(index){
35851         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35852         return (r && this.selections.key(r.id) ? true : false);
35853     },
35854
35855     /**
35856      * Returns True if the specified record id is selected.
35857      * @param {String} id The id of record to check
35858      * @return {Boolean}
35859      */
35860     isIdSelected : function(id){
35861         return (this.selections.key(id) ? true : false);
35862     },
35863
35864     // private
35865     handleMouseDown : function(e, t){
35866         var view = this.grid.getView(), rowIndex;
35867         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35868             return;
35869         };
35870         if(e.shiftKey && this.last !== false){
35871             var last = this.last;
35872             this.selectRange(last, rowIndex, e.ctrlKey);
35873             this.last = last; // reset the last
35874             view.focusRow(rowIndex);
35875         }else{
35876             var isSelected = this.isSelected(rowIndex);
35877             if(e.button !== 0 && isSelected){
35878                 view.focusRow(rowIndex);
35879             }else if(e.ctrlKey && isSelected){
35880                 this.deselectRow(rowIndex);
35881             }else if(!isSelected){
35882                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35883                 view.focusRow(rowIndex);
35884             }
35885         }
35886         this.fireEvent("afterselectionchange", this);
35887     },
35888     // private
35889     handleDragableRowClick :  function(grid, rowIndex, e) 
35890     {
35891         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35892             this.selectRow(rowIndex, false);
35893             grid.view.focusRow(rowIndex);
35894              this.fireEvent("afterselectionchange", this);
35895         }
35896     },
35897     
35898     /**
35899      * Selects multiple rows.
35900      * @param {Array} rows Array of the indexes of the row to select
35901      * @param {Boolean} keepExisting (optional) True to keep existing selections
35902      */
35903     selectRows : function(rows, keepExisting){
35904         if(!keepExisting){
35905             this.clearSelections();
35906         }
35907         for(var i = 0, len = rows.length; i < len; i++){
35908             this.selectRow(rows[i], true);
35909         }
35910     },
35911
35912     /**
35913      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35914      * @param {Number} startRow The index of the first row in the range
35915      * @param {Number} endRow The index of the last row in the range
35916      * @param {Boolean} keepExisting (optional) True to retain existing selections
35917      */
35918     selectRange : function(startRow, endRow, keepExisting){
35919         if(this.locked) {
35920             return;
35921         }
35922         if(!keepExisting){
35923             this.clearSelections();
35924         }
35925         if(startRow <= endRow){
35926             for(var i = startRow; i <= endRow; i++){
35927                 this.selectRow(i, true);
35928             }
35929         }else{
35930             for(var i = startRow; i >= endRow; i--){
35931                 this.selectRow(i, true);
35932             }
35933         }
35934     },
35935
35936     /**
35937      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35938      * @param {Number} startRow The index of the first row in the range
35939      * @param {Number} endRow The index of the last row in the range
35940      */
35941     deselectRange : function(startRow, endRow, preventViewNotify){
35942         if(this.locked) {
35943             return;
35944         }
35945         for(var i = startRow; i <= endRow; i++){
35946             this.deselectRow(i, preventViewNotify);
35947         }
35948     },
35949
35950     /**
35951      * Selects a row.
35952      * @param {Number} row The index of the row to select
35953      * @param {Boolean} keepExisting (optional) True to keep existing selections
35954      */
35955     selectRow : function(index, keepExisting, preventViewNotify){
35956         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35957             return;
35958         }
35959         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35960             if(!keepExisting || this.singleSelect){
35961                 this.clearSelections();
35962             }
35963             var r = this.grid.dataSource.getAt(index);
35964             this.selections.add(r);
35965             this.last = this.lastActive = index;
35966             if(!preventViewNotify){
35967                 this.grid.getView().onRowSelect(index);
35968             }
35969             this.fireEvent("rowselect", this, index, r);
35970             this.fireEvent("selectionchange", this);
35971         }
35972     },
35973
35974     /**
35975      * Deselects a row.
35976      * @param {Number} row The index of the row to deselect
35977      */
35978     deselectRow : function(index, preventViewNotify){
35979         if(this.locked) {
35980             return;
35981         }
35982         if(this.last == index){
35983             this.last = false;
35984         }
35985         if(this.lastActive == index){
35986             this.lastActive = false;
35987         }
35988         var r = this.grid.dataSource.getAt(index);
35989         this.selections.remove(r);
35990         if(!preventViewNotify){
35991             this.grid.getView().onRowDeselect(index);
35992         }
35993         this.fireEvent("rowdeselect", this, index);
35994         this.fireEvent("selectionchange", this);
35995     },
35996
35997     // private
35998     restoreLast : function(){
35999         if(this._last){
36000             this.last = this._last;
36001         }
36002     },
36003
36004     // private
36005     acceptsNav : function(row, col, cm){
36006         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36007     },
36008
36009     // private
36010     onEditorKey : function(field, e){
36011         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36012         if(k == e.TAB){
36013             e.stopEvent();
36014             ed.completeEdit();
36015             if(e.shiftKey){
36016                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36017             }else{
36018                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36019             }
36020         }else if(k == e.ENTER && !e.ctrlKey){
36021             e.stopEvent();
36022             ed.completeEdit();
36023             if(e.shiftKey){
36024                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36025             }else{
36026                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36027             }
36028         }else if(k == e.ESC){
36029             ed.cancelEdit();
36030         }
36031         if(newCell){
36032             g.startEditing(newCell[0], newCell[1]);
36033         }
36034     }
36035 });/*
36036  * Based on:
36037  * Ext JS Library 1.1.1
36038  * Copyright(c) 2006-2007, Ext JS, LLC.
36039  *
36040  * Originally Released Under LGPL - original licence link has changed is not relivant.
36041  *
36042  * Fork - LGPL
36043  * <script type="text/javascript">
36044  */
36045 /**
36046  * @class Roo.grid.CellSelectionModel
36047  * @extends Roo.grid.AbstractSelectionModel
36048  * This class provides the basic implementation for cell selection in a grid.
36049  * @constructor
36050  * @param {Object} config The object containing the configuration of this model.
36051  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36052  */
36053 Roo.grid.CellSelectionModel = function(config){
36054     Roo.apply(this, config);
36055
36056     this.selection = null;
36057
36058     this.addEvents({
36059         /**
36060              * @event beforerowselect
36061              * Fires before a cell is selected.
36062              * @param {SelectionModel} this
36063              * @param {Number} rowIndex The selected row index
36064              * @param {Number} colIndex The selected cell index
36065              */
36066             "beforecellselect" : true,
36067         /**
36068              * @event cellselect
36069              * Fires when a cell is selected.
36070              * @param {SelectionModel} this
36071              * @param {Number} rowIndex The selected row index
36072              * @param {Number} colIndex The selected cell index
36073              */
36074             "cellselect" : true,
36075         /**
36076              * @event selectionchange
36077              * Fires when the active selection changes.
36078              * @param {SelectionModel} this
36079              * @param {Object} selection null for no selection or an object (o) with two properties
36080                 <ul>
36081                 <li>o.record: the record object for the row the selection is in</li>
36082                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36083                 </ul>
36084              */
36085             "selectionchange" : true,
36086         /**
36087              * @event tabend
36088              * Fires when the tab (or enter) was pressed on the last editable cell
36089              * You can use this to trigger add new row.
36090              * @param {SelectionModel} this
36091              */
36092             "tabend" : true,
36093          /**
36094              * @event beforeeditnext
36095              * Fires before the next editable sell is made active
36096              * You can use this to skip to another cell or fire the tabend
36097              *    if you set cell to false
36098              * @param {Object} eventdata object : { cell : [ row, col ] } 
36099              */
36100             "beforeeditnext" : true
36101     });
36102     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36103 };
36104
36105 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36106     
36107     enter_is_tab: false,
36108
36109     /** @ignore */
36110     initEvents : function(){
36111         this.grid.on("mousedown", this.handleMouseDown, this);
36112         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36113         var view = this.grid.view;
36114         view.on("refresh", this.onViewChange, this);
36115         view.on("rowupdated", this.onRowUpdated, this);
36116         view.on("beforerowremoved", this.clearSelections, this);
36117         view.on("beforerowsinserted", this.clearSelections, this);
36118         if(this.grid.isEditor){
36119             this.grid.on("beforeedit", this.beforeEdit,  this);
36120         }
36121     },
36122
36123         //private
36124     beforeEdit : function(e){
36125         this.select(e.row, e.column, false, true, e.record);
36126     },
36127
36128         //private
36129     onRowUpdated : function(v, index, r){
36130         if(this.selection && this.selection.record == r){
36131             v.onCellSelect(index, this.selection.cell[1]);
36132         }
36133     },
36134
36135         //private
36136     onViewChange : function(){
36137         this.clearSelections(true);
36138     },
36139
36140         /**
36141          * Returns the currently selected cell,.
36142          * @return {Array} The selected cell (row, column) or null if none selected.
36143          */
36144     getSelectedCell : function(){
36145         return this.selection ? this.selection.cell : null;
36146     },
36147
36148     /**
36149      * Clears all selections.
36150      * @param {Boolean} true to prevent the gridview from being notified about the change.
36151      */
36152     clearSelections : function(preventNotify){
36153         var s = this.selection;
36154         if(s){
36155             if(preventNotify !== true){
36156                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36157             }
36158             this.selection = null;
36159             this.fireEvent("selectionchange", this, null);
36160         }
36161     },
36162
36163     /**
36164      * Returns true if there is a selection.
36165      * @return {Boolean}
36166      */
36167     hasSelection : function(){
36168         return this.selection ? true : false;
36169     },
36170
36171     /** @ignore */
36172     handleMouseDown : function(e, t){
36173         var v = this.grid.getView();
36174         if(this.isLocked()){
36175             return;
36176         };
36177         var row = v.findRowIndex(t);
36178         var cell = v.findCellIndex(t);
36179         if(row !== false && cell !== false){
36180             this.select(row, cell);
36181         }
36182     },
36183
36184     /**
36185      * Selects a cell.
36186      * @param {Number} rowIndex
36187      * @param {Number} collIndex
36188      */
36189     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36190         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36191             this.clearSelections();
36192             r = r || this.grid.dataSource.getAt(rowIndex);
36193             this.selection = {
36194                 record : r,
36195                 cell : [rowIndex, colIndex]
36196             };
36197             if(!preventViewNotify){
36198                 var v = this.grid.getView();
36199                 v.onCellSelect(rowIndex, colIndex);
36200                 if(preventFocus !== true){
36201                     v.focusCell(rowIndex, colIndex);
36202                 }
36203             }
36204             this.fireEvent("cellselect", this, rowIndex, colIndex);
36205             this.fireEvent("selectionchange", this, this.selection);
36206         }
36207     },
36208
36209         //private
36210     isSelectable : function(rowIndex, colIndex, cm){
36211         return !cm.isHidden(colIndex);
36212     },
36213
36214     /** @ignore */
36215     handleKeyDown : function(e){
36216         //Roo.log('Cell Sel Model handleKeyDown');
36217         if(!e.isNavKeyPress()){
36218             return;
36219         }
36220         var g = this.grid, s = this.selection;
36221         if(!s){
36222             e.stopEvent();
36223             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36224             if(cell){
36225                 this.select(cell[0], cell[1]);
36226             }
36227             return;
36228         }
36229         var sm = this;
36230         var walk = function(row, col, step){
36231             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36232         };
36233         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36234         var newCell;
36235
36236       
36237
36238         switch(k){
36239             case e.TAB:
36240                 // handled by onEditorKey
36241                 if (g.isEditor && g.editing) {
36242                     return;
36243                 }
36244                 if(e.shiftKey) {
36245                     newCell = walk(r, c-1, -1);
36246                 } else {
36247                     newCell = walk(r, c+1, 1);
36248                 }
36249                 break;
36250             
36251             case e.DOWN:
36252                newCell = walk(r+1, c, 1);
36253                 break;
36254             
36255             case e.UP:
36256                 newCell = walk(r-1, c, -1);
36257                 break;
36258             
36259             case e.RIGHT:
36260                 newCell = walk(r, c+1, 1);
36261                 break;
36262             
36263             case e.LEFT:
36264                 newCell = walk(r, c-1, -1);
36265                 break;
36266             
36267             case e.ENTER:
36268                 
36269                 if(g.isEditor && !g.editing){
36270                    g.startEditing(r, c);
36271                    e.stopEvent();
36272                    return;
36273                 }
36274                 
36275                 
36276              break;
36277         };
36278         if(newCell){
36279             this.select(newCell[0], newCell[1]);
36280             e.stopEvent();
36281             
36282         }
36283     },
36284
36285     acceptsNav : function(row, col, cm){
36286         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36287     },
36288     /**
36289      * Selects a cell.
36290      * @param {Number} field (not used) - as it's normally used as a listener
36291      * @param {Number} e - event - fake it by using
36292      *
36293      * var e = Roo.EventObjectImpl.prototype;
36294      * e.keyCode = e.TAB
36295      *
36296      * 
36297      */
36298     onEditorKey : function(field, e){
36299         
36300         var k = e.getKey(),
36301             newCell,
36302             g = this.grid,
36303             ed = g.activeEditor,
36304             forward = false;
36305         ///Roo.log('onEditorKey' + k);
36306         
36307         
36308         if (this.enter_is_tab && k == e.ENTER) {
36309             k = e.TAB;
36310         }
36311         
36312         if(k == e.TAB){
36313             if(e.shiftKey){
36314                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36315             }else{
36316                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36317                 forward = true;
36318             }
36319             
36320             e.stopEvent();
36321             
36322         } else if(k == e.ENTER &&  !e.ctrlKey){
36323             ed.completeEdit();
36324             e.stopEvent();
36325             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36326         
36327                 } else if(k == e.ESC){
36328             ed.cancelEdit();
36329         }
36330                 
36331         if (newCell) {
36332             var ecall = { cell : newCell, forward : forward };
36333             this.fireEvent('beforeeditnext', ecall );
36334             newCell = ecall.cell;
36335                         forward = ecall.forward;
36336         }
36337                 
36338         if(newCell){
36339             //Roo.log('next cell after edit');
36340             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36341         } else if (forward) {
36342             // tabbed past last
36343             this.fireEvent.defer(100, this, ['tabend',this]);
36344         }
36345     }
36346 });/*
36347  * Based on:
36348  * Ext JS Library 1.1.1
36349  * Copyright(c) 2006-2007, Ext JS, LLC.
36350  *
36351  * Originally Released Under LGPL - original licence link has changed is not relivant.
36352  *
36353  * Fork - LGPL
36354  * <script type="text/javascript">
36355  */
36356  
36357 /**
36358  * @class Roo.grid.EditorGrid
36359  * @extends Roo.grid.Grid
36360  * Class for creating and editable grid.
36361  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36362  * The container MUST have some type of size defined for the grid to fill. The container will be 
36363  * automatically set to position relative if it isn't already.
36364  * @param {Object} dataSource The data model to bind to
36365  * @param {Object} colModel The column model with info about this grid's columns
36366  */
36367 Roo.grid.EditorGrid = function(container, config){
36368     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36369     this.getGridEl().addClass("xedit-grid");
36370
36371     if(!this.selModel){
36372         this.selModel = new Roo.grid.CellSelectionModel();
36373     }
36374
36375     this.activeEditor = null;
36376
36377         this.addEvents({
36378             /**
36379              * @event beforeedit
36380              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36381              * <ul style="padding:5px;padding-left:16px;">
36382              * <li>grid - This grid</li>
36383              * <li>record - The record being edited</li>
36384              * <li>field - The field name being edited</li>
36385              * <li>value - The value for the field being edited.</li>
36386              * <li>row - The grid row index</li>
36387              * <li>column - The grid column index</li>
36388              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36389              * </ul>
36390              * @param {Object} e An edit event (see above for description)
36391              */
36392             "beforeedit" : true,
36393             /**
36394              * @event afteredit
36395              * Fires after a cell is edited. <br />
36396              * <ul style="padding:5px;padding-left:16px;">
36397              * <li>grid - This grid</li>
36398              * <li>record - The record being edited</li>
36399              * <li>field - The field name being edited</li>
36400              * <li>value - The value being set</li>
36401              * <li>originalValue - The original value for the field, before the edit.</li>
36402              * <li>row - The grid row index</li>
36403              * <li>column - The grid column index</li>
36404              * </ul>
36405              * @param {Object} e An edit event (see above for description)
36406              */
36407             "afteredit" : true,
36408             /**
36409              * @event validateedit
36410              * Fires after a cell is edited, but before the value is set in the record. 
36411          * You can use this to modify the value being set in the field, Return false
36412              * to cancel the change. The edit event object has the following properties <br />
36413              * <ul style="padding:5px;padding-left:16px;">
36414          * <li>editor - This editor</li>
36415              * <li>grid - This grid</li>
36416              * <li>record - The record being edited</li>
36417              * <li>field - The field name being edited</li>
36418              * <li>value - The value being set</li>
36419              * <li>originalValue - The original value for the field, before the edit.</li>
36420              * <li>row - The grid row index</li>
36421              * <li>column - The grid column index</li>
36422              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36423              * </ul>
36424              * @param {Object} e An edit event (see above for description)
36425              */
36426             "validateedit" : true
36427         });
36428     this.on("bodyscroll", this.stopEditing,  this);
36429     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36430 };
36431
36432 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36433     /**
36434      * @cfg {Number} clicksToEdit
36435      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36436      */
36437     clicksToEdit: 2,
36438
36439     // private
36440     isEditor : true,
36441     // private
36442     trackMouseOver: false, // causes very odd FF errors
36443
36444     onCellDblClick : function(g, row, col){
36445         this.startEditing(row, col);
36446     },
36447
36448     onEditComplete : function(ed, value, startValue){
36449         this.editing = false;
36450         this.activeEditor = null;
36451         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36452         var r = ed.record;
36453         var field = this.colModel.getDataIndex(ed.col);
36454         var e = {
36455             grid: this,
36456             record: r,
36457             field: field,
36458             originalValue: startValue,
36459             value: value,
36460             row: ed.row,
36461             column: ed.col,
36462             cancel:false,
36463             editor: ed
36464         };
36465         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36466         cell.show();
36467           
36468         if(String(value) !== String(startValue)){
36469             
36470             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36471                 r.set(field, e.value);
36472                 // if we are dealing with a combo box..
36473                 // then we also set the 'name' colum to be the displayField
36474                 if (ed.field.displayField && ed.field.name) {
36475                     r.set(ed.field.name, ed.field.el.dom.value);
36476                 }
36477                 
36478                 delete e.cancel; //?? why!!!
36479                 this.fireEvent("afteredit", e);
36480             }
36481         } else {
36482             this.fireEvent("afteredit", e); // always fire it!
36483         }
36484         this.view.focusCell(ed.row, ed.col);
36485     },
36486
36487     /**
36488      * Starts editing the specified for the specified row/column
36489      * @param {Number} rowIndex
36490      * @param {Number} colIndex
36491      */
36492     startEditing : function(row, col){
36493         this.stopEditing();
36494         if(this.colModel.isCellEditable(col, row)){
36495             this.view.ensureVisible(row, col, true);
36496           
36497             var r = this.dataSource.getAt(row);
36498             var field = this.colModel.getDataIndex(col);
36499             var cell = Roo.get(this.view.getCell(row,col));
36500             var e = {
36501                 grid: this,
36502                 record: r,
36503                 field: field,
36504                 value: r.data[field],
36505                 row: row,
36506                 column: col,
36507                 cancel:false 
36508             };
36509             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36510                 this.editing = true;
36511                 var ed = this.colModel.getCellEditor(col, row);
36512                 
36513                 if (!ed) {
36514                     return;
36515                 }
36516                 if(!ed.rendered){
36517                     ed.render(ed.parentEl || document.body);
36518                 }
36519                 ed.field.reset();
36520                
36521                 cell.hide();
36522                 
36523                 (function(){ // complex but required for focus issues in safari, ie and opera
36524                     ed.row = row;
36525                     ed.col = col;
36526                     ed.record = r;
36527                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36528                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36529                     this.activeEditor = ed;
36530                     var v = r.data[field];
36531                     ed.startEdit(this.view.getCell(row, col), v);
36532                     // combo's with 'displayField and name set
36533                     if (ed.field.displayField && ed.field.name) {
36534                         ed.field.el.dom.value = r.data[ed.field.name];
36535                     }
36536                     
36537                     
36538                 }).defer(50, this);
36539             }
36540         }
36541     },
36542         
36543     /**
36544      * Stops any active editing
36545      */
36546     stopEditing : function(){
36547         if(this.activeEditor){
36548             this.activeEditor.completeEdit();
36549         }
36550         this.activeEditor = null;
36551     },
36552         
36553          /**
36554      * Called to get grid's drag proxy text, by default returns this.ddText.
36555      * @return {String}
36556      */
36557     getDragDropText : function(){
36558         var count = this.selModel.getSelectedCell() ? 1 : 0;
36559         return String.format(this.ddText, count, count == 1 ? '' : 's');
36560     }
36561         
36562 });/*
36563  * Based on:
36564  * Ext JS Library 1.1.1
36565  * Copyright(c) 2006-2007, Ext JS, LLC.
36566  *
36567  * Originally Released Under LGPL - original licence link has changed is not relivant.
36568  *
36569  * Fork - LGPL
36570  * <script type="text/javascript">
36571  */
36572
36573 // private - not really -- you end up using it !
36574 // This is a support class used internally by the Grid components
36575
36576 /**
36577  * @class Roo.grid.GridEditor
36578  * @extends Roo.Editor
36579  * Class for creating and editable grid elements.
36580  * @param {Object} config any settings (must include field)
36581  */
36582 Roo.grid.GridEditor = function(field, config){
36583     if (!config && field.field) {
36584         config = field;
36585         field = Roo.factory(config.field, Roo.form);
36586     }
36587     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36588     field.monitorTab = false;
36589 };
36590
36591 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36592     
36593     /**
36594      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36595      */
36596     
36597     alignment: "tl-tl",
36598     autoSize: "width",
36599     hideEl : false,
36600     cls: "x-small-editor x-grid-editor",
36601     shim:false,
36602     shadow:"frame"
36603 });/*
36604  * Based on:
36605  * Ext JS Library 1.1.1
36606  * Copyright(c) 2006-2007, Ext JS, LLC.
36607  *
36608  * Originally Released Under LGPL - original licence link has changed is not relivant.
36609  *
36610  * Fork - LGPL
36611  * <script type="text/javascript">
36612  */
36613   
36614
36615   
36616 Roo.grid.PropertyRecord = Roo.data.Record.create([
36617     {name:'name',type:'string'},  'value'
36618 ]);
36619
36620
36621 Roo.grid.PropertyStore = function(grid, source){
36622     this.grid = grid;
36623     this.store = new Roo.data.Store({
36624         recordType : Roo.grid.PropertyRecord
36625     });
36626     this.store.on('update', this.onUpdate,  this);
36627     if(source){
36628         this.setSource(source);
36629     }
36630     Roo.grid.PropertyStore.superclass.constructor.call(this);
36631 };
36632
36633
36634
36635 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36636     setSource : function(o){
36637         this.source = o;
36638         this.store.removeAll();
36639         var data = [];
36640         for(var k in o){
36641             if(this.isEditableValue(o[k])){
36642                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36643             }
36644         }
36645         this.store.loadRecords({records: data}, {}, true);
36646     },
36647
36648     onUpdate : function(ds, record, type){
36649         if(type == Roo.data.Record.EDIT){
36650             var v = record.data['value'];
36651             var oldValue = record.modified['value'];
36652             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36653                 this.source[record.id] = v;
36654                 record.commit();
36655                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36656             }else{
36657                 record.reject();
36658             }
36659         }
36660     },
36661
36662     getProperty : function(row){
36663        return this.store.getAt(row);
36664     },
36665
36666     isEditableValue: function(val){
36667         if(val && val instanceof Date){
36668             return true;
36669         }else if(typeof val == 'object' || typeof val == 'function'){
36670             return false;
36671         }
36672         return true;
36673     },
36674
36675     setValue : function(prop, value){
36676         this.source[prop] = value;
36677         this.store.getById(prop).set('value', value);
36678     },
36679
36680     getSource : function(){
36681         return this.source;
36682     }
36683 });
36684
36685 Roo.grid.PropertyColumnModel = function(grid, store){
36686     this.grid = grid;
36687     var g = Roo.grid;
36688     g.PropertyColumnModel.superclass.constructor.call(this, [
36689         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36690         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36691     ]);
36692     this.store = store;
36693     this.bselect = Roo.DomHelper.append(document.body, {
36694         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36695             {tag: 'option', value: 'true', html: 'true'},
36696             {tag: 'option', value: 'false', html: 'false'}
36697         ]
36698     });
36699     Roo.id(this.bselect);
36700     var f = Roo.form;
36701     this.editors = {
36702         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36703         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36704         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36705         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36706         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36707     };
36708     this.renderCellDelegate = this.renderCell.createDelegate(this);
36709     this.renderPropDelegate = this.renderProp.createDelegate(this);
36710 };
36711
36712 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36713     
36714     
36715     nameText : 'Name',
36716     valueText : 'Value',
36717     
36718     dateFormat : 'm/j/Y',
36719     
36720     
36721     renderDate : function(dateVal){
36722         return dateVal.dateFormat(this.dateFormat);
36723     },
36724
36725     renderBool : function(bVal){
36726         return bVal ? 'true' : 'false';
36727     },
36728
36729     isCellEditable : function(colIndex, rowIndex){
36730         return colIndex == 1;
36731     },
36732
36733     getRenderer : function(col){
36734         return col == 1 ?
36735             this.renderCellDelegate : this.renderPropDelegate;
36736     },
36737
36738     renderProp : function(v){
36739         return this.getPropertyName(v);
36740     },
36741
36742     renderCell : function(val){
36743         var rv = val;
36744         if(val instanceof Date){
36745             rv = this.renderDate(val);
36746         }else if(typeof val == 'boolean'){
36747             rv = this.renderBool(val);
36748         }
36749         return Roo.util.Format.htmlEncode(rv);
36750     },
36751
36752     getPropertyName : function(name){
36753         var pn = this.grid.propertyNames;
36754         return pn && pn[name] ? pn[name] : name;
36755     },
36756
36757     getCellEditor : function(colIndex, rowIndex){
36758         var p = this.store.getProperty(rowIndex);
36759         var n = p.data['name'], val = p.data['value'];
36760         
36761         if(typeof(this.grid.customEditors[n]) == 'string'){
36762             return this.editors[this.grid.customEditors[n]];
36763         }
36764         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36765             return this.grid.customEditors[n];
36766         }
36767         if(val instanceof Date){
36768             return this.editors['date'];
36769         }else if(typeof val == 'number'){
36770             return this.editors['number'];
36771         }else if(typeof val == 'boolean'){
36772             return this.editors['boolean'];
36773         }else{
36774             return this.editors['string'];
36775         }
36776     }
36777 });
36778
36779 /**
36780  * @class Roo.grid.PropertyGrid
36781  * @extends Roo.grid.EditorGrid
36782  * This class represents the  interface of a component based property grid control.
36783  * <br><br>Usage:<pre><code>
36784  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36785       
36786  });
36787  // set any options
36788  grid.render();
36789  * </code></pre>
36790   
36791  * @constructor
36792  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36793  * The container MUST have some type of size defined for the grid to fill. The container will be
36794  * automatically set to position relative if it isn't already.
36795  * @param {Object} config A config object that sets properties on this grid.
36796  */
36797 Roo.grid.PropertyGrid = function(container, config){
36798     config = config || {};
36799     var store = new Roo.grid.PropertyStore(this);
36800     this.store = store;
36801     var cm = new Roo.grid.PropertyColumnModel(this, store);
36802     store.store.sort('name', 'ASC');
36803     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36804         ds: store.store,
36805         cm: cm,
36806         enableColLock:false,
36807         enableColumnMove:false,
36808         stripeRows:false,
36809         trackMouseOver: false,
36810         clicksToEdit:1
36811     }, config));
36812     this.getGridEl().addClass('x-props-grid');
36813     this.lastEditRow = null;
36814     this.on('columnresize', this.onColumnResize, this);
36815     this.addEvents({
36816          /**
36817              * @event beforepropertychange
36818              * Fires before a property changes (return false to stop?)
36819              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36820              * @param {String} id Record Id
36821              * @param {String} newval New Value
36822          * @param {String} oldval Old Value
36823              */
36824         "beforepropertychange": true,
36825         /**
36826              * @event propertychange
36827              * Fires after a property changes
36828              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36829              * @param {String} id Record Id
36830              * @param {String} newval New Value
36831          * @param {String} oldval Old Value
36832              */
36833         "propertychange": true
36834     });
36835     this.customEditors = this.customEditors || {};
36836 };
36837 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36838     
36839      /**
36840      * @cfg {Object} customEditors map of colnames=> custom editors.
36841      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36842      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36843      * false disables editing of the field.
36844          */
36845     
36846       /**
36847      * @cfg {Object} propertyNames map of property Names to their displayed value
36848          */
36849     
36850     render : function(){
36851         Roo.grid.PropertyGrid.superclass.render.call(this);
36852         this.autoSize.defer(100, this);
36853     },
36854
36855     autoSize : function(){
36856         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36857         if(this.view){
36858             this.view.fitColumns();
36859         }
36860     },
36861
36862     onColumnResize : function(){
36863         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36864         this.autoSize();
36865     },
36866     /**
36867      * Sets the data for the Grid
36868      * accepts a Key => Value object of all the elements avaiable.
36869      * @param {Object} data  to appear in grid.
36870      */
36871     setSource : function(source){
36872         this.store.setSource(source);
36873         //this.autoSize();
36874     },
36875     /**
36876      * Gets all the data from the grid.
36877      * @return {Object} data  data stored in grid
36878      */
36879     getSource : function(){
36880         return this.store.getSource();
36881     }
36882 });/*
36883   
36884  * Licence LGPL
36885  
36886  */
36887  
36888 /**
36889  * @class Roo.grid.Calendar
36890  * @extends Roo.util.Grid
36891  * This class extends the Grid to provide a calendar widget
36892  * <br><br>Usage:<pre><code>
36893  var grid = new Roo.grid.Calendar("my-container-id", {
36894      ds: myDataStore,
36895      cm: myColModel,
36896      selModel: mySelectionModel,
36897      autoSizeColumns: true,
36898      monitorWindowResize: false,
36899      trackMouseOver: true
36900      eventstore : real data store..
36901  });
36902  // set any options
36903  grid.render();
36904   
36905   * @constructor
36906  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36907  * The container MUST have some type of size defined for the grid to fill. The container will be
36908  * automatically set to position relative if it isn't already.
36909  * @param {Object} config A config object that sets properties on this grid.
36910  */
36911 Roo.grid.Calendar = function(container, config){
36912         // initialize the container
36913         this.container = Roo.get(container);
36914         this.container.update("");
36915         this.container.setStyle("overflow", "hidden");
36916     this.container.addClass('x-grid-container');
36917
36918     this.id = this.container.id;
36919
36920     Roo.apply(this, config);
36921     // check and correct shorthanded configs
36922     
36923     var rows = [];
36924     var d =1;
36925     for (var r = 0;r < 6;r++) {
36926         
36927         rows[r]=[];
36928         for (var c =0;c < 7;c++) {
36929             rows[r][c]= '';
36930         }
36931     }
36932     if (this.eventStore) {
36933         this.eventStore= Roo.factory(this.eventStore, Roo.data);
36934         this.eventStore.on('load',this.onLoad, this);
36935         this.eventStore.on('beforeload',this.clearEvents, this);
36936          
36937     }
36938     
36939     this.dataSource = new Roo.data.Store({
36940             proxy: new Roo.data.MemoryProxy(rows),
36941             reader: new Roo.data.ArrayReader({}, [
36942                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36943     });
36944
36945     this.dataSource.load();
36946     this.ds = this.dataSource;
36947     this.ds.xmodule = this.xmodule || false;
36948     
36949     
36950     var cellRender = function(v,x,r)
36951     {
36952         return String.format(
36953             '<div class="fc-day  fc-widget-content"><div>' +
36954                 '<div class="fc-event-container"></div>' +
36955                 '<div class="fc-day-number">{0}</div>'+
36956                 
36957                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36958             '</div></div>', v);
36959     
36960     }
36961     
36962     
36963     this.colModel = new Roo.grid.ColumnModel( [
36964         {
36965             xtype: 'ColumnModel',
36966             xns: Roo.grid,
36967             dataIndex : 'weekday0',
36968             header : 'Sunday',
36969             renderer : cellRender
36970         },
36971         {
36972             xtype: 'ColumnModel',
36973             xns: Roo.grid,
36974             dataIndex : 'weekday1',
36975             header : 'Monday',
36976             renderer : cellRender
36977         },
36978         {
36979             xtype: 'ColumnModel',
36980             xns: Roo.grid,
36981             dataIndex : 'weekday2',
36982             header : 'Tuesday',
36983             renderer : cellRender
36984         },
36985         {
36986             xtype: 'ColumnModel',
36987             xns: Roo.grid,
36988             dataIndex : 'weekday3',
36989             header : 'Wednesday',
36990             renderer : cellRender
36991         },
36992         {
36993             xtype: 'ColumnModel',
36994             xns: Roo.grid,
36995             dataIndex : 'weekday4',
36996             header : 'Thursday',
36997             renderer : cellRender
36998         },
36999         {
37000             xtype: 'ColumnModel',
37001             xns: Roo.grid,
37002             dataIndex : 'weekday5',
37003             header : 'Friday',
37004             renderer : cellRender
37005         },
37006         {
37007             xtype: 'ColumnModel',
37008             xns: Roo.grid,
37009             dataIndex : 'weekday6',
37010             header : 'Saturday',
37011             renderer : cellRender
37012         }
37013     ]);
37014     this.cm = this.colModel;
37015     this.cm.xmodule = this.xmodule || false;
37016  
37017         
37018           
37019     //this.selModel = new Roo.grid.CellSelectionModel();
37020     //this.sm = this.selModel;
37021     //this.selModel.init(this);
37022     
37023     
37024     if(this.width){
37025         this.container.setWidth(this.width);
37026     }
37027
37028     if(this.height){
37029         this.container.setHeight(this.height);
37030     }
37031     /** @private */
37032         this.addEvents({
37033         // raw events
37034         /**
37035          * @event click
37036          * The raw click event for the entire grid.
37037          * @param {Roo.EventObject} e
37038          */
37039         "click" : true,
37040         /**
37041          * @event dblclick
37042          * The raw dblclick event for the entire grid.
37043          * @param {Roo.EventObject} e
37044          */
37045         "dblclick" : true,
37046         /**
37047          * @event contextmenu
37048          * The raw contextmenu event for the entire grid.
37049          * @param {Roo.EventObject} e
37050          */
37051         "contextmenu" : true,
37052         /**
37053          * @event mousedown
37054          * The raw mousedown event for the entire grid.
37055          * @param {Roo.EventObject} e
37056          */
37057         "mousedown" : true,
37058         /**
37059          * @event mouseup
37060          * The raw mouseup event for the entire grid.
37061          * @param {Roo.EventObject} e
37062          */
37063         "mouseup" : true,
37064         /**
37065          * @event mouseover
37066          * The raw mouseover event for the entire grid.
37067          * @param {Roo.EventObject} e
37068          */
37069         "mouseover" : true,
37070         /**
37071          * @event mouseout
37072          * The raw mouseout event for the entire grid.
37073          * @param {Roo.EventObject} e
37074          */
37075         "mouseout" : true,
37076         /**
37077          * @event keypress
37078          * The raw keypress event for the entire grid.
37079          * @param {Roo.EventObject} e
37080          */
37081         "keypress" : true,
37082         /**
37083          * @event keydown
37084          * The raw keydown event for the entire grid.
37085          * @param {Roo.EventObject} e
37086          */
37087         "keydown" : true,
37088
37089         // custom events
37090
37091         /**
37092          * @event cellclick
37093          * Fires when a cell is clicked
37094          * @param {Grid} this
37095          * @param {Number} rowIndex
37096          * @param {Number} columnIndex
37097          * @param {Roo.EventObject} e
37098          */
37099         "cellclick" : true,
37100         /**
37101          * @event celldblclick
37102          * Fires when a cell is double clicked
37103          * @param {Grid} this
37104          * @param {Number} rowIndex
37105          * @param {Number} columnIndex
37106          * @param {Roo.EventObject} e
37107          */
37108         "celldblclick" : true,
37109         /**
37110          * @event rowclick
37111          * Fires when a row is clicked
37112          * @param {Grid} this
37113          * @param {Number} rowIndex
37114          * @param {Roo.EventObject} e
37115          */
37116         "rowclick" : true,
37117         /**
37118          * @event rowdblclick
37119          * Fires when a row is double clicked
37120          * @param {Grid} this
37121          * @param {Number} rowIndex
37122          * @param {Roo.EventObject} e
37123          */
37124         "rowdblclick" : true,
37125         /**
37126          * @event headerclick
37127          * Fires when a header is clicked
37128          * @param {Grid} this
37129          * @param {Number} columnIndex
37130          * @param {Roo.EventObject} e
37131          */
37132         "headerclick" : true,
37133         /**
37134          * @event headerdblclick
37135          * Fires when a header cell is double clicked
37136          * @param {Grid} this
37137          * @param {Number} columnIndex
37138          * @param {Roo.EventObject} e
37139          */
37140         "headerdblclick" : true,
37141         /**
37142          * @event rowcontextmenu
37143          * Fires when a row is right clicked
37144          * @param {Grid} this
37145          * @param {Number} rowIndex
37146          * @param {Roo.EventObject} e
37147          */
37148         "rowcontextmenu" : true,
37149         /**
37150          * @event cellcontextmenu
37151          * Fires when a cell is right clicked
37152          * @param {Grid} this
37153          * @param {Number} rowIndex
37154          * @param {Number} cellIndex
37155          * @param {Roo.EventObject} e
37156          */
37157          "cellcontextmenu" : true,
37158         /**
37159          * @event headercontextmenu
37160          * Fires when a header is right clicked
37161          * @param {Grid} this
37162          * @param {Number} columnIndex
37163          * @param {Roo.EventObject} e
37164          */
37165         "headercontextmenu" : true,
37166         /**
37167          * @event bodyscroll
37168          * Fires when the body element is scrolled
37169          * @param {Number} scrollLeft
37170          * @param {Number} scrollTop
37171          */
37172         "bodyscroll" : true,
37173         /**
37174          * @event columnresize
37175          * Fires when the user resizes a column
37176          * @param {Number} columnIndex
37177          * @param {Number} newSize
37178          */
37179         "columnresize" : true,
37180         /**
37181          * @event columnmove
37182          * Fires when the user moves a column
37183          * @param {Number} oldIndex
37184          * @param {Number} newIndex
37185          */
37186         "columnmove" : true,
37187         /**
37188          * @event startdrag
37189          * Fires when row(s) start being dragged
37190          * @param {Grid} this
37191          * @param {Roo.GridDD} dd The drag drop object
37192          * @param {event} e The raw browser event
37193          */
37194         "startdrag" : true,
37195         /**
37196          * @event enddrag
37197          * Fires when a drag operation is complete
37198          * @param {Grid} this
37199          * @param {Roo.GridDD} dd The drag drop object
37200          * @param {event} e The raw browser event
37201          */
37202         "enddrag" : true,
37203         /**
37204          * @event dragdrop
37205          * Fires when dragged row(s) are dropped on a valid DD target
37206          * @param {Grid} this
37207          * @param {Roo.GridDD} dd The drag drop object
37208          * @param {String} targetId The target drag drop object
37209          * @param {event} e The raw browser event
37210          */
37211         "dragdrop" : true,
37212         /**
37213          * @event dragover
37214          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37215          * @param {Grid} this
37216          * @param {Roo.GridDD} dd The drag drop object
37217          * @param {String} targetId The target drag drop object
37218          * @param {event} e The raw browser event
37219          */
37220         "dragover" : true,
37221         /**
37222          * @event dragenter
37223          *  Fires when the dragged row(s) first cross another DD target while being dragged
37224          * @param {Grid} this
37225          * @param {Roo.GridDD} dd The drag drop object
37226          * @param {String} targetId The target drag drop object
37227          * @param {event} e The raw browser event
37228          */
37229         "dragenter" : true,
37230         /**
37231          * @event dragout
37232          * Fires when the dragged row(s) leave another DD target while being dragged
37233          * @param {Grid} this
37234          * @param {Roo.GridDD} dd The drag drop object
37235          * @param {String} targetId The target drag drop object
37236          * @param {event} e The raw browser event
37237          */
37238         "dragout" : true,
37239         /**
37240          * @event rowclass
37241          * Fires when a row is rendered, so you can change add a style to it.
37242          * @param {GridView} gridview   The grid view
37243          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37244          */
37245         'rowclass' : true,
37246
37247         /**
37248          * @event render
37249          * Fires when the grid is rendered
37250          * @param {Grid} grid
37251          */
37252         'render' : true,
37253             /**
37254              * @event select
37255              * Fires when a date is selected
37256              * @param {DatePicker} this
37257              * @param {Date} date The selected date
37258              */
37259         'select': true,
37260         /**
37261              * @event monthchange
37262              * Fires when the displayed month changes 
37263              * @param {DatePicker} this
37264              * @param {Date} date The selected month
37265              */
37266         'monthchange': true,
37267         /**
37268              * @event evententer
37269              * Fires when mouse over an event
37270              * @param {Calendar} this
37271              * @param {event} Event
37272              */
37273         'evententer': true,
37274         /**
37275              * @event eventleave
37276              * Fires when the mouse leaves an
37277              * @param {Calendar} this
37278              * @param {event}
37279              */
37280         'eventleave': true,
37281         /**
37282              * @event eventclick
37283              * Fires when the mouse click an
37284              * @param {Calendar} this
37285              * @param {event}
37286              */
37287         'eventclick': true,
37288         /**
37289              * @event eventrender
37290              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37291              * @param {Calendar} this
37292              * @param {data} data to be modified
37293              */
37294         'eventrender': true
37295         
37296     });
37297
37298     Roo.grid.Grid.superclass.constructor.call(this);
37299     this.on('render', function() {
37300         this.view.el.addClass('x-grid-cal'); 
37301         
37302         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37303
37304     },this);
37305     
37306     if (!Roo.grid.Calendar.style) {
37307         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37308             
37309             
37310             '.x-grid-cal .x-grid-col' :  {
37311                 height: 'auto !important',
37312                 'vertical-align': 'top'
37313             },
37314             '.x-grid-cal  .fc-event-hori' : {
37315                 height: '14px'
37316             }
37317              
37318             
37319         }, Roo.id());
37320     }
37321
37322     
37323     
37324 };
37325 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37326     /**
37327      * @cfg {Store} eventStore The store that loads events.
37328      */
37329     eventStore : 25,
37330
37331      
37332     activeDate : false,
37333     startDay : 0,
37334     autoWidth : true,
37335     monitorWindowResize : false,
37336
37337     
37338     resizeColumns : function() {
37339         var col = (this.view.el.getWidth() / 7) - 3;
37340         // loop through cols, and setWidth
37341         for(var i =0 ; i < 7 ; i++){
37342             this.cm.setColumnWidth(i, col);
37343         }
37344     },
37345      setDate :function(date) {
37346         
37347         Roo.log('setDate?');
37348         
37349         this.resizeColumns();
37350         var vd = this.activeDate;
37351         this.activeDate = date;
37352 //        if(vd && this.el){
37353 //            var t = date.getTime();
37354 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37355 //                Roo.log('using add remove');
37356 //                
37357 //                this.fireEvent('monthchange', this, date);
37358 //                
37359 //                this.cells.removeClass("fc-state-highlight");
37360 //                this.cells.each(function(c){
37361 //                   if(c.dateValue == t){
37362 //                       c.addClass("fc-state-highlight");
37363 //                       setTimeout(function(){
37364 //                            try{c.dom.firstChild.focus();}catch(e){}
37365 //                       }, 50);
37366 //                       return false;
37367 //                   }
37368 //                   return true;
37369 //                });
37370 //                return;
37371 //            }
37372 //        }
37373         
37374         var days = date.getDaysInMonth();
37375         
37376         var firstOfMonth = date.getFirstDateOfMonth();
37377         var startingPos = firstOfMonth.getDay()-this.startDay;
37378         
37379         if(startingPos < this.startDay){
37380             startingPos += 7;
37381         }
37382         
37383         var pm = date.add(Date.MONTH, -1);
37384         var prevStart = pm.getDaysInMonth()-startingPos;
37385 //        
37386         
37387         
37388         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37389         
37390         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37391         //this.cells.addClassOnOver('fc-state-hover');
37392         
37393         var cells = this.cells.elements;
37394         var textEls = this.textNodes;
37395         
37396         //Roo.each(cells, function(cell){
37397         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37398         //});
37399         
37400         days += startingPos;
37401
37402         // convert everything to numbers so it's fast
37403         var day = 86400000;
37404         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37405         //Roo.log(d);
37406         //Roo.log(pm);
37407         //Roo.log(prevStart);
37408         
37409         var today = new Date().clearTime().getTime();
37410         var sel = date.clearTime().getTime();
37411         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37412         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37413         var ddMatch = this.disabledDatesRE;
37414         var ddText = this.disabledDatesText;
37415         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37416         var ddaysText = this.disabledDaysText;
37417         var format = this.format;
37418         
37419         var setCellClass = function(cal, cell){
37420             
37421             //Roo.log('set Cell Class');
37422             cell.title = "";
37423             var t = d.getTime();
37424             
37425             //Roo.log(d);
37426             
37427             
37428             cell.dateValue = t;
37429             if(t == today){
37430                 cell.className += " fc-today";
37431                 cell.className += " fc-state-highlight";
37432                 cell.title = cal.todayText;
37433             }
37434             if(t == sel){
37435                 // disable highlight in other month..
37436                 cell.className += " fc-state-highlight";
37437                 
37438             }
37439             // disabling
37440             if(t < min) {
37441                 //cell.className = " fc-state-disabled";
37442                 cell.title = cal.minText;
37443                 return;
37444             }
37445             if(t > max) {
37446                 //cell.className = " fc-state-disabled";
37447                 cell.title = cal.maxText;
37448                 return;
37449             }
37450             if(ddays){
37451                 if(ddays.indexOf(d.getDay()) != -1){
37452                     // cell.title = ddaysText;
37453                    // cell.className = " fc-state-disabled";
37454                 }
37455             }
37456             if(ddMatch && format){
37457                 var fvalue = d.dateFormat(format);
37458                 if(ddMatch.test(fvalue)){
37459                     cell.title = ddText.replace("%0", fvalue);
37460                    cell.className = " fc-state-disabled";
37461                 }
37462             }
37463             
37464             if (!cell.initialClassName) {
37465                 cell.initialClassName = cell.dom.className;
37466             }
37467             
37468             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37469         };
37470
37471         var i = 0;
37472         
37473         for(; i < startingPos; i++) {
37474             cells[i].dayName =  (++prevStart);
37475             Roo.log(textEls[i]);
37476             d.setDate(d.getDate()+1);
37477             
37478             //cells[i].className = "fc-past fc-other-month";
37479             setCellClass(this, cells[i]);
37480         }
37481         
37482         var intDay = 0;
37483         
37484         for(; i < days; i++){
37485             intDay = i - startingPos + 1;
37486             cells[i].dayName =  (intDay);
37487             d.setDate(d.getDate()+1);
37488             
37489             cells[i].className = ''; // "x-date-active";
37490             setCellClass(this, cells[i]);
37491         }
37492         var extraDays = 0;
37493         
37494         for(; i < 42; i++) {
37495             //textEls[i].innerHTML = (++extraDays);
37496             
37497             d.setDate(d.getDate()+1);
37498             cells[i].dayName = (++extraDays);
37499             cells[i].className = "fc-future fc-other-month";
37500             setCellClass(this, cells[i]);
37501         }
37502         
37503         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37504         
37505         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37506         
37507         // this will cause all the cells to mis
37508         var rows= [];
37509         var i =0;
37510         for (var r = 0;r < 6;r++) {
37511             for (var c =0;c < 7;c++) {
37512                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37513             }    
37514         }
37515         
37516         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37517         for(i=0;i<cells.length;i++) {
37518             
37519             this.cells.elements[i].dayName = cells[i].dayName ;
37520             this.cells.elements[i].className = cells[i].className;
37521             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37522             this.cells.elements[i].title = cells[i].title ;
37523             this.cells.elements[i].dateValue = cells[i].dateValue ;
37524         }
37525         
37526         
37527         
37528         
37529         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37530         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37531         
37532         ////if(totalRows != 6){
37533             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37534            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37535        // }
37536         
37537         this.fireEvent('monthchange', this, date);
37538         
37539         
37540     },
37541  /**
37542      * Returns the grid's SelectionModel.
37543      * @return {SelectionModel}
37544      */
37545     getSelectionModel : function(){
37546         if(!this.selModel){
37547             this.selModel = new Roo.grid.CellSelectionModel();
37548         }
37549         return this.selModel;
37550     },
37551
37552     load: function() {
37553         this.eventStore.load()
37554         
37555         
37556         
37557     },
37558     
37559     findCell : function(dt) {
37560         dt = dt.clearTime().getTime();
37561         var ret = false;
37562         this.cells.each(function(c){
37563             //Roo.log("check " +c.dateValue + '?=' + dt);
37564             if(c.dateValue == dt){
37565                 ret = c;
37566                 return false;
37567             }
37568             return true;
37569         });
37570         
37571         return ret;
37572     },
37573     
37574     findCells : function(rec) {
37575         var s = rec.data.start_dt.clone().clearTime().getTime();
37576        // Roo.log(s);
37577         var e= rec.data.end_dt.clone().clearTime().getTime();
37578        // Roo.log(e);
37579         var ret = [];
37580         this.cells.each(function(c){
37581              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37582             
37583             if(c.dateValue > e){
37584                 return ;
37585             }
37586             if(c.dateValue < s){
37587                 return ;
37588             }
37589             ret.push(c);
37590         });
37591         
37592         return ret;    
37593     },
37594     
37595     findBestRow: function(cells)
37596     {
37597         var ret = 0;
37598         
37599         for (var i =0 ; i < cells.length;i++) {
37600             ret  = Math.max(cells[i].rows || 0,ret);
37601         }
37602         return ret;
37603         
37604     },
37605     
37606     
37607     addItem : function(rec)
37608     {
37609         // look for vertical location slot in
37610         var cells = this.findCells(rec);
37611         
37612         rec.row = this.findBestRow(cells);
37613         
37614         // work out the location.
37615         
37616         var crow = false;
37617         var rows = [];
37618         for(var i =0; i < cells.length; i++) {
37619             if (!crow) {
37620                 crow = {
37621                     start : cells[i],
37622                     end :  cells[i]
37623                 };
37624                 continue;
37625             }
37626             if (crow.start.getY() == cells[i].getY()) {
37627                 // on same row.
37628                 crow.end = cells[i];
37629                 continue;
37630             }
37631             // different row.
37632             rows.push(crow);
37633             crow = {
37634                 start: cells[i],
37635                 end : cells[i]
37636             };
37637             
37638         }
37639         
37640         rows.push(crow);
37641         rec.els = [];
37642         rec.rows = rows;
37643         rec.cells = cells;
37644         for (var i = 0; i < cells.length;i++) {
37645             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37646             
37647         }
37648         
37649         
37650     },
37651     
37652     clearEvents: function() {
37653         
37654         if (!this.eventStore.getCount()) {
37655             return;
37656         }
37657         // reset number of rows in cells.
37658         Roo.each(this.cells.elements, function(c){
37659             c.rows = 0;
37660         });
37661         
37662         this.eventStore.each(function(e) {
37663             this.clearEvent(e);
37664         },this);
37665         
37666     },
37667     
37668     clearEvent : function(ev)
37669     {
37670         if (ev.els) {
37671             Roo.each(ev.els, function(el) {
37672                 el.un('mouseenter' ,this.onEventEnter, this);
37673                 el.un('mouseleave' ,this.onEventLeave, this);
37674                 el.remove();
37675             },this);
37676             ev.els = [];
37677         }
37678     },
37679     
37680     
37681     renderEvent : function(ev,ctr) {
37682         if (!ctr) {
37683              ctr = this.view.el.select('.fc-event-container',true).first();
37684         }
37685         
37686          
37687         this.clearEvent(ev);
37688             //code
37689        
37690         
37691         
37692         ev.els = [];
37693         var cells = ev.cells;
37694         var rows = ev.rows;
37695         this.fireEvent('eventrender', this, ev);
37696         
37697         for(var i =0; i < rows.length; i++) {
37698             
37699             cls = '';
37700             if (i == 0) {
37701                 cls += ' fc-event-start';
37702             }
37703             if ((i+1) == rows.length) {
37704                 cls += ' fc-event-end';
37705             }
37706             
37707             //Roo.log(ev.data);
37708             // how many rows should it span..
37709             var cg = this.eventTmpl.append(ctr,Roo.apply({
37710                 fccls : cls
37711                 
37712             }, ev.data) , true);
37713             
37714             
37715             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37716             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37717             cg.on('click', this.onEventClick, this, ev);
37718             
37719             ev.els.push(cg);
37720             
37721             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37722             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37723             //Roo.log(cg);
37724              
37725             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37726             cg.setWidth(ebox.right - sbox.x -2);
37727         }
37728     },
37729     
37730     renderEvents: function()
37731     {   
37732         // first make sure there is enough space..
37733         
37734         if (!this.eventTmpl) {
37735             this.eventTmpl = new Roo.Template(
37736                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37737                     '<div class="fc-event-inner">' +
37738                         '<span class="fc-event-time">{time}</span>' +
37739                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37740                     '</div>' +
37741                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37742                 '</div>'
37743             );
37744                 
37745         }
37746                
37747         
37748         
37749         this.cells.each(function(c) {
37750             //Roo.log(c.select('.fc-day-content div',true).first());
37751             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37752         });
37753         
37754         var ctr = this.view.el.select('.fc-event-container',true).first();
37755         
37756         var cls;
37757         this.eventStore.each(function(ev){
37758             
37759             this.renderEvent(ev);
37760              
37761              
37762         }, this);
37763         this.view.layout();
37764         
37765     },
37766     
37767     onEventEnter: function (e, el,event,d) {
37768         this.fireEvent('evententer', this, el, event);
37769     },
37770     
37771     onEventLeave: function (e, el,event,d) {
37772         this.fireEvent('eventleave', this, el, event);
37773     },
37774     
37775     onEventClick: function (e, el,event,d) {
37776         this.fireEvent('eventclick', this, el, event);
37777     },
37778     
37779     onMonthChange: function () {
37780         this.store.load();
37781     },
37782     
37783     onLoad: function () {
37784         
37785         //Roo.log('calendar onload');
37786 //         
37787         if(this.eventStore.getCount() > 0){
37788             
37789            
37790             
37791             this.eventStore.each(function(d){
37792                 
37793                 
37794                 // FIXME..
37795                 var add =   d.data;
37796                 if (typeof(add.end_dt) == 'undefined')  {
37797                     Roo.log("Missing End time in calendar data: ");
37798                     Roo.log(d);
37799                     return;
37800                 }
37801                 if (typeof(add.start_dt) == 'undefined')  {
37802                     Roo.log("Missing Start time in calendar data: ");
37803                     Roo.log(d);
37804                     return;
37805                 }
37806                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37807                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37808                 add.id = add.id || d.id;
37809                 add.title = add.title || '??';
37810                 
37811                 this.addItem(d);
37812                 
37813              
37814             },this);
37815         }
37816         
37817         this.renderEvents();
37818     }
37819     
37820
37821 });
37822 /*
37823  grid : {
37824                 xtype: 'Grid',
37825                 xns: Roo.grid,
37826                 listeners : {
37827                     render : function ()
37828                     {
37829                         _this.grid = this;
37830                         
37831                         if (!this.view.el.hasClass('course-timesheet')) {
37832                             this.view.el.addClass('course-timesheet');
37833                         }
37834                         if (this.tsStyle) {
37835                             this.ds.load({});
37836                             return; 
37837                         }
37838                         Roo.log('width');
37839                         Roo.log(_this.grid.view.el.getWidth());
37840                         
37841                         
37842                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37843                             '.course-timesheet .x-grid-row' : {
37844                                 height: '80px'
37845                             },
37846                             '.x-grid-row td' : {
37847                                 'vertical-align' : 0
37848                             },
37849                             '.course-edit-link' : {
37850                                 'color' : 'blue',
37851                                 'text-overflow' : 'ellipsis',
37852                                 'overflow' : 'hidden',
37853                                 'white-space' : 'nowrap',
37854                                 'cursor' : 'pointer'
37855                             },
37856                             '.sub-link' : {
37857                                 'color' : 'green'
37858                             },
37859                             '.de-act-sup-link' : {
37860                                 'color' : 'purple',
37861                                 'text-decoration' : 'line-through'
37862                             },
37863                             '.de-act-link' : {
37864                                 'color' : 'red',
37865                                 'text-decoration' : 'line-through'
37866                             },
37867                             '.course-timesheet .course-highlight' : {
37868                                 'border-top-style': 'dashed !important',
37869                                 'border-bottom-bottom': 'dashed !important'
37870                             },
37871                             '.course-timesheet .course-item' : {
37872                                 'font-family'   : 'tahoma, arial, helvetica',
37873                                 'font-size'     : '11px',
37874                                 'overflow'      : 'hidden',
37875                                 'padding-left'  : '10px',
37876                                 'padding-right' : '10px',
37877                                 'padding-top' : '10px' 
37878                             }
37879                             
37880                         }, Roo.id());
37881                                 this.ds.load({});
37882                     }
37883                 },
37884                 autoWidth : true,
37885                 monitorWindowResize : false,
37886                 cellrenderer : function(v,x,r)
37887                 {
37888                     return v;
37889                 },
37890                 sm : {
37891                     xtype: 'CellSelectionModel',
37892                     xns: Roo.grid
37893                 },
37894                 dataSource : {
37895                     xtype: 'Store',
37896                     xns: Roo.data,
37897                     listeners : {
37898                         beforeload : function (_self, options)
37899                         {
37900                             options.params = options.params || {};
37901                             options.params._month = _this.monthField.getValue();
37902                             options.params.limit = 9999;
37903                             options.params['sort'] = 'when_dt';    
37904                             options.params['dir'] = 'ASC';    
37905                             this.proxy.loadResponse = this.loadResponse;
37906                             Roo.log("load?");
37907                             //this.addColumns();
37908                         },
37909                         load : function (_self, records, options)
37910                         {
37911                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37912                                 // if you click on the translation.. you can edit it...
37913                                 var el = Roo.get(this);
37914                                 var id = el.dom.getAttribute('data-id');
37915                                 var d = el.dom.getAttribute('data-date');
37916                                 var t = el.dom.getAttribute('data-time');
37917                                 //var id = this.child('span').dom.textContent;
37918                                 
37919                                 //Roo.log(this);
37920                                 Pman.Dialog.CourseCalendar.show({
37921                                     id : id,
37922                                     when_d : d,
37923                                     when_t : t,
37924                                     productitem_active : id ? 1 : 0
37925                                 }, function() {
37926                                     _this.grid.ds.load({});
37927                                 });
37928                            
37929                            });
37930                            
37931                            _this.panel.fireEvent('resize', [ '', '' ]);
37932                         }
37933                     },
37934                     loadResponse : function(o, success, response){
37935                             // this is overridden on before load..
37936                             
37937                             Roo.log("our code?");       
37938                             //Roo.log(success);
37939                             //Roo.log(response)
37940                             delete this.activeRequest;
37941                             if(!success){
37942                                 this.fireEvent("loadexception", this, o, response);
37943                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37944                                 return;
37945                             }
37946                             var result;
37947                             try {
37948                                 result = o.reader.read(response);
37949                             }catch(e){
37950                                 Roo.log("load exception?");
37951                                 this.fireEvent("loadexception", this, o, response, e);
37952                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37953                                 return;
37954                             }
37955                             Roo.log("ready...");        
37956                             // loop through result.records;
37957                             // and set this.tdate[date] = [] << array of records..
37958                             _this.tdata  = {};
37959                             Roo.each(result.records, function(r){
37960                                 //Roo.log(r.data);
37961                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37962                                     _this.tdata[r.data.when_dt.format('j')] = [];
37963                                 }
37964                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37965                             });
37966                             
37967                             //Roo.log(_this.tdata);
37968                             
37969                             result.records = [];
37970                             result.totalRecords = 6;
37971                     
37972                             // let's generate some duumy records for the rows.
37973                             //var st = _this.dateField.getValue();
37974                             
37975                             // work out monday..
37976                             //st = st.add(Date.DAY, -1 * st.format('w'));
37977                             
37978                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37979                             
37980                             var firstOfMonth = date.getFirstDayOfMonth();
37981                             var days = date.getDaysInMonth();
37982                             var d = 1;
37983                             var firstAdded = false;
37984                             for (var i = 0; i < result.totalRecords ; i++) {
37985                                 //var d= st.add(Date.DAY, i);
37986                                 var row = {};
37987                                 var added = 0;
37988                                 for(var w = 0 ; w < 7 ; w++){
37989                                     if(!firstAdded && firstOfMonth != w){
37990                                         continue;
37991                                     }
37992                                     if(d > days){
37993                                         continue;
37994                                     }
37995                                     firstAdded = true;
37996                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
37997                                     row['weekday'+w] = String.format(
37998                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
37999                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38000                                                     d,
38001                                                     date.format('Y-m-')+dd
38002                                                 );
38003                                     added++;
38004                                     if(typeof(_this.tdata[d]) != 'undefined'){
38005                                         Roo.each(_this.tdata[d], function(r){
38006                                             var is_sub = '';
38007                                             var deactive = '';
38008                                             var id = r.id;
38009                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38010                                             if(r.parent_id*1>0){
38011                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38012                                                 id = r.parent_id;
38013                                             }
38014                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38015                                                 deactive = 'de-act-link';
38016                                             }
38017                                             
38018                                             row['weekday'+w] += String.format(
38019                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38020                                                     id, //0
38021                                                     r.product_id_name, //1
38022                                                     r.when_dt.format('h:ia'), //2
38023                                                     is_sub, //3
38024                                                     deactive, //4
38025                                                     desc // 5
38026                                             );
38027                                         });
38028                                     }
38029                                     d++;
38030                                 }
38031                                 
38032                                 // only do this if something added..
38033                                 if(added > 0){ 
38034                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38035                                 }
38036                                 
38037                                 
38038                                 // push it twice. (second one with an hour..
38039                                 
38040                             }
38041                             //Roo.log(result);
38042                             this.fireEvent("load", this, o, o.request.arg);
38043                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38044                         },
38045                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38046                     proxy : {
38047                         xtype: 'HttpProxy',
38048                         xns: Roo.data,
38049                         method : 'GET',
38050                         url : baseURL + '/Roo/Shop_course.php'
38051                     },
38052                     reader : {
38053                         xtype: 'JsonReader',
38054                         xns: Roo.data,
38055                         id : 'id',
38056                         fields : [
38057                             {
38058                                 'name': 'id',
38059                                 'type': 'int'
38060                             },
38061                             {
38062                                 'name': 'when_dt',
38063                                 'type': 'string'
38064                             },
38065                             {
38066                                 'name': 'end_dt',
38067                                 'type': 'string'
38068                             },
38069                             {
38070                                 'name': 'parent_id',
38071                                 'type': 'int'
38072                             },
38073                             {
38074                                 'name': 'product_id',
38075                                 'type': 'int'
38076                             },
38077                             {
38078                                 'name': 'productitem_id',
38079                                 'type': 'int'
38080                             },
38081                             {
38082                                 'name': 'guid',
38083                                 'type': 'int'
38084                             }
38085                         ]
38086                     }
38087                 },
38088                 toolbar : {
38089                     xtype: 'Toolbar',
38090                     xns: Roo,
38091                     items : [
38092                         {
38093                             xtype: 'Button',
38094                             xns: Roo.Toolbar,
38095                             listeners : {
38096                                 click : function (_self, e)
38097                                 {
38098                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38099                                     sd.setMonth(sd.getMonth()-1);
38100                                     _this.monthField.setValue(sd.format('Y-m-d'));
38101                                     _this.grid.ds.load({});
38102                                 }
38103                             },
38104                             text : "Back"
38105                         },
38106                         {
38107                             xtype: 'Separator',
38108                             xns: Roo.Toolbar
38109                         },
38110                         {
38111                             xtype: 'MonthField',
38112                             xns: Roo.form,
38113                             listeners : {
38114                                 render : function (_self)
38115                                 {
38116                                     _this.monthField = _self;
38117                                    // _this.monthField.set  today
38118                                 },
38119                                 select : function (combo, date)
38120                                 {
38121                                     _this.grid.ds.load({});
38122                                 }
38123                             },
38124                             value : (function() { return new Date(); })()
38125                         },
38126                         {
38127                             xtype: 'Separator',
38128                             xns: Roo.Toolbar
38129                         },
38130                         {
38131                             xtype: 'TextItem',
38132                             xns: Roo.Toolbar,
38133                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38134                         },
38135                         {
38136                             xtype: 'Fill',
38137                             xns: Roo.Toolbar
38138                         },
38139                         {
38140                             xtype: 'Button',
38141                             xns: Roo.Toolbar,
38142                             listeners : {
38143                                 click : function (_self, e)
38144                                 {
38145                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38146                                     sd.setMonth(sd.getMonth()+1);
38147                                     _this.monthField.setValue(sd.format('Y-m-d'));
38148                                     _this.grid.ds.load({});
38149                                 }
38150                             },
38151                             text : "Next"
38152                         }
38153                     ]
38154                 },
38155                  
38156             }
38157         };
38158         
38159         *//*
38160  * Based on:
38161  * Ext JS Library 1.1.1
38162  * Copyright(c) 2006-2007, Ext JS, LLC.
38163  *
38164  * Originally Released Under LGPL - original licence link has changed is not relivant.
38165  *
38166  * Fork - LGPL
38167  * <script type="text/javascript">
38168  */
38169  
38170 /**
38171  * @class Roo.LoadMask
38172  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38173  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38174  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38175  * element's UpdateManager load indicator and will be destroyed after the initial load.
38176  * @constructor
38177  * Create a new LoadMask
38178  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38179  * @param {Object} config The config object
38180  */
38181 Roo.LoadMask = function(el, config){
38182     this.el = Roo.get(el);
38183     Roo.apply(this, config);
38184     if(this.store){
38185         this.store.on('beforeload', this.onBeforeLoad, this);
38186         this.store.on('load', this.onLoad, this);
38187         this.store.on('loadexception', this.onLoadException, this);
38188         this.removeMask = false;
38189     }else{
38190         var um = this.el.getUpdateManager();
38191         um.showLoadIndicator = false; // disable the default indicator
38192         um.on('beforeupdate', this.onBeforeLoad, this);
38193         um.on('update', this.onLoad, this);
38194         um.on('failure', this.onLoad, this);
38195         this.removeMask = true;
38196     }
38197 };
38198
38199 Roo.LoadMask.prototype = {
38200     /**
38201      * @cfg {Boolean} removeMask
38202      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38203      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38204      */
38205     /**
38206      * @cfg {String} msg
38207      * The text to display in a centered loading message box (defaults to 'Loading...')
38208      */
38209     msg : 'Loading...',
38210     /**
38211      * @cfg {String} msgCls
38212      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38213      */
38214     msgCls : 'x-mask-loading',
38215
38216     /**
38217      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38218      * @type Boolean
38219      */
38220     disabled: false,
38221
38222     /**
38223      * Disables the mask to prevent it from being displayed
38224      */
38225     disable : function(){
38226        this.disabled = true;
38227     },
38228
38229     /**
38230      * Enables the mask so that it can be displayed
38231      */
38232     enable : function(){
38233         this.disabled = false;
38234     },
38235     
38236     onLoadException : function()
38237     {
38238         Roo.log(arguments);
38239         
38240         if (typeof(arguments[3]) != 'undefined') {
38241             Roo.MessageBox.alert("Error loading",arguments[3]);
38242         } 
38243         /*
38244         try {
38245             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38246                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38247             }   
38248         } catch(e) {
38249             
38250         }
38251         */
38252     
38253         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38254     },
38255     // private
38256     onLoad : function()
38257     {
38258         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38259     },
38260
38261     // private
38262     onBeforeLoad : function(){
38263         if(!this.disabled){
38264             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38265         }
38266     },
38267
38268     // private
38269     destroy : function(){
38270         if(this.store){
38271             this.store.un('beforeload', this.onBeforeLoad, this);
38272             this.store.un('load', this.onLoad, this);
38273             this.store.un('loadexception', this.onLoadException, this);
38274         }else{
38275             var um = this.el.getUpdateManager();
38276             um.un('beforeupdate', this.onBeforeLoad, this);
38277             um.un('update', this.onLoad, this);
38278             um.un('failure', this.onLoad, this);
38279         }
38280     }
38281 };/*
38282  * Based on:
38283  * Ext JS Library 1.1.1
38284  * Copyright(c) 2006-2007, Ext JS, LLC.
38285  *
38286  * Originally Released Under LGPL - original licence link has changed is not relivant.
38287  *
38288  * Fork - LGPL
38289  * <script type="text/javascript">
38290  */
38291
38292
38293 /**
38294  * @class Roo.XTemplate
38295  * @extends Roo.Template
38296  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38297 <pre><code>
38298 var t = new Roo.XTemplate(
38299         '&lt;select name="{name}"&gt;',
38300                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38301         '&lt;/select&gt;'
38302 );
38303  
38304 // then append, applying the master template values
38305  </code></pre>
38306  *
38307  * Supported features:
38308  *
38309  *  Tags:
38310
38311 <pre><code>
38312       {a_variable} - output encoded.
38313       {a_variable.format:("Y-m-d")} - call a method on the variable
38314       {a_variable:raw} - unencoded output
38315       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38316       {a_variable:this.method_on_template(...)} - call a method on the template object.
38317  
38318 </code></pre>
38319  *  The tpl tag:
38320 <pre><code>
38321         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38322         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38323         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38324         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38325   
38326         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38327         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38328 </code></pre>
38329  *      
38330  */
38331 Roo.XTemplate = function()
38332 {
38333     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38334     if (this.html) {
38335         this.compile();
38336     }
38337 };
38338
38339
38340 Roo.extend(Roo.XTemplate, Roo.Template, {
38341
38342     /**
38343      * The various sub templates
38344      */
38345     tpls : false,
38346     /**
38347      *
38348      * basic tag replacing syntax
38349      * WORD:WORD()
38350      *
38351      * // you can fake an object call by doing this
38352      *  x.t:(test,tesT) 
38353      * 
38354      */
38355     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38356
38357     /**
38358      * compile the template
38359      *
38360      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38361      *
38362      */
38363     compile: function()
38364     {
38365         var s = this.html;
38366      
38367         s = ['<tpl>', s, '</tpl>'].join('');
38368     
38369         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38370             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38371             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38372             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38373             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38374             m,
38375             id     = 0,
38376             tpls   = [];
38377     
38378         while(true == !!(m = s.match(re))){
38379             var forMatch   = m[0].match(nameRe),
38380                 ifMatch   = m[0].match(ifRe),
38381                 execMatch   = m[0].match(execRe),
38382                 namedMatch   = m[0].match(namedRe),
38383                 
38384                 exp  = null, 
38385                 fn   = null,
38386                 exec = null,
38387                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38388                 
38389             if (ifMatch) {
38390                 // if - puts fn into test..
38391                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38392                 if(exp){
38393                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38394                 }
38395             }
38396             
38397             if (execMatch) {
38398                 // exec - calls a function... returns empty if true is  returned.
38399                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38400                 if(exp){
38401                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38402                 }
38403             }
38404             
38405             
38406             if (name) {
38407                 // for = 
38408                 switch(name){
38409                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38410                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38411                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38412                 }
38413             }
38414             var uid = namedMatch ? namedMatch[1] : id;
38415             
38416             
38417             tpls.push({
38418                 id:     namedMatch ? namedMatch[1] : id,
38419                 target: name,
38420                 exec:   exec,
38421                 test:   fn,
38422                 body:   m[1] || ''
38423             });
38424             if (namedMatch) {
38425                 s = s.replace(m[0], '');
38426             } else { 
38427                 s = s.replace(m[0], '{xtpl'+ id + '}');
38428             }
38429             ++id;
38430         }
38431         this.tpls = [];
38432         for(var i = tpls.length-1; i >= 0; --i){
38433             this.compileTpl(tpls[i]);
38434             this.tpls[tpls[i].id] = tpls[i];
38435         }
38436         this.master = tpls[tpls.length-1];
38437         return this;
38438     },
38439     /**
38440      * same as applyTemplate, except it's done to one of the subTemplates
38441      * when using named templates, you can do:
38442      *
38443      * var str = pl.applySubTemplate('your-name', values);
38444      *
38445      * 
38446      * @param {Number} id of the template
38447      * @param {Object} values to apply to template
38448      * @param {Object} parent (normaly the instance of this object)
38449      */
38450     applySubTemplate : function(id, values, parent)
38451     {
38452         
38453         
38454         var t = this.tpls[id];
38455         
38456         
38457         try { 
38458             if(t.test && !t.test.call(this, values, parent)){
38459                 return '';
38460             }
38461         } catch(e) {
38462             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38463             Roo.log(e.toString());
38464             Roo.log(t.test);
38465             return ''
38466         }
38467         try { 
38468             
38469             if(t.exec && t.exec.call(this, values, parent)){
38470                 return '';
38471             }
38472         } catch(e) {
38473             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38474             Roo.log(e.toString());
38475             Roo.log(t.exec);
38476             return ''
38477         }
38478         try {
38479             var vs = t.target ? t.target.call(this, values, parent) : values;
38480             parent = t.target ? values : parent;
38481             if(t.target && vs instanceof Array){
38482                 var buf = [];
38483                 for(var i = 0, len = vs.length; i < len; i++){
38484                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38485                 }
38486                 return buf.join('');
38487             }
38488             return t.compiled.call(this, vs, parent);
38489         } catch (e) {
38490             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38491             Roo.log(e.toString());
38492             Roo.log(t.compiled);
38493             return '';
38494         }
38495     },
38496
38497     compileTpl : function(tpl)
38498     {
38499         var fm = Roo.util.Format;
38500         var useF = this.disableFormats !== true;
38501         var sep = Roo.isGecko ? "+" : ",";
38502         var undef = function(str) {
38503             Roo.log("Property not found :"  + str);
38504             return '';
38505         };
38506         
38507         var fn = function(m, name, format, args)
38508         {
38509             //Roo.log(arguments);
38510             args = args ? args.replace(/\\'/g,"'") : args;
38511             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38512             if (typeof(format) == 'undefined') {
38513                 format= 'htmlEncode';
38514             }
38515             if (format == 'raw' ) {
38516                 format = false;
38517             }
38518             
38519             if(name.substr(0, 4) == 'xtpl'){
38520                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38521             }
38522             
38523             // build an array of options to determine if value is undefined..
38524             
38525             // basically get 'xxxx.yyyy' then do
38526             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38527             //    (function () { Roo.log("Property not found"); return ''; })() :
38528             //    ......
38529             
38530             var udef_ar = [];
38531             var lookfor = '';
38532             Roo.each(name.split('.'), function(st) {
38533                 lookfor += (lookfor.length ? '.': '') + st;
38534                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38535             });
38536             
38537             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38538             
38539             
38540             if(format && useF){
38541                 
38542                 args = args ? ',' + args : "";
38543                  
38544                 if(format.substr(0, 5) != "this."){
38545                     format = "fm." + format + '(';
38546                 }else{
38547                     format = 'this.call("'+ format.substr(5) + '", ';
38548                     args = ", values";
38549                 }
38550                 
38551                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38552             }
38553              
38554             if (args.length) {
38555                 // called with xxyx.yuu:(test,test)
38556                 // change to ()
38557                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38558             }
38559             // raw.. - :raw modifier..
38560             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38561             
38562         };
38563         var body;
38564         // branched to use + in gecko and [].join() in others
38565         if(Roo.isGecko){
38566             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38567                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38568                     "';};};";
38569         }else{
38570             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38571             body.push(tpl.body.replace(/(\r\n|\n)/g,
38572                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38573             body.push("'].join('');};};");
38574             body = body.join('');
38575         }
38576         
38577         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38578        
38579         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38580         eval(body);
38581         
38582         return this;
38583     },
38584
38585     applyTemplate : function(values){
38586         return this.master.compiled.call(this, values, {});
38587         //var s = this.subs;
38588     },
38589
38590     apply : function(){
38591         return this.applyTemplate.apply(this, arguments);
38592     }
38593
38594  });
38595
38596 Roo.XTemplate.from = function(el){
38597     el = Roo.getDom(el);
38598     return new Roo.XTemplate(el.value || el.innerHTML);
38599 };