Roo/form/TextItem.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         var sid = this.meta ? this.meta.id : null;
2170         var recordType = this.recordType, fields = recordType.prototype.fields;
2171         var records = [];
2172         var root = o;
2173             for(var i = 0; i < root.length; i++){
2174                     var n = root[i];
2175                 var values = {};
2176                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2177                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2178                 var f = fields.items[j];
2179                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2180                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2181                 v = f.convert(v);
2182                 values[f.name] = v;
2183             }
2184                 var record = new recordType(values, id);
2185                 record.json = n;
2186                 records[records.length] = record;
2187             }
2188             return {
2189                 records : records,
2190                 totalRecords : records.length
2191             };
2192     }
2193 });/*
2194  * Based on:
2195  * Ext JS Library 1.1.1
2196  * Copyright(c) 2006-2007, Ext JS, LLC.
2197  *
2198  * Originally Released Under LGPL - original licence link has changed is not relivant.
2199  *
2200  * Fork - LGPL
2201  * <script type="text/javascript">
2202  */
2203
2204
2205 /**
2206  * @class Roo.data.Tree
2207  * @extends Roo.util.Observable
2208  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2209  * in the tree have most standard DOM functionality.
2210  * @constructor
2211  * @param {Node} root (optional) The root node
2212  */
2213 Roo.data.Tree = function(root){
2214    this.nodeHash = {};
2215    /**
2216     * The root node for this tree
2217     * @type Node
2218     */
2219    this.root = null;
2220    if(root){
2221        this.setRootNode(root);
2222    }
2223    this.addEvents({
2224        /**
2225         * @event append
2226         * Fires when a new child node is appended to a node in this tree.
2227         * @param {Tree} tree The owner tree
2228         * @param {Node} parent The parent node
2229         * @param {Node} node The newly appended node
2230         * @param {Number} index The index of the newly appended node
2231         */
2232        "append" : true,
2233        /**
2234         * @event remove
2235         * Fires when a child node is removed from a node in this tree.
2236         * @param {Tree} tree The owner tree
2237         * @param {Node} parent The parent node
2238         * @param {Node} node The child node removed
2239         */
2240        "remove" : true,
2241        /**
2242         * @event move
2243         * Fires when a node is moved to a new location in the tree
2244         * @param {Tree} tree The owner tree
2245         * @param {Node} node The node moved
2246         * @param {Node} oldParent The old parent of this node
2247         * @param {Node} newParent The new parent of this node
2248         * @param {Number} index The index it was moved to
2249         */
2250        "move" : true,
2251        /**
2252         * @event insert
2253         * Fires when a new child node is inserted in a node in this tree.
2254         * @param {Tree} tree The owner tree
2255         * @param {Node} parent The parent node
2256         * @param {Node} node The child node inserted
2257         * @param {Node} refNode The child node the node was inserted before
2258         */
2259        "insert" : true,
2260        /**
2261         * @event beforeappend
2262         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2263         * @param {Tree} tree The owner tree
2264         * @param {Node} parent The parent node
2265         * @param {Node} node The child node to be appended
2266         */
2267        "beforeappend" : true,
2268        /**
2269         * @event beforeremove
2270         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2271         * @param {Tree} tree The owner tree
2272         * @param {Node} parent The parent node
2273         * @param {Node} node The child node to be removed
2274         */
2275        "beforeremove" : true,
2276        /**
2277         * @event beforemove
2278         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2279         * @param {Tree} tree The owner tree
2280         * @param {Node} node The node being moved
2281         * @param {Node} oldParent The parent of the node
2282         * @param {Node} newParent The new parent the node is moving to
2283         * @param {Number} index The index it is being moved to
2284         */
2285        "beforemove" : true,
2286        /**
2287         * @event beforeinsert
2288         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2289         * @param {Tree} tree The owner tree
2290         * @param {Node} parent The parent node
2291         * @param {Node} node The child node to be inserted
2292         * @param {Node} refNode The child node the node is being inserted before
2293         */
2294        "beforeinsert" : true
2295    });
2296
2297     Roo.data.Tree.superclass.constructor.call(this);
2298 };
2299
2300 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2301     pathSeparator: "/",
2302
2303     proxyNodeEvent : function(){
2304         return this.fireEvent.apply(this, arguments);
2305     },
2306
2307     /**
2308      * Returns the root node for this tree.
2309      * @return {Node}
2310      */
2311     getRootNode : function(){
2312         return this.root;
2313     },
2314
2315     /**
2316      * Sets the root node for this tree.
2317      * @param {Node} node
2318      * @return {Node}
2319      */
2320     setRootNode : function(node){
2321         this.root = node;
2322         node.ownerTree = this;
2323         node.isRoot = true;
2324         this.registerNode(node);
2325         return node;
2326     },
2327
2328     /**
2329      * Gets a node in this tree by its id.
2330      * @param {String} id
2331      * @return {Node}
2332      */
2333     getNodeById : function(id){
2334         return this.nodeHash[id];
2335     },
2336
2337     registerNode : function(node){
2338         this.nodeHash[node.id] = node;
2339     },
2340
2341     unregisterNode : function(node){
2342         delete this.nodeHash[node.id];
2343     },
2344
2345     toString : function(){
2346         return "[Tree"+(this.id?" "+this.id:"")+"]";
2347     }
2348 });
2349
2350 /**
2351  * @class Roo.data.Node
2352  * @extends Roo.util.Observable
2353  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2354  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2355  * @constructor
2356  * @param {Object} attributes The attributes/config for the node
2357  */
2358 Roo.data.Node = function(attributes){
2359     /**
2360      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2361      * @type {Object}
2362      */
2363     this.attributes = attributes || {};
2364     this.leaf = this.attributes.leaf;
2365     /**
2366      * The node id. @type String
2367      */
2368     this.id = this.attributes.id;
2369     if(!this.id){
2370         this.id = Roo.id(null, "ynode-");
2371         this.attributes.id = this.id;
2372     }
2373      
2374     
2375     /**
2376      * All child nodes of this node. @type Array
2377      */
2378     this.childNodes = [];
2379     if(!this.childNodes.indexOf){ // indexOf is a must
2380         this.childNodes.indexOf = function(o){
2381             for(var i = 0, len = this.length; i < len; i++){
2382                 if(this[i] == o) {
2383                     return i;
2384                 }
2385             }
2386             return -1;
2387         };
2388     }
2389     /**
2390      * The parent node for this node. @type Node
2391      */
2392     this.parentNode = null;
2393     /**
2394      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2395      */
2396     this.firstChild = null;
2397     /**
2398      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2399      */
2400     this.lastChild = null;
2401     /**
2402      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2403      */
2404     this.previousSibling = null;
2405     /**
2406      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2407      */
2408     this.nextSibling = null;
2409
2410     this.addEvents({
2411        /**
2412         * @event append
2413         * Fires when a new child node is appended
2414         * @param {Tree} tree The owner tree
2415         * @param {Node} this This node
2416         * @param {Node} node The newly appended node
2417         * @param {Number} index The index of the newly appended node
2418         */
2419        "append" : true,
2420        /**
2421         * @event remove
2422         * Fires when a child node is removed
2423         * @param {Tree} tree The owner tree
2424         * @param {Node} this This node
2425         * @param {Node} node The removed node
2426         */
2427        "remove" : true,
2428        /**
2429         * @event move
2430         * Fires when this node is moved to a new location in the tree
2431         * @param {Tree} tree The owner tree
2432         * @param {Node} this This node
2433         * @param {Node} oldParent The old parent of this node
2434         * @param {Node} newParent The new parent of this node
2435         * @param {Number} index The index it was moved to
2436         */
2437        "move" : true,
2438        /**
2439         * @event insert
2440         * Fires when a new child node is inserted.
2441         * @param {Tree} tree The owner tree
2442         * @param {Node} this This node
2443         * @param {Node} node The child node inserted
2444         * @param {Node} refNode The child node the node was inserted before
2445         */
2446        "insert" : true,
2447        /**
2448         * @event beforeappend
2449         * Fires before a new child is appended, return false to cancel the append.
2450         * @param {Tree} tree The owner tree
2451         * @param {Node} this This node
2452         * @param {Node} node The child node to be appended
2453         */
2454        "beforeappend" : true,
2455        /**
2456         * @event beforeremove
2457         * Fires before a child is removed, return false to cancel the remove.
2458         * @param {Tree} tree The owner tree
2459         * @param {Node} this This node
2460         * @param {Node} node The child node to be removed
2461         */
2462        "beforeremove" : true,
2463        /**
2464         * @event beforemove
2465         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2466         * @param {Tree} tree The owner tree
2467         * @param {Node} this This node
2468         * @param {Node} oldParent The parent of this node
2469         * @param {Node} newParent The new parent this node is moving to
2470         * @param {Number} index The index it is being moved to
2471         */
2472        "beforemove" : true,
2473        /**
2474         * @event beforeinsert
2475         * Fires before a new child is inserted, return false to cancel the insert.
2476         * @param {Tree} tree The owner tree
2477         * @param {Node} this This node
2478         * @param {Node} node The child node to be inserted
2479         * @param {Node} refNode The child node the node is being inserted before
2480         */
2481        "beforeinsert" : true
2482    });
2483     this.listeners = this.attributes.listeners;
2484     Roo.data.Node.superclass.constructor.call(this);
2485 };
2486
2487 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2488     fireEvent : function(evtName){
2489         // first do standard event for this node
2490         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2491             return false;
2492         }
2493         // then bubble it up to the tree if the event wasn't cancelled
2494         var ot = this.getOwnerTree();
2495         if(ot){
2496             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2497                 return false;
2498             }
2499         }
2500         return true;
2501     },
2502
2503     /**
2504      * Returns true if this node is a leaf
2505      * @return {Boolean}
2506      */
2507     isLeaf : function(){
2508         return this.leaf === true;
2509     },
2510
2511     // private
2512     setFirstChild : function(node){
2513         this.firstChild = node;
2514     },
2515
2516     //private
2517     setLastChild : function(node){
2518         this.lastChild = node;
2519     },
2520
2521
2522     /**
2523      * Returns true if this node is the last child of its parent
2524      * @return {Boolean}
2525      */
2526     isLast : function(){
2527        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2528     },
2529
2530     /**
2531      * Returns true if this node is the first child of its parent
2532      * @return {Boolean}
2533      */
2534     isFirst : function(){
2535        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2536     },
2537
2538     hasChildNodes : function(){
2539         return !this.isLeaf() && this.childNodes.length > 0;
2540     },
2541
2542     /**
2543      * Insert node(s) as the last child node of this node.
2544      * @param {Node/Array} node The node or Array of nodes to append
2545      * @return {Node} The appended node if single append, or null if an array was passed
2546      */
2547     appendChild : function(node){
2548         var multi = false;
2549         if(node instanceof Array){
2550             multi = node;
2551         }else if(arguments.length > 1){
2552             multi = arguments;
2553         }
2554         // if passed an array or multiple args do them one by one
2555         if(multi){
2556             for(var i = 0, len = multi.length; i < len; i++) {
2557                 this.appendChild(multi[i]);
2558             }
2559         }else{
2560             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2561                 return false;
2562             }
2563             var index = this.childNodes.length;
2564             var oldParent = node.parentNode;
2565             // it's a move, make sure we move it cleanly
2566             if(oldParent){
2567                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2568                     return false;
2569                 }
2570                 oldParent.removeChild(node);
2571             }
2572             index = this.childNodes.length;
2573             if(index == 0){
2574                 this.setFirstChild(node);
2575             }
2576             this.childNodes.push(node);
2577             node.parentNode = this;
2578             var ps = this.childNodes[index-1];
2579             if(ps){
2580                 node.previousSibling = ps;
2581                 ps.nextSibling = node;
2582             }else{
2583                 node.previousSibling = null;
2584             }
2585             node.nextSibling = null;
2586             this.setLastChild(node);
2587             node.setOwnerTree(this.getOwnerTree());
2588             this.fireEvent("append", this.ownerTree, this, node, index);
2589             if(oldParent){
2590                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2591             }
2592             return node;
2593         }
2594     },
2595
2596     /**
2597      * Removes a child node from this node.
2598      * @param {Node} node The node to remove
2599      * @return {Node} The removed node
2600      */
2601     removeChild : function(node){
2602         var index = this.childNodes.indexOf(node);
2603         if(index == -1){
2604             return false;
2605         }
2606         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2607             return false;
2608         }
2609
2610         // remove it from childNodes collection
2611         this.childNodes.splice(index, 1);
2612
2613         // update siblings
2614         if(node.previousSibling){
2615             node.previousSibling.nextSibling = node.nextSibling;
2616         }
2617         if(node.nextSibling){
2618             node.nextSibling.previousSibling = node.previousSibling;
2619         }
2620
2621         // update child refs
2622         if(this.firstChild == node){
2623             this.setFirstChild(node.nextSibling);
2624         }
2625         if(this.lastChild == node){
2626             this.setLastChild(node.previousSibling);
2627         }
2628
2629         node.setOwnerTree(null);
2630         // clear any references from the node
2631         node.parentNode = null;
2632         node.previousSibling = null;
2633         node.nextSibling = null;
2634         this.fireEvent("remove", this.ownerTree, this, node);
2635         return node;
2636     },
2637
2638     /**
2639      * Inserts the first node before the second node in this nodes childNodes collection.
2640      * @param {Node} node The node to insert
2641      * @param {Node} refNode The node to insert before (if null the node is appended)
2642      * @return {Node} The inserted node
2643      */
2644     insertBefore : function(node, refNode){
2645         if(!refNode){ // like standard Dom, refNode can be null for append
2646             return this.appendChild(node);
2647         }
2648         // nothing to do
2649         if(node == refNode){
2650             return false;
2651         }
2652
2653         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2654             return false;
2655         }
2656         var index = this.childNodes.indexOf(refNode);
2657         var oldParent = node.parentNode;
2658         var refIndex = index;
2659
2660         // when moving internally, indexes will change after remove
2661         if(oldParent == this && this.childNodes.indexOf(node) < index){
2662             refIndex--;
2663         }
2664
2665         // it's a move, make sure we move it cleanly
2666         if(oldParent){
2667             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2668                 return false;
2669             }
2670             oldParent.removeChild(node);
2671         }
2672         if(refIndex == 0){
2673             this.setFirstChild(node);
2674         }
2675         this.childNodes.splice(refIndex, 0, node);
2676         node.parentNode = this;
2677         var ps = this.childNodes[refIndex-1];
2678         if(ps){
2679             node.previousSibling = ps;
2680             ps.nextSibling = node;
2681         }else{
2682             node.previousSibling = null;
2683         }
2684         node.nextSibling = refNode;
2685         refNode.previousSibling = node;
2686         node.setOwnerTree(this.getOwnerTree());
2687         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2688         if(oldParent){
2689             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2690         }
2691         return node;
2692     },
2693
2694     /**
2695      * Returns the child node at the specified index.
2696      * @param {Number} index
2697      * @return {Node}
2698      */
2699     item : function(index){
2700         return this.childNodes[index];
2701     },
2702
2703     /**
2704      * Replaces one child node in this node with another.
2705      * @param {Node} newChild The replacement node
2706      * @param {Node} oldChild The node to replace
2707      * @return {Node} The replaced node
2708      */
2709     replaceChild : function(newChild, oldChild){
2710         this.insertBefore(newChild, oldChild);
2711         this.removeChild(oldChild);
2712         return oldChild;
2713     },
2714
2715     /**
2716      * Returns the index of a child node
2717      * @param {Node} node
2718      * @return {Number} The index of the node or -1 if it was not found
2719      */
2720     indexOf : function(child){
2721         return this.childNodes.indexOf(child);
2722     },
2723
2724     /**
2725      * Returns the tree this node is in.
2726      * @return {Tree}
2727      */
2728     getOwnerTree : function(){
2729         // if it doesn't have one, look for one
2730         if(!this.ownerTree){
2731             var p = this;
2732             while(p){
2733                 if(p.ownerTree){
2734                     this.ownerTree = p.ownerTree;
2735                     break;
2736                 }
2737                 p = p.parentNode;
2738             }
2739         }
2740         return this.ownerTree;
2741     },
2742
2743     /**
2744      * Returns depth of this node (the root node has a depth of 0)
2745      * @return {Number}
2746      */
2747     getDepth : function(){
2748         var depth = 0;
2749         var p = this;
2750         while(p.parentNode){
2751             ++depth;
2752             p = p.parentNode;
2753         }
2754         return depth;
2755     },
2756
2757     // private
2758     setOwnerTree : function(tree){
2759         // if it's move, we need to update everyone
2760         if(tree != this.ownerTree){
2761             if(this.ownerTree){
2762                 this.ownerTree.unregisterNode(this);
2763             }
2764             this.ownerTree = tree;
2765             var cs = this.childNodes;
2766             for(var i = 0, len = cs.length; i < len; i++) {
2767                 cs[i].setOwnerTree(tree);
2768             }
2769             if(tree){
2770                 tree.registerNode(this);
2771             }
2772         }
2773     },
2774
2775     /**
2776      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2777      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2778      * @return {String} The path
2779      */
2780     getPath : function(attr){
2781         attr = attr || "id";
2782         var p = this.parentNode;
2783         var b = [this.attributes[attr]];
2784         while(p){
2785             b.unshift(p.attributes[attr]);
2786             p = p.parentNode;
2787         }
2788         var sep = this.getOwnerTree().pathSeparator;
2789         return sep + b.join(sep);
2790     },
2791
2792     /**
2793      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2794      * function call will be the scope provided or the current node. The arguments to the function
2795      * will be the args provided or the current node. If the function returns false at any point,
2796      * the bubble is stopped.
2797      * @param {Function} fn The function to call
2798      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2799      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2800      */
2801     bubble : function(fn, scope, args){
2802         var p = this;
2803         while(p){
2804             if(fn.call(scope || p, args || p) === false){
2805                 break;
2806             }
2807             p = p.parentNode;
2808         }
2809     },
2810
2811     /**
2812      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2813      * function call will be the scope provided or the current node. The arguments to the function
2814      * will be the args provided or the current node. If the function returns false at any point,
2815      * the cascade is stopped on that branch.
2816      * @param {Function} fn The function to call
2817      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2818      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2819      */
2820     cascade : function(fn, scope, args){
2821         if(fn.call(scope || this, args || this) !== false){
2822             var cs = this.childNodes;
2823             for(var i = 0, len = cs.length; i < len; i++) {
2824                 cs[i].cascade(fn, scope, args);
2825             }
2826         }
2827     },
2828
2829     /**
2830      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2831      * function call will be the scope provided or the current node. The arguments to the function
2832      * will be the args provided or the current node. If the function returns false at any point,
2833      * the iteration stops.
2834      * @param {Function} fn The function to call
2835      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2836      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2837      */
2838     eachChild : function(fn, scope, args){
2839         var cs = this.childNodes;
2840         for(var i = 0, len = cs.length; i < len; i++) {
2841                 if(fn.call(scope || this, args || cs[i]) === false){
2842                     break;
2843                 }
2844         }
2845     },
2846
2847     /**
2848      * Finds the first child that has the attribute with the specified value.
2849      * @param {String} attribute The attribute name
2850      * @param {Mixed} value The value to search for
2851      * @return {Node} The found child or null if none was found
2852      */
2853     findChild : function(attribute, value){
2854         var cs = this.childNodes;
2855         for(var i = 0, len = cs.length; i < len; i++) {
2856                 if(cs[i].attributes[attribute] == value){
2857                     return cs[i];
2858                 }
2859         }
2860         return null;
2861     },
2862
2863     /**
2864      * Finds the first child by a custom function. The child matches if the function passed
2865      * returns true.
2866      * @param {Function} fn
2867      * @param {Object} scope (optional)
2868      * @return {Node} The found child or null if none was found
2869      */
2870     findChildBy : function(fn, scope){
2871         var cs = this.childNodes;
2872         for(var i = 0, len = cs.length; i < len; i++) {
2873                 if(fn.call(scope||cs[i], cs[i]) === true){
2874                     return cs[i];
2875                 }
2876         }
2877         return null;
2878     },
2879
2880     /**
2881      * Sorts this nodes children using the supplied sort function
2882      * @param {Function} fn
2883      * @param {Object} scope (optional)
2884      */
2885     sort : function(fn, scope){
2886         var cs = this.childNodes;
2887         var len = cs.length;
2888         if(len > 0){
2889             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2890             cs.sort(sortFn);
2891             for(var i = 0; i < len; i++){
2892                 var n = cs[i];
2893                 n.previousSibling = cs[i-1];
2894                 n.nextSibling = cs[i+1];
2895                 if(i == 0){
2896                     this.setFirstChild(n);
2897                 }
2898                 if(i == len-1){
2899                     this.setLastChild(n);
2900                 }
2901             }
2902         }
2903     },
2904
2905     /**
2906      * Returns true if this node is an ancestor (at any point) of the passed node.
2907      * @param {Node} node
2908      * @return {Boolean}
2909      */
2910     contains : function(node){
2911         return node.isAncestor(this);
2912     },
2913
2914     /**
2915      * Returns true if the passed node is an ancestor (at any point) of this node.
2916      * @param {Node} node
2917      * @return {Boolean}
2918      */
2919     isAncestor : function(node){
2920         var p = this.parentNode;
2921         while(p){
2922             if(p == node){
2923                 return true;
2924             }
2925             p = p.parentNode;
2926         }
2927         return false;
2928     },
2929
2930     toString : function(){
2931         return "[Node"+(this.id?" "+this.id:"")+"]";
2932     }
2933 });/*
2934  * Based on:
2935  * Ext JS Library 1.1.1
2936  * Copyright(c) 2006-2007, Ext JS, LLC.
2937  *
2938  * Originally Released Under LGPL - original licence link has changed is not relivant.
2939  *
2940  * Fork - LGPL
2941  * <script type="text/javascript">
2942  */
2943  (function(){ 
2944 /**
2945  * @class Roo.Layer
2946  * @extends Roo.Element
2947  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2948  * automatic maintaining of shadow/shim positions.
2949  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2950  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2951  * you can pass a string with a CSS class name. False turns off the shadow.
2952  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2953  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2954  * @cfg {String} cls CSS class to add to the element
2955  * @cfg {Number} zindex Starting z-index (defaults to 11000)
2956  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2957  * @constructor
2958  * @param {Object} config An object with config options.
2959  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2960  */
2961
2962 Roo.Layer = function(config, existingEl){
2963     config = config || {};
2964     var dh = Roo.DomHelper;
2965     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2966     if(existingEl){
2967         this.dom = Roo.getDom(existingEl);
2968     }
2969     if(!this.dom){
2970         var o = config.dh || {tag: "div", cls: "x-layer"};
2971         this.dom = dh.append(pel, o);
2972     }
2973     if(config.cls){
2974         this.addClass(config.cls);
2975     }
2976     this.constrain = config.constrain !== false;
2977     this.visibilityMode = Roo.Element.VISIBILITY;
2978     if(config.id){
2979         this.id = this.dom.id = config.id;
2980     }else{
2981         this.id = Roo.id(this.dom);
2982     }
2983     this.zindex = config.zindex || this.getZIndex();
2984     this.position("absolute", this.zindex);
2985     if(config.shadow){
2986         this.shadowOffset = config.shadowOffset || 4;
2987         this.shadow = new Roo.Shadow({
2988             offset : this.shadowOffset,
2989             mode : config.shadow
2990         });
2991     }else{
2992         this.shadowOffset = 0;
2993     }
2994     this.useShim = config.shim !== false && Roo.useShims;
2995     this.useDisplay = config.useDisplay;
2996     this.hide();
2997 };
2998
2999 var supr = Roo.Element.prototype;
3000
3001 // shims are shared among layer to keep from having 100 iframes
3002 var shims = [];
3003
3004 Roo.extend(Roo.Layer, Roo.Element, {
3005
3006     getZIndex : function(){
3007         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3008     },
3009
3010     getShim : function(){
3011         if(!this.useShim){
3012             return null;
3013         }
3014         if(this.shim){
3015             return this.shim;
3016         }
3017         var shim = shims.shift();
3018         if(!shim){
3019             shim = this.createShim();
3020             shim.enableDisplayMode('block');
3021             shim.dom.style.display = 'none';
3022             shim.dom.style.visibility = 'visible';
3023         }
3024         var pn = this.dom.parentNode;
3025         if(shim.dom.parentNode != pn){
3026             pn.insertBefore(shim.dom, this.dom);
3027         }
3028         shim.setStyle('z-index', this.getZIndex()-2);
3029         this.shim = shim;
3030         return shim;
3031     },
3032
3033     hideShim : function(){
3034         if(this.shim){
3035             this.shim.setDisplayed(false);
3036             shims.push(this.shim);
3037             delete this.shim;
3038         }
3039     },
3040
3041     disableShadow : function(){
3042         if(this.shadow){
3043             this.shadowDisabled = true;
3044             this.shadow.hide();
3045             this.lastShadowOffset = this.shadowOffset;
3046             this.shadowOffset = 0;
3047         }
3048     },
3049
3050     enableShadow : function(show){
3051         if(this.shadow){
3052             this.shadowDisabled = false;
3053             this.shadowOffset = this.lastShadowOffset;
3054             delete this.lastShadowOffset;
3055             if(show){
3056                 this.sync(true);
3057             }
3058         }
3059     },
3060
3061     // private
3062     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3063     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3064     sync : function(doShow){
3065         var sw = this.shadow;
3066         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3067             var sh = this.getShim();
3068
3069             var w = this.getWidth(),
3070                 h = this.getHeight();
3071
3072             var l = this.getLeft(true),
3073                 t = this.getTop(true);
3074
3075             if(sw && !this.shadowDisabled){
3076                 if(doShow && !sw.isVisible()){
3077                     sw.show(this);
3078                 }else{
3079                     sw.realign(l, t, w, h);
3080                 }
3081                 if(sh){
3082                     if(doShow){
3083                        sh.show();
3084                     }
3085                     // fit the shim behind the shadow, so it is shimmed too
3086                     var a = sw.adjusts, s = sh.dom.style;
3087                     s.left = (Math.min(l, l+a.l))+"px";
3088                     s.top = (Math.min(t, t+a.t))+"px";
3089                     s.width = (w+a.w)+"px";
3090                     s.height = (h+a.h)+"px";
3091                 }
3092             }else if(sh){
3093                 if(doShow){
3094                    sh.show();
3095                 }
3096                 sh.setSize(w, h);
3097                 sh.setLeftTop(l, t);
3098             }
3099             
3100         }
3101     },
3102
3103     // private
3104     destroy : function(){
3105         this.hideShim();
3106         if(this.shadow){
3107             this.shadow.hide();
3108         }
3109         this.removeAllListeners();
3110         var pn = this.dom.parentNode;
3111         if(pn){
3112             pn.removeChild(this.dom);
3113         }
3114         Roo.Element.uncache(this.id);
3115     },
3116
3117     remove : function(){
3118         this.destroy();
3119     },
3120
3121     // private
3122     beginUpdate : function(){
3123         this.updating = true;
3124     },
3125
3126     // private
3127     endUpdate : function(){
3128         this.updating = false;
3129         this.sync(true);
3130     },
3131
3132     // private
3133     hideUnders : function(negOffset){
3134         if(this.shadow){
3135             this.shadow.hide();
3136         }
3137         this.hideShim();
3138     },
3139
3140     // private
3141     constrainXY : function(){
3142         if(this.constrain){
3143             var vw = Roo.lib.Dom.getViewWidth(),
3144                 vh = Roo.lib.Dom.getViewHeight();
3145             var s = Roo.get(document).getScroll();
3146
3147             var xy = this.getXY();
3148             var x = xy[0], y = xy[1];   
3149             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3150             // only move it if it needs it
3151             var moved = false;
3152             // first validate right/bottom
3153             if((x + w) > vw+s.left){
3154                 x = vw - w - this.shadowOffset;
3155                 moved = true;
3156             }
3157             if((y + h) > vh+s.top){
3158                 y = vh - h - this.shadowOffset;
3159                 moved = true;
3160             }
3161             // then make sure top/left isn't negative
3162             if(x < s.left){
3163                 x = s.left;
3164                 moved = true;
3165             }
3166             if(y < s.top){
3167                 y = s.top;
3168                 moved = true;
3169             }
3170             if(moved){
3171                 if(this.avoidY){
3172                     var ay = this.avoidY;
3173                     if(y <= ay && (y+h) >= ay){
3174                         y = ay-h-5;   
3175                     }
3176                 }
3177                 xy = [x, y];
3178                 this.storeXY(xy);
3179                 supr.setXY.call(this, xy);
3180                 this.sync();
3181             }
3182         }
3183     },
3184
3185     isVisible : function(){
3186         return this.visible;    
3187     },
3188
3189     // private
3190     showAction : function(){
3191         this.visible = true; // track visibility to prevent getStyle calls
3192         if(this.useDisplay === true){
3193             this.setDisplayed("");
3194         }else if(this.lastXY){
3195             supr.setXY.call(this, this.lastXY);
3196         }else if(this.lastLT){
3197             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3198         }
3199     },
3200
3201     // private
3202     hideAction : function(){
3203         this.visible = false;
3204         if(this.useDisplay === true){
3205             this.setDisplayed(false);
3206         }else{
3207             this.setLeftTop(-10000,-10000);
3208         }
3209     },
3210
3211     // overridden Element method
3212     setVisible : function(v, a, d, c, e){
3213         if(v){
3214             this.showAction();
3215         }
3216         if(a && v){
3217             var cb = function(){
3218                 this.sync(true);
3219                 if(c){
3220                     c();
3221                 }
3222             }.createDelegate(this);
3223             supr.setVisible.call(this, true, true, d, cb, e);
3224         }else{
3225             if(!v){
3226                 this.hideUnders(true);
3227             }
3228             var cb = c;
3229             if(a){
3230                 cb = function(){
3231                     this.hideAction();
3232                     if(c){
3233                         c();
3234                     }
3235                 }.createDelegate(this);
3236             }
3237             supr.setVisible.call(this, v, a, d, cb, e);
3238             if(v){
3239                 this.sync(true);
3240             }else if(!a){
3241                 this.hideAction();
3242             }
3243         }
3244     },
3245
3246     storeXY : function(xy){
3247         delete this.lastLT;
3248         this.lastXY = xy;
3249     },
3250
3251     storeLeftTop : function(left, top){
3252         delete this.lastXY;
3253         this.lastLT = [left, top];
3254     },
3255
3256     // private
3257     beforeFx : function(){
3258         this.beforeAction();
3259         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3260     },
3261
3262     // private
3263     afterFx : function(){
3264         Roo.Layer.superclass.afterFx.apply(this, arguments);
3265         this.sync(this.isVisible());
3266     },
3267
3268     // private
3269     beforeAction : function(){
3270         if(!this.updating && this.shadow){
3271             this.shadow.hide();
3272         }
3273     },
3274
3275     // overridden Element method
3276     setLeft : function(left){
3277         this.storeLeftTop(left, this.getTop(true));
3278         supr.setLeft.apply(this, arguments);
3279         this.sync();
3280     },
3281
3282     setTop : function(top){
3283         this.storeLeftTop(this.getLeft(true), top);
3284         supr.setTop.apply(this, arguments);
3285         this.sync();
3286     },
3287
3288     setLeftTop : function(left, top){
3289         this.storeLeftTop(left, top);
3290         supr.setLeftTop.apply(this, arguments);
3291         this.sync();
3292     },
3293
3294     setXY : function(xy, a, d, c, e){
3295         this.fixDisplay();
3296         this.beforeAction();
3297         this.storeXY(xy);
3298         var cb = this.createCB(c);
3299         supr.setXY.call(this, xy, a, d, cb, e);
3300         if(!a){
3301             cb();
3302         }
3303     },
3304
3305     // private
3306     createCB : function(c){
3307         var el = this;
3308         return function(){
3309             el.constrainXY();
3310             el.sync(true);
3311             if(c){
3312                 c();
3313             }
3314         };
3315     },
3316
3317     // overridden Element method
3318     setX : function(x, a, d, c, e){
3319         this.setXY([x, this.getY()], a, d, c, e);
3320     },
3321
3322     // overridden Element method
3323     setY : function(y, a, d, c, e){
3324         this.setXY([this.getX(), y], a, d, c, e);
3325     },
3326
3327     // overridden Element method
3328     setSize : function(w, h, a, d, c, e){
3329         this.beforeAction();
3330         var cb = this.createCB(c);
3331         supr.setSize.call(this, w, h, a, d, cb, e);
3332         if(!a){
3333             cb();
3334         }
3335     },
3336
3337     // overridden Element method
3338     setWidth : function(w, a, d, c, e){
3339         this.beforeAction();
3340         var cb = this.createCB(c);
3341         supr.setWidth.call(this, w, a, d, cb, e);
3342         if(!a){
3343             cb();
3344         }
3345     },
3346
3347     // overridden Element method
3348     setHeight : function(h, a, d, c, e){
3349         this.beforeAction();
3350         var cb = this.createCB(c);
3351         supr.setHeight.call(this, h, a, d, cb, e);
3352         if(!a){
3353             cb();
3354         }
3355     },
3356
3357     // overridden Element method
3358     setBounds : function(x, y, w, h, a, d, c, e){
3359         this.beforeAction();
3360         var cb = this.createCB(c);
3361         if(!a){
3362             this.storeXY([x, y]);
3363             supr.setXY.call(this, [x, y]);
3364             supr.setSize.call(this, w, h, a, d, cb, e);
3365             cb();
3366         }else{
3367             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3368         }
3369         return this;
3370     },
3371     
3372     /**
3373      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3374      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3375      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3376      * @param {Number} zindex The new z-index to set
3377      * @return {this} The Layer
3378      */
3379     setZIndex : function(zindex){
3380         this.zindex = zindex;
3381         this.setStyle("z-index", zindex + 2);
3382         if(this.shadow){
3383             this.shadow.setZIndex(zindex + 1);
3384         }
3385         if(this.shim){
3386             this.shim.setStyle("z-index", zindex);
3387         }
3388     }
3389 });
3390 })();/*
3391  * Based on:
3392  * Ext JS Library 1.1.1
3393  * Copyright(c) 2006-2007, Ext JS, LLC.
3394  *
3395  * Originally Released Under LGPL - original licence link has changed is not relivant.
3396  *
3397  * Fork - LGPL
3398  * <script type="text/javascript">
3399  */
3400
3401
3402 /**
3403  * @class Roo.Shadow
3404  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3405  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3406  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3407  * @constructor
3408  * Create a new Shadow
3409  * @param {Object} config The config object
3410  */
3411 Roo.Shadow = function(config){
3412     Roo.apply(this, config);
3413     if(typeof this.mode != "string"){
3414         this.mode = this.defaultMode;
3415     }
3416     var o = this.offset, a = {h: 0};
3417     var rad = Math.floor(this.offset/2);
3418     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3419         case "drop":
3420             a.w = 0;
3421             a.l = a.t = o;
3422             a.t -= 1;
3423             if(Roo.isIE){
3424                 a.l -= this.offset + rad;
3425                 a.t -= this.offset + rad;
3426                 a.w -= rad;
3427                 a.h -= rad;
3428                 a.t += 1;
3429             }
3430         break;
3431         case "sides":
3432             a.w = (o*2);
3433             a.l = -o;
3434             a.t = o-1;
3435             if(Roo.isIE){
3436                 a.l -= (this.offset - rad);
3437                 a.t -= this.offset + rad;
3438                 a.l += 1;
3439                 a.w -= (this.offset - rad)*2;
3440                 a.w -= rad + 1;
3441                 a.h -= 1;
3442             }
3443         break;
3444         case "frame":
3445             a.w = a.h = (o*2);
3446             a.l = a.t = -o;
3447             a.t += 1;
3448             a.h -= 2;
3449             if(Roo.isIE){
3450                 a.l -= (this.offset - rad);
3451                 a.t -= (this.offset - rad);
3452                 a.l += 1;
3453                 a.w -= (this.offset + rad + 1);
3454                 a.h -= (this.offset + rad);
3455                 a.h += 1;
3456             }
3457         break;
3458     };
3459
3460     this.adjusts = a;
3461 };
3462
3463 Roo.Shadow.prototype = {
3464     /**
3465      * @cfg {String} mode
3466      * The shadow display mode.  Supports the following options:<br />
3467      * sides: Shadow displays on both sides and bottom only<br />
3468      * frame: Shadow displays equally on all four sides<br />
3469      * drop: Traditional bottom-right drop shadow (default)
3470      */
3471     /**
3472      * @cfg {String} offset
3473      * The number of pixels to offset the shadow from the element (defaults to 4)
3474      */
3475     offset: 4,
3476
3477     // private
3478     defaultMode: "drop",
3479
3480     /**
3481      * Displays the shadow under the target element
3482      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3483      */
3484     show : function(target){
3485         target = Roo.get(target);
3486         if(!this.el){
3487             this.el = Roo.Shadow.Pool.pull();
3488             if(this.el.dom.nextSibling != target.dom){
3489                 this.el.insertBefore(target);
3490             }
3491         }
3492         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3493         if(Roo.isIE){
3494             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3495         }
3496         this.realign(
3497             target.getLeft(true),
3498             target.getTop(true),
3499             target.getWidth(),
3500             target.getHeight()
3501         );
3502         this.el.dom.style.display = "block";
3503     },
3504
3505     /**
3506      * Returns true if the shadow is visible, else false
3507      */
3508     isVisible : function(){
3509         return this.el ? true : false;  
3510     },
3511
3512     /**
3513      * Direct alignment when values are already available. Show must be called at least once before
3514      * calling this method to ensure it is initialized.
3515      * @param {Number} left The target element left position
3516      * @param {Number} top The target element top position
3517      * @param {Number} width The target element width
3518      * @param {Number} height The target element height
3519      */
3520     realign : function(l, t, w, h){
3521         if(!this.el){
3522             return;
3523         }
3524         var a = this.adjusts, d = this.el.dom, s = d.style;
3525         var iea = 0;
3526         s.left = (l+a.l)+"px";
3527         s.top = (t+a.t)+"px";
3528         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3529  
3530         if(s.width != sws || s.height != shs){
3531             s.width = sws;
3532             s.height = shs;
3533             if(!Roo.isIE){
3534                 var cn = d.childNodes;
3535                 var sww = Math.max(0, (sw-12))+"px";
3536                 cn[0].childNodes[1].style.width = sww;
3537                 cn[1].childNodes[1].style.width = sww;
3538                 cn[2].childNodes[1].style.width = sww;
3539                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3540             }
3541         }
3542     },
3543
3544     /**
3545      * Hides this shadow
3546      */
3547     hide : function(){
3548         if(this.el){
3549             this.el.dom.style.display = "none";
3550             Roo.Shadow.Pool.push(this.el);
3551             delete this.el;
3552         }
3553     },
3554
3555     /**
3556      * Adjust the z-index of this shadow
3557      * @param {Number} zindex The new z-index
3558      */
3559     setZIndex : function(z){
3560         this.zIndex = z;
3561         if(this.el){
3562             this.el.setStyle("z-index", z);
3563         }
3564     }
3565 };
3566
3567 // Private utility class that manages the internal Shadow cache
3568 Roo.Shadow.Pool = function(){
3569     var p = [];
3570     var markup = Roo.isIE ?
3571                  '<div class="x-ie-shadow"></div>' :
3572                  '<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>';
3573     return {
3574         pull : function(){
3575             var sh = p.shift();
3576             if(!sh){
3577                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3578                 sh.autoBoxAdjust = false;
3579             }
3580             return sh;
3581         },
3582
3583         push : function(sh){
3584             p.push(sh);
3585         }
3586     };
3587 }();/*
3588  * Based on:
3589  * Ext JS Library 1.1.1
3590  * Copyright(c) 2006-2007, Ext JS, LLC.
3591  *
3592  * Originally Released Under LGPL - original licence link has changed is not relivant.
3593  *
3594  * Fork - LGPL
3595  * <script type="text/javascript">
3596  */
3597
3598
3599 /**
3600  * @class Roo.SplitBar
3601  * @extends Roo.util.Observable
3602  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3603  * <br><br>
3604  * Usage:
3605  * <pre><code>
3606 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3607                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3608 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3609 split.minSize = 100;
3610 split.maxSize = 600;
3611 split.animate = true;
3612 split.on('moved', splitterMoved);
3613 </code></pre>
3614  * @constructor
3615  * Create a new SplitBar
3616  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3617  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3618  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3619  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3620                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3621                         position of the SplitBar).
3622  */
3623 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3624     
3625     /** @private */
3626     this.el = Roo.get(dragElement, true);
3627     this.el.dom.unselectable = "on";
3628     /** @private */
3629     this.resizingEl = Roo.get(resizingElement, true);
3630
3631     /**
3632      * @private
3633      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3634      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3635      * @type Number
3636      */
3637     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3638     
3639     /**
3640      * The minimum size of the resizing element. (Defaults to 0)
3641      * @type Number
3642      */
3643     this.minSize = 0;
3644     
3645     /**
3646      * The maximum size of the resizing element. (Defaults to 2000)
3647      * @type Number
3648      */
3649     this.maxSize = 2000;
3650     
3651     /**
3652      * Whether to animate the transition to the new size
3653      * @type Boolean
3654      */
3655     this.animate = false;
3656     
3657     /**
3658      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3659      * @type Boolean
3660      */
3661     this.useShim = false;
3662     
3663     /** @private */
3664     this.shim = null;
3665     
3666     if(!existingProxy){
3667         /** @private */
3668         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3669     }else{
3670         this.proxy = Roo.get(existingProxy).dom;
3671     }
3672     /** @private */
3673     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3674     
3675     /** @private */
3676     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3677     
3678     /** @private */
3679     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3680     
3681     /** @private */
3682     this.dragSpecs = {};
3683     
3684     /**
3685      * @private The adapter to use to positon and resize elements
3686      */
3687     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3688     this.adapter.init(this);
3689     
3690     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3691         /** @private */
3692         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3693         this.el.addClass("x-splitbar-h");
3694     }else{
3695         /** @private */
3696         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3697         this.el.addClass("x-splitbar-v");
3698     }
3699     
3700     this.addEvents({
3701         /**
3702          * @event resize
3703          * Fires when the splitter is moved (alias for {@link #event-moved})
3704          * @param {Roo.SplitBar} this
3705          * @param {Number} newSize the new width or height
3706          */
3707         "resize" : true,
3708         /**
3709          * @event moved
3710          * Fires when the splitter is moved
3711          * @param {Roo.SplitBar} this
3712          * @param {Number} newSize the new width or height
3713          */
3714         "moved" : true,
3715         /**
3716          * @event beforeresize
3717          * Fires before the splitter is dragged
3718          * @param {Roo.SplitBar} this
3719          */
3720         "beforeresize" : true,
3721
3722         "beforeapply" : true
3723     });
3724
3725     Roo.util.Observable.call(this);
3726 };
3727
3728 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3729     onStartProxyDrag : function(x, y){
3730         this.fireEvent("beforeresize", this);
3731         if(!this.overlay){
3732             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3733             o.unselectable();
3734             o.enableDisplayMode("block");
3735             // all splitbars share the same overlay
3736             Roo.SplitBar.prototype.overlay = o;
3737         }
3738         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3739         this.overlay.show();
3740         Roo.get(this.proxy).setDisplayed("block");
3741         var size = this.adapter.getElementSize(this);
3742         this.activeMinSize = this.getMinimumSize();;
3743         this.activeMaxSize = this.getMaximumSize();;
3744         var c1 = size - this.activeMinSize;
3745         var c2 = Math.max(this.activeMaxSize - size, 0);
3746         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3747             this.dd.resetConstraints();
3748             this.dd.setXConstraint(
3749                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3750                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3751             );
3752             this.dd.setYConstraint(0, 0);
3753         }else{
3754             this.dd.resetConstraints();
3755             this.dd.setXConstraint(0, 0);
3756             this.dd.setYConstraint(
3757                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3758                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3759             );
3760          }
3761         this.dragSpecs.startSize = size;
3762         this.dragSpecs.startPoint = [x, y];
3763         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3764     },
3765     
3766     /** 
3767      * @private Called after the drag operation by the DDProxy
3768      */
3769     onEndProxyDrag : function(e){
3770         Roo.get(this.proxy).setDisplayed(false);
3771         var endPoint = Roo.lib.Event.getXY(e);
3772         if(this.overlay){
3773             this.overlay.hide();
3774         }
3775         var newSize;
3776         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3777             newSize = this.dragSpecs.startSize + 
3778                 (this.placement == Roo.SplitBar.LEFT ?
3779                     endPoint[0] - this.dragSpecs.startPoint[0] :
3780                     this.dragSpecs.startPoint[0] - endPoint[0]
3781                 );
3782         }else{
3783             newSize = this.dragSpecs.startSize + 
3784                 (this.placement == Roo.SplitBar.TOP ?
3785                     endPoint[1] - this.dragSpecs.startPoint[1] :
3786                     this.dragSpecs.startPoint[1] - endPoint[1]
3787                 );
3788         }
3789         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3790         if(newSize != this.dragSpecs.startSize){
3791             if(this.fireEvent('beforeapply', this, newSize) !== false){
3792                 this.adapter.setElementSize(this, newSize);
3793                 this.fireEvent("moved", this, newSize);
3794                 this.fireEvent("resize", this, newSize);
3795             }
3796         }
3797     },
3798     
3799     /**
3800      * Get the adapter this SplitBar uses
3801      * @return The adapter object
3802      */
3803     getAdapter : function(){
3804         return this.adapter;
3805     },
3806     
3807     /**
3808      * Set the adapter this SplitBar uses
3809      * @param {Object} adapter A SplitBar adapter object
3810      */
3811     setAdapter : function(adapter){
3812         this.adapter = adapter;
3813         this.adapter.init(this);
3814     },
3815     
3816     /**
3817      * Gets the minimum size for the resizing element
3818      * @return {Number} The minimum size
3819      */
3820     getMinimumSize : function(){
3821         return this.minSize;
3822     },
3823     
3824     /**
3825      * Sets the minimum size for the resizing element
3826      * @param {Number} minSize The minimum size
3827      */
3828     setMinimumSize : function(minSize){
3829         this.minSize = minSize;
3830     },
3831     
3832     /**
3833      * Gets the maximum size for the resizing element
3834      * @return {Number} The maximum size
3835      */
3836     getMaximumSize : function(){
3837         return this.maxSize;
3838     },
3839     
3840     /**
3841      * Sets the maximum size for the resizing element
3842      * @param {Number} maxSize The maximum size
3843      */
3844     setMaximumSize : function(maxSize){
3845         this.maxSize = maxSize;
3846     },
3847     
3848     /**
3849      * Sets the initialize size for the resizing element
3850      * @param {Number} size The initial size
3851      */
3852     setCurrentSize : function(size){
3853         var oldAnimate = this.animate;
3854         this.animate = false;
3855         this.adapter.setElementSize(this, size);
3856         this.animate = oldAnimate;
3857     },
3858     
3859     /**
3860      * Destroy this splitbar. 
3861      * @param {Boolean} removeEl True to remove the element
3862      */
3863     destroy : function(removeEl){
3864         if(this.shim){
3865             this.shim.remove();
3866         }
3867         this.dd.unreg();
3868         this.proxy.parentNode.removeChild(this.proxy);
3869         if(removeEl){
3870             this.el.remove();
3871         }
3872     }
3873 });
3874
3875 /**
3876  * @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.
3877  */
3878 Roo.SplitBar.createProxy = function(dir){
3879     var proxy = new Roo.Element(document.createElement("div"));
3880     proxy.unselectable();
3881     var cls = 'x-splitbar-proxy';
3882     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3883     document.body.appendChild(proxy.dom);
3884     return proxy.dom;
3885 };
3886
3887 /** 
3888  * @class Roo.SplitBar.BasicLayoutAdapter
3889  * Default Adapter. It assumes the splitter and resizing element are not positioned
3890  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3891  */
3892 Roo.SplitBar.BasicLayoutAdapter = function(){
3893 };
3894
3895 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3896     // do nothing for now
3897     init : function(s){
3898     
3899     },
3900     /**
3901      * Called before drag operations to get the current size of the resizing element. 
3902      * @param {Roo.SplitBar} s The SplitBar using this adapter
3903      */
3904      getElementSize : function(s){
3905         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3906             return s.resizingEl.getWidth();
3907         }else{
3908             return s.resizingEl.getHeight();
3909         }
3910     },
3911     
3912     /**
3913      * Called after drag operations to set the size of the resizing element.
3914      * @param {Roo.SplitBar} s The SplitBar using this adapter
3915      * @param {Number} newSize The new size to set
3916      * @param {Function} onComplete A function to be invoked when resizing is complete
3917      */
3918     setElementSize : function(s, newSize, onComplete){
3919         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3920             if(!s.animate){
3921                 s.resizingEl.setWidth(newSize);
3922                 if(onComplete){
3923                     onComplete(s, newSize);
3924                 }
3925             }else{
3926                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3927             }
3928         }else{
3929             
3930             if(!s.animate){
3931                 s.resizingEl.setHeight(newSize);
3932                 if(onComplete){
3933                     onComplete(s, newSize);
3934                 }
3935             }else{
3936                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3937             }
3938         }
3939     }
3940 };
3941
3942 /** 
3943  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3944  * @extends Roo.SplitBar.BasicLayoutAdapter
3945  * Adapter that  moves the splitter element to align with the resized sizing element. 
3946  * Used with an absolute positioned SplitBar.
3947  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3948  * document.body, make sure you assign an id to the body element.
3949  */
3950 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3951     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3952     this.container = Roo.get(container);
3953 };
3954
3955 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3956     init : function(s){
3957         this.basic.init(s);
3958     },
3959     
3960     getElementSize : function(s){
3961         return this.basic.getElementSize(s);
3962     },
3963     
3964     setElementSize : function(s, newSize, onComplete){
3965         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3966     },
3967     
3968     moveSplitter : function(s){
3969         var yes = Roo.SplitBar;
3970         switch(s.placement){
3971             case yes.LEFT:
3972                 s.el.setX(s.resizingEl.getRight());
3973                 break;
3974             case yes.RIGHT:
3975                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3976                 break;
3977             case yes.TOP:
3978                 s.el.setY(s.resizingEl.getBottom());
3979                 break;
3980             case yes.BOTTOM:
3981                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3982                 break;
3983         }
3984     }
3985 };
3986
3987 /**
3988  * Orientation constant - Create a vertical SplitBar
3989  * @static
3990  * @type Number
3991  */
3992 Roo.SplitBar.VERTICAL = 1;
3993
3994 /**
3995  * Orientation constant - Create a horizontal SplitBar
3996  * @static
3997  * @type Number
3998  */
3999 Roo.SplitBar.HORIZONTAL = 2;
4000
4001 /**
4002  * Placement constant - The resizing element is to the left of the splitter element
4003  * @static
4004  * @type Number
4005  */
4006 Roo.SplitBar.LEFT = 1;
4007
4008 /**
4009  * Placement constant - The resizing element is to the right of the splitter element
4010  * @static
4011  * @type Number
4012  */
4013 Roo.SplitBar.RIGHT = 2;
4014
4015 /**
4016  * Placement constant - The resizing element is positioned above the splitter element
4017  * @static
4018  * @type Number
4019  */
4020 Roo.SplitBar.TOP = 3;
4021
4022 /**
4023  * Placement constant - The resizing element is positioned under splitter element
4024  * @static
4025  * @type Number
4026  */
4027 Roo.SplitBar.BOTTOM = 4;
4028 /*
4029  * Based on:
4030  * Ext JS Library 1.1.1
4031  * Copyright(c) 2006-2007, Ext JS, LLC.
4032  *
4033  * Originally Released Under LGPL - original licence link has changed is not relivant.
4034  *
4035  * Fork - LGPL
4036  * <script type="text/javascript">
4037  */
4038
4039 /**
4040  * @class Roo.View
4041  * @extends Roo.util.Observable
4042  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4043  * This class also supports single and multi selection modes. <br>
4044  * Create a data model bound view:
4045  <pre><code>
4046  var store = new Roo.data.Store(...);
4047
4048  var view = new Roo.View({
4049     el : "my-element",
4050     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4051  
4052     singleSelect: true,
4053     selectedClass: "ydataview-selected",
4054     store: store
4055  });
4056
4057  // listen for node click?
4058  view.on("click", function(vw, index, node, e){
4059  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4060  });
4061
4062  // load XML data
4063  dataModel.load("foobar.xml");
4064  </code></pre>
4065  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4066  * <br><br>
4067  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4068  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4069  * 
4070  * Note: old style constructor is still suported (container, template, config)
4071  * 
4072  * @constructor
4073  * Create a new View
4074  * @param {Object} config The config object
4075  * 
4076  */
4077 Roo.View = function(config, depreciated_tpl, depreciated_config){
4078     
4079     this.parent = false;
4080     
4081     if (typeof(depreciated_tpl) == 'undefined') {
4082         // new way.. - universal constructor.
4083         Roo.apply(this, config);
4084         this.el  = Roo.get(this.el);
4085     } else {
4086         // old format..
4087         this.el  = Roo.get(config);
4088         this.tpl = depreciated_tpl;
4089         Roo.apply(this, depreciated_config);
4090     }
4091     this.wrapEl  = this.el.wrap().wrap();
4092     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4093     
4094     
4095     if(typeof(this.tpl) == "string"){
4096         this.tpl = new Roo.Template(this.tpl);
4097     } else {
4098         // support xtype ctors..
4099         this.tpl = new Roo.factory(this.tpl, Roo);
4100     }
4101     
4102     
4103     this.tpl.compile();
4104     
4105     /** @private */
4106     this.addEvents({
4107         /**
4108          * @event beforeclick
4109          * Fires before a click is processed. Returns false to cancel the default action.
4110          * @param {Roo.View} this
4111          * @param {Number} index The index of the target node
4112          * @param {HTMLElement} node The target node
4113          * @param {Roo.EventObject} e The raw event object
4114          */
4115             "beforeclick" : true,
4116         /**
4117          * @event click
4118          * Fires when a template node is clicked.
4119          * @param {Roo.View} this
4120          * @param {Number} index The index of the target node
4121          * @param {HTMLElement} node The target node
4122          * @param {Roo.EventObject} e The raw event object
4123          */
4124             "click" : true,
4125         /**
4126          * @event dblclick
4127          * Fires when a template node is double clicked.
4128          * @param {Roo.View} this
4129          * @param {Number} index The index of the target node
4130          * @param {HTMLElement} node The target node
4131          * @param {Roo.EventObject} e The raw event object
4132          */
4133             "dblclick" : true,
4134         /**
4135          * @event contextmenu
4136          * Fires when a template node is right clicked.
4137          * @param {Roo.View} this
4138          * @param {Number} index The index of the target node
4139          * @param {HTMLElement} node The target node
4140          * @param {Roo.EventObject} e The raw event object
4141          */
4142             "contextmenu" : true,
4143         /**
4144          * @event selectionchange
4145          * Fires when the selected nodes change.
4146          * @param {Roo.View} this
4147          * @param {Array} selections Array of the selected nodes
4148          */
4149             "selectionchange" : true,
4150     
4151         /**
4152          * @event beforeselect
4153          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4154          * @param {Roo.View} this
4155          * @param {HTMLElement} node The node to be selected
4156          * @param {Array} selections Array of currently selected nodes
4157          */
4158             "beforeselect" : true,
4159         /**
4160          * @event preparedata
4161          * Fires on every row to render, to allow you to change the data.
4162          * @param {Roo.View} this
4163          * @param {Object} data to be rendered (change this)
4164          */
4165           "preparedata" : true
4166           
4167           
4168         });
4169
4170
4171
4172     this.el.on({
4173         "click": this.onClick,
4174         "dblclick": this.onDblClick,
4175         "contextmenu": this.onContextMenu,
4176         scope:this
4177     });
4178
4179     this.selections = [];
4180     this.nodes = [];
4181     this.cmp = new Roo.CompositeElementLite([]);
4182     if(this.store){
4183         this.store = Roo.factory(this.store, Roo.data);
4184         this.setStore(this.store, true);
4185     }
4186     
4187     if ( this.footer && this.footer.xtype) {
4188            
4189          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4190         
4191         this.footer.dataSource = this.store;
4192         this.footer.container = fctr;
4193         this.footer = Roo.factory(this.footer, Roo);
4194         fctr.insertFirst(this.el);
4195         
4196         // this is a bit insane - as the paging toolbar seems to detach the el..
4197 //        dom.parentNode.parentNode.parentNode
4198          // they get detached?
4199     }
4200     
4201     
4202     Roo.View.superclass.constructor.call(this);
4203     
4204     
4205 };
4206
4207 Roo.extend(Roo.View, Roo.util.Observable, {
4208     
4209      /**
4210      * @cfg {Roo.data.Store} store Data store to load data from.
4211      */
4212     store : false,
4213     
4214     /**
4215      * @cfg {String|Roo.Element} el The container element.
4216      */
4217     el : '',
4218     
4219     /**
4220      * @cfg {String|Roo.Template} tpl The template used by this View 
4221      */
4222     tpl : false,
4223     /**
4224      * @cfg {String} dataName the named area of the template to use as the data area
4225      *                          Works with domtemplates roo-name="name"
4226      */
4227     dataName: false,
4228     /**
4229      * @cfg {String} selectedClass The css class to add to selected nodes
4230      */
4231     selectedClass : "x-view-selected",
4232      /**
4233      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4234      */
4235     emptyText : "",
4236     
4237     /**
4238      * @cfg {String} text to display on mask (default Loading)
4239      */
4240     mask : false,
4241     /**
4242      * @cfg {Boolean} multiSelect Allow multiple selection
4243      */
4244     multiSelect : false,
4245     /**
4246      * @cfg {Boolean} singleSelect Allow single selection
4247      */
4248     singleSelect:  false,
4249     
4250     /**
4251      * @cfg {Boolean} toggleSelect - selecting 
4252      */
4253     toggleSelect : false,
4254     
4255     /**
4256      * @cfg {Boolean} tickable - selecting 
4257      */
4258     tickable : false,
4259     
4260     /**
4261      * Returns the element this view is bound to.
4262      * @return {Roo.Element}
4263      */
4264     getEl : function(){
4265         return this.wrapEl;
4266     },
4267     
4268     
4269
4270     /**
4271      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4272      */
4273     refresh : function(){
4274         //Roo.log('refresh');
4275         var t = this.tpl;
4276         
4277         // if we are using something like 'domtemplate', then
4278         // the what gets used is:
4279         // t.applySubtemplate(NAME, data, wrapping data..)
4280         // the outer template then get' applied with
4281         //     the store 'extra data'
4282         // and the body get's added to the
4283         //      roo-name="data" node?
4284         //      <span class='roo-tpl-{name}'></span> ?????
4285         
4286         
4287         
4288         this.clearSelections();
4289         this.el.update("");
4290         var html = [];
4291         var records = this.store.getRange();
4292         if(records.length < 1) {
4293             
4294             // is this valid??  = should it render a template??
4295             
4296             this.el.update(this.emptyText);
4297             return;
4298         }
4299         var el = this.el;
4300         if (this.dataName) {
4301             this.el.update(t.apply(this.store.meta)); //????
4302             el = this.el.child('.roo-tpl-' + this.dataName);
4303         }
4304         
4305         for(var i = 0, len = records.length; i < len; i++){
4306             var data = this.prepareData(records[i].data, i, records[i]);
4307             this.fireEvent("preparedata", this, data, i, records[i]);
4308             
4309             var d = Roo.apply({}, data);
4310             
4311             if(this.tickable){
4312                 Roo.apply(d, {'roo-id' : Roo.id()});
4313                 
4314                 var _this = this;
4315             
4316                 Roo.each(this.parent.item, function(item){
4317                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4318                         return;
4319                     }
4320                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4321                 });
4322             }
4323             
4324             html[html.length] = Roo.util.Format.trim(
4325                 this.dataName ?
4326                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4327                     t.apply(d)
4328             );
4329         }
4330         
4331         
4332         
4333         el.update(html.join(""));
4334         this.nodes = el.dom.childNodes;
4335         this.updateIndexes(0);
4336     },
4337     
4338
4339     /**
4340      * Function to override to reformat the data that is sent to
4341      * the template for each node.
4342      * DEPRICATED - use the preparedata event handler.
4343      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4344      * a JSON object for an UpdateManager bound view).
4345      */
4346     prepareData : function(data, index, record)
4347     {
4348         this.fireEvent("preparedata", this, data, index, record);
4349         return data;
4350     },
4351
4352     onUpdate : function(ds, record){
4353         // Roo.log('on update');   
4354         this.clearSelections();
4355         var index = this.store.indexOf(record);
4356         var n = this.nodes[index];
4357         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4358         n.parentNode.removeChild(n);
4359         this.updateIndexes(index, index);
4360     },
4361
4362     
4363     
4364 // --------- FIXME     
4365     onAdd : function(ds, records, index)
4366     {
4367         //Roo.log(['on Add', ds, records, index] );        
4368         this.clearSelections();
4369         if(this.nodes.length == 0){
4370             this.refresh();
4371             return;
4372         }
4373         var n = this.nodes[index];
4374         for(var i = 0, len = records.length; i < len; i++){
4375             var d = this.prepareData(records[i].data, i, records[i]);
4376             if(n){
4377                 this.tpl.insertBefore(n, d);
4378             }else{
4379                 
4380                 this.tpl.append(this.el, d);
4381             }
4382         }
4383         this.updateIndexes(index);
4384     },
4385
4386     onRemove : function(ds, record, index){
4387        // Roo.log('onRemove');
4388         this.clearSelections();
4389         var el = this.dataName  ?
4390             this.el.child('.roo-tpl-' + this.dataName) :
4391             this.el; 
4392         
4393         el.dom.removeChild(this.nodes[index]);
4394         this.updateIndexes(index);
4395     },
4396
4397     /**
4398      * Refresh an individual node.
4399      * @param {Number} index
4400      */
4401     refreshNode : function(index){
4402         this.onUpdate(this.store, this.store.getAt(index));
4403     },
4404
4405     updateIndexes : function(startIndex, endIndex){
4406         var ns = this.nodes;
4407         startIndex = startIndex || 0;
4408         endIndex = endIndex || ns.length - 1;
4409         for(var i = startIndex; i <= endIndex; i++){
4410             ns[i].nodeIndex = i;
4411         }
4412     },
4413
4414     /**
4415      * Changes the data store this view uses and refresh the view.
4416      * @param {Store} store
4417      */
4418     setStore : function(store, initial){
4419         if(!initial && this.store){
4420             this.store.un("datachanged", this.refresh);
4421             this.store.un("add", this.onAdd);
4422             this.store.un("remove", this.onRemove);
4423             this.store.un("update", this.onUpdate);
4424             this.store.un("clear", this.refresh);
4425             this.store.un("beforeload", this.onBeforeLoad);
4426             this.store.un("load", this.onLoad);
4427             this.store.un("loadexception", this.onLoad);
4428         }
4429         if(store){
4430           
4431             store.on("datachanged", this.refresh, this);
4432             store.on("add", this.onAdd, this);
4433             store.on("remove", this.onRemove, this);
4434             store.on("update", this.onUpdate, this);
4435             store.on("clear", this.refresh, this);
4436             store.on("beforeload", this.onBeforeLoad, this);
4437             store.on("load", this.onLoad, this);
4438             store.on("loadexception", this.onLoad, this);
4439         }
4440         
4441         if(store){
4442             this.refresh();
4443         }
4444     },
4445     /**
4446      * onbeforeLoad - masks the loading area.
4447      *
4448      */
4449     onBeforeLoad : function(store,opts)
4450     {
4451          //Roo.log('onBeforeLoad');   
4452         if (!opts.add) {
4453             this.el.update("");
4454         }
4455         this.el.mask(this.mask ? this.mask : "Loading" ); 
4456     },
4457     onLoad : function ()
4458     {
4459         this.el.unmask();
4460     },
4461     
4462
4463     /**
4464      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4465      * @param {HTMLElement} node
4466      * @return {HTMLElement} The template node
4467      */
4468     findItemFromChild : function(node){
4469         var el = this.dataName  ?
4470             this.el.child('.roo-tpl-' + this.dataName,true) :
4471             this.el.dom; 
4472         
4473         if(!node || node.parentNode == el){
4474                     return node;
4475             }
4476             var p = node.parentNode;
4477             while(p && p != el){
4478             if(p.parentNode == el){
4479                 return p;
4480             }
4481             p = p.parentNode;
4482         }
4483             return null;
4484     },
4485
4486     /** @ignore */
4487     onClick : function(e){
4488         var item = this.findItemFromChild(e.getTarget());
4489         if(item){
4490             var index = this.indexOf(item);
4491             if(this.onItemClick(item, index, e) !== false){
4492                 this.fireEvent("click", this, index, item, e);
4493             }
4494         }else{
4495             this.clearSelections();
4496         }
4497     },
4498
4499     /** @ignore */
4500     onContextMenu : function(e){
4501         var item = this.findItemFromChild(e.getTarget());
4502         if(item){
4503             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4504         }
4505     },
4506
4507     /** @ignore */
4508     onDblClick : function(e){
4509         var item = this.findItemFromChild(e.getTarget());
4510         if(item){
4511             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4512         }
4513     },
4514
4515     onItemClick : function(item, index, e)
4516     {
4517         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4518             return false;
4519         }
4520         if (this.toggleSelect) {
4521             var m = this.isSelected(item) ? 'unselect' : 'select';
4522             //Roo.log(m);
4523             var _t = this;
4524             _t[m](item, true, false);
4525             return true;
4526         }
4527         if(this.multiSelect || this.singleSelect){
4528             if(this.multiSelect && e.shiftKey && this.lastSelection){
4529                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4530             }else{
4531                 this.select(item, this.multiSelect && e.ctrlKey);
4532                 this.lastSelection = item;
4533             }
4534             
4535             if(!this.tickable){
4536                 e.preventDefault();
4537             }
4538             
4539         }
4540         return true;
4541     },
4542
4543     /**
4544      * Get the number of selected nodes.
4545      * @return {Number}
4546      */
4547     getSelectionCount : function(){
4548         return this.selections.length;
4549     },
4550
4551     /**
4552      * Get the currently selected nodes.
4553      * @return {Array} An array of HTMLElements
4554      */
4555     getSelectedNodes : function(){
4556         return this.selections;
4557     },
4558
4559     /**
4560      * Get the indexes of the selected nodes.
4561      * @return {Array}
4562      */
4563     getSelectedIndexes : function(){
4564         var indexes = [], s = this.selections;
4565         for(var i = 0, len = s.length; i < len; i++){
4566             indexes.push(s[i].nodeIndex);
4567         }
4568         return indexes;
4569     },
4570
4571     /**
4572      * Clear all selections
4573      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4574      */
4575     clearSelections : function(suppressEvent){
4576         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4577             this.cmp.elements = this.selections;
4578             this.cmp.removeClass(this.selectedClass);
4579             this.selections = [];
4580             if(!suppressEvent){
4581                 this.fireEvent("selectionchange", this, this.selections);
4582             }
4583         }
4584     },
4585
4586     /**
4587      * Returns true if the passed node is selected
4588      * @param {HTMLElement/Number} node The node or node index
4589      * @return {Boolean}
4590      */
4591     isSelected : function(node){
4592         var s = this.selections;
4593         if(s.length < 1){
4594             return false;
4595         }
4596         node = this.getNode(node);
4597         return s.indexOf(node) !== -1;
4598     },
4599
4600     /**
4601      * Selects nodes.
4602      * @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
4603      * @param {Boolean} keepExisting (optional) true to keep existing selections
4604      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4605      */
4606     select : function(nodeInfo, keepExisting, suppressEvent){
4607         if(nodeInfo instanceof Array){
4608             if(!keepExisting){
4609                 this.clearSelections(true);
4610             }
4611             for(var i = 0, len = nodeInfo.length; i < len; i++){
4612                 this.select(nodeInfo[i], true, true);
4613             }
4614             return;
4615         } 
4616         var node = this.getNode(nodeInfo);
4617         if(!node || this.isSelected(node)){
4618             return; // already selected.
4619         }
4620         if(!keepExisting){
4621             this.clearSelections(true);
4622         }
4623         
4624         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4625             Roo.fly(node).addClass(this.selectedClass);
4626             this.selections.push(node);
4627             if(!suppressEvent){
4628                 this.fireEvent("selectionchange", this, this.selections);
4629             }
4630         }
4631         
4632         
4633     },
4634       /**
4635      * Unselects nodes.
4636      * @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
4637      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4638      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4639      */
4640     unselect : function(nodeInfo, keepExisting, suppressEvent)
4641     {
4642         if(nodeInfo instanceof Array){
4643             Roo.each(this.selections, function(s) {
4644                 this.unselect(s, nodeInfo);
4645             }, this);
4646             return;
4647         }
4648         var node = this.getNode(nodeInfo);
4649         if(!node || !this.isSelected(node)){
4650             //Roo.log("not selected");
4651             return; // not selected.
4652         }
4653         // fireevent???
4654         var ns = [];
4655         Roo.each(this.selections, function(s) {
4656             if (s == node ) {
4657                 Roo.fly(node).removeClass(this.selectedClass);
4658
4659                 return;
4660             }
4661             ns.push(s);
4662         },this);
4663         
4664         this.selections= ns;
4665         this.fireEvent("selectionchange", this, this.selections);
4666     },
4667
4668     /**
4669      * Gets a template node.
4670      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4671      * @return {HTMLElement} The node or null if it wasn't found
4672      */
4673     getNode : function(nodeInfo){
4674         if(typeof nodeInfo == "string"){
4675             return document.getElementById(nodeInfo);
4676         }else if(typeof nodeInfo == "number"){
4677             return this.nodes[nodeInfo];
4678         }
4679         return nodeInfo;
4680     },
4681
4682     /**
4683      * Gets a range template nodes.
4684      * @param {Number} startIndex
4685      * @param {Number} endIndex
4686      * @return {Array} An array of nodes
4687      */
4688     getNodes : function(start, end){
4689         var ns = this.nodes;
4690         start = start || 0;
4691         end = typeof end == "undefined" ? ns.length - 1 : end;
4692         var nodes = [];
4693         if(start <= end){
4694             for(var i = start; i <= end; i++){
4695                 nodes.push(ns[i]);
4696             }
4697         } else{
4698             for(var i = start; i >= end; i--){
4699                 nodes.push(ns[i]);
4700             }
4701         }
4702         return nodes;
4703     },
4704
4705     /**
4706      * Finds the index of the passed node
4707      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4708      * @return {Number} The index of the node or -1
4709      */
4710     indexOf : function(node){
4711         node = this.getNode(node);
4712         if(typeof node.nodeIndex == "number"){
4713             return node.nodeIndex;
4714         }
4715         var ns = this.nodes;
4716         for(var i = 0, len = ns.length; i < len; i++){
4717             if(ns[i] == node){
4718                 return i;
4719             }
4720         }
4721         return -1;
4722     }
4723 });
4724 /*
4725  * Based on:
4726  * Ext JS Library 1.1.1
4727  * Copyright(c) 2006-2007, Ext JS, LLC.
4728  *
4729  * Originally Released Under LGPL - original licence link has changed is not relivant.
4730  *
4731  * Fork - LGPL
4732  * <script type="text/javascript">
4733  */
4734
4735 /**
4736  * @class Roo.JsonView
4737  * @extends Roo.View
4738  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4739 <pre><code>
4740 var view = new Roo.JsonView({
4741     container: "my-element",
4742     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4743     multiSelect: true, 
4744     jsonRoot: "data" 
4745 });
4746
4747 // listen for node click?
4748 view.on("click", function(vw, index, node, e){
4749     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4750 });
4751
4752 // direct load of JSON data
4753 view.load("foobar.php");
4754
4755 // Example from my blog list
4756 var tpl = new Roo.Template(
4757     '&lt;div class="entry"&gt;' +
4758     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4759     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4760     "&lt;/div&gt;&lt;hr /&gt;"
4761 );
4762
4763 var moreView = new Roo.JsonView({
4764     container :  "entry-list", 
4765     template : tpl,
4766     jsonRoot: "posts"
4767 });
4768 moreView.on("beforerender", this.sortEntries, this);
4769 moreView.load({
4770     url: "/blog/get-posts.php",
4771     params: "allposts=true",
4772     text: "Loading Blog Entries..."
4773 });
4774 </code></pre>
4775
4776 * Note: old code is supported with arguments : (container, template, config)
4777
4778
4779  * @constructor
4780  * Create a new JsonView
4781  * 
4782  * @param {Object} config The config object
4783  * 
4784  */
4785 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4786     
4787     
4788     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4789
4790     var um = this.el.getUpdateManager();
4791     um.setRenderer(this);
4792     um.on("update", this.onLoad, this);
4793     um.on("failure", this.onLoadException, this);
4794
4795     /**
4796      * @event beforerender
4797      * Fires before rendering of the downloaded JSON data.
4798      * @param {Roo.JsonView} this
4799      * @param {Object} data The JSON data loaded
4800      */
4801     /**
4802      * @event load
4803      * Fires when data is loaded.
4804      * @param {Roo.JsonView} this
4805      * @param {Object} data The JSON data loaded
4806      * @param {Object} response The raw Connect response object
4807      */
4808     /**
4809      * @event loadexception
4810      * Fires when loading fails.
4811      * @param {Roo.JsonView} this
4812      * @param {Object} response The raw Connect response object
4813      */
4814     this.addEvents({
4815         'beforerender' : true,
4816         'load' : true,
4817         'loadexception' : true
4818     });
4819 };
4820 Roo.extend(Roo.JsonView, Roo.View, {
4821     /**
4822      * @type {String} The root property in the loaded JSON object that contains the data
4823      */
4824     jsonRoot : "",
4825
4826     /**
4827      * Refreshes the view.
4828      */
4829     refresh : function(){
4830         this.clearSelections();
4831         this.el.update("");
4832         var html = [];
4833         var o = this.jsonData;
4834         if(o && o.length > 0){
4835             for(var i = 0, len = o.length; i < len; i++){
4836                 var data = this.prepareData(o[i], i, o);
4837                 html[html.length] = this.tpl.apply(data);
4838             }
4839         }else{
4840             html.push(this.emptyText);
4841         }
4842         this.el.update(html.join(""));
4843         this.nodes = this.el.dom.childNodes;
4844         this.updateIndexes(0);
4845     },
4846
4847     /**
4848      * 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.
4849      * @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:
4850      <pre><code>
4851      view.load({
4852          url: "your-url.php",
4853          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4854          callback: yourFunction,
4855          scope: yourObject, //(optional scope)
4856          discardUrl: false,
4857          nocache: false,
4858          text: "Loading...",
4859          timeout: 30,
4860          scripts: false
4861      });
4862      </code></pre>
4863      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4864      * 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.
4865      * @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}
4866      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4867      * @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.
4868      */
4869     load : function(){
4870         var um = this.el.getUpdateManager();
4871         um.update.apply(um, arguments);
4872     },
4873
4874     // note - render is a standard framework call...
4875     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4876     render : function(el, response){
4877         
4878         this.clearSelections();
4879         this.el.update("");
4880         var o;
4881         try{
4882             if (response != '') {
4883                 o = Roo.util.JSON.decode(response.responseText);
4884                 if(this.jsonRoot){
4885                     
4886                     o = o[this.jsonRoot];
4887                 }
4888             }
4889         } catch(e){
4890         }
4891         /**
4892          * The current JSON data or null
4893          */
4894         this.jsonData = o;
4895         this.beforeRender();
4896         this.refresh();
4897     },
4898
4899 /**
4900  * Get the number of records in the current JSON dataset
4901  * @return {Number}
4902  */
4903     getCount : function(){
4904         return this.jsonData ? this.jsonData.length : 0;
4905     },
4906
4907 /**
4908  * Returns the JSON object for the specified node(s)
4909  * @param {HTMLElement/Array} node The node or an array of nodes
4910  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4911  * you get the JSON object for the node
4912  */
4913     getNodeData : function(node){
4914         if(node instanceof Array){
4915             var data = [];
4916             for(var i = 0, len = node.length; i < len; i++){
4917                 data.push(this.getNodeData(node[i]));
4918             }
4919             return data;
4920         }
4921         return this.jsonData[this.indexOf(node)] || null;
4922     },
4923
4924     beforeRender : function(){
4925         this.snapshot = this.jsonData;
4926         if(this.sortInfo){
4927             this.sort.apply(this, this.sortInfo);
4928         }
4929         this.fireEvent("beforerender", this, this.jsonData);
4930     },
4931
4932     onLoad : function(el, o){
4933         this.fireEvent("load", this, this.jsonData, o);
4934     },
4935
4936     onLoadException : function(el, o){
4937         this.fireEvent("loadexception", this, o);
4938     },
4939
4940 /**
4941  * Filter the data by a specific property.
4942  * @param {String} property A property on your JSON objects
4943  * @param {String/RegExp} value Either string that the property values
4944  * should start with, or a RegExp to test against the property
4945  */
4946     filter : function(property, value){
4947         if(this.jsonData){
4948             var data = [];
4949             var ss = this.snapshot;
4950             if(typeof value == "string"){
4951                 var vlen = value.length;
4952                 if(vlen == 0){
4953                     this.clearFilter();
4954                     return;
4955                 }
4956                 value = value.toLowerCase();
4957                 for(var i = 0, len = ss.length; i < len; i++){
4958                     var o = ss[i];
4959                     if(o[property].substr(0, vlen).toLowerCase() == value){
4960                         data.push(o);
4961                     }
4962                 }
4963             } else if(value.exec){ // regex?
4964                 for(var i = 0, len = ss.length; i < len; i++){
4965                     var o = ss[i];
4966                     if(value.test(o[property])){
4967                         data.push(o);
4968                     }
4969                 }
4970             } else{
4971                 return;
4972             }
4973             this.jsonData = data;
4974             this.refresh();
4975         }
4976     },
4977
4978 /**
4979  * Filter by a function. The passed function will be called with each
4980  * object in the current dataset. If the function returns true the value is kept,
4981  * otherwise it is filtered.
4982  * @param {Function} fn
4983  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4984  */
4985     filterBy : function(fn, scope){
4986         if(this.jsonData){
4987             var data = [];
4988             var ss = this.snapshot;
4989             for(var i = 0, len = ss.length; i < len; i++){
4990                 var o = ss[i];
4991                 if(fn.call(scope || this, o)){
4992                     data.push(o);
4993                 }
4994             }
4995             this.jsonData = data;
4996             this.refresh();
4997         }
4998     },
4999
5000 /**
5001  * Clears the current filter.
5002  */
5003     clearFilter : function(){
5004         if(this.snapshot && this.jsonData != this.snapshot){
5005             this.jsonData = this.snapshot;
5006             this.refresh();
5007         }
5008     },
5009
5010
5011 /**
5012  * Sorts the data for this view and refreshes it.
5013  * @param {String} property A property on your JSON objects to sort on
5014  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5015  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5016  */
5017     sort : function(property, dir, sortType){
5018         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5019         if(this.jsonData){
5020             var p = property;
5021             var dsc = dir && dir.toLowerCase() == "desc";
5022             var f = function(o1, o2){
5023                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5024                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5025                 ;
5026                 if(v1 < v2){
5027                     return dsc ? +1 : -1;
5028                 } else if(v1 > v2){
5029                     return dsc ? -1 : +1;
5030                 } else{
5031                     return 0;
5032                 }
5033             };
5034             this.jsonData.sort(f);
5035             this.refresh();
5036             if(this.jsonData != this.snapshot){
5037                 this.snapshot.sort(f);
5038             }
5039         }
5040     }
5041 });/*
5042  * Based on:
5043  * Ext JS Library 1.1.1
5044  * Copyright(c) 2006-2007, Ext JS, LLC.
5045  *
5046  * Originally Released Under LGPL - original licence link has changed is not relivant.
5047  *
5048  * Fork - LGPL
5049  * <script type="text/javascript">
5050  */
5051  
5052
5053 /**
5054  * @class Roo.ColorPalette
5055  * @extends Roo.Component
5056  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5057  * Here's an example of typical usage:
5058  * <pre><code>
5059 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5060 cp.render('my-div');
5061
5062 cp.on('select', function(palette, selColor){
5063     // do something with selColor
5064 });
5065 </code></pre>
5066  * @constructor
5067  * Create a new ColorPalette
5068  * @param {Object} config The config object
5069  */
5070 Roo.ColorPalette = function(config){
5071     Roo.ColorPalette.superclass.constructor.call(this, config);
5072     this.addEvents({
5073         /**
5074              * @event select
5075              * Fires when a color is selected
5076              * @param {ColorPalette} this
5077              * @param {String} color The 6-digit color hex code (without the # symbol)
5078              */
5079         select: true
5080     });
5081
5082     if(this.handler){
5083         this.on("select", this.handler, this.scope, true);
5084     }
5085 };
5086 Roo.extend(Roo.ColorPalette, Roo.Component, {
5087     /**
5088      * @cfg {String} itemCls
5089      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5090      */
5091     itemCls : "x-color-palette",
5092     /**
5093      * @cfg {String} value
5094      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5095      * the hex codes are case-sensitive.
5096      */
5097     value : null,
5098     clickEvent:'click',
5099     // private
5100     ctype: "Roo.ColorPalette",
5101
5102     /**
5103      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5104      */
5105     allowReselect : false,
5106
5107     /**
5108      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5109      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5110      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5111      * of colors with the width setting until the box is symmetrical.</p>
5112      * <p>You can override individual colors if needed:</p>
5113      * <pre><code>
5114 var cp = new Roo.ColorPalette();
5115 cp.colors[0] = "FF0000";  // change the first box to red
5116 </code></pre>
5117
5118 Or you can provide a custom array of your own for complete control:
5119 <pre><code>
5120 var cp = new Roo.ColorPalette();
5121 cp.colors = ["000000", "993300", "333300"];
5122 </code></pre>
5123      * @type Array
5124      */
5125     colors : [
5126         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5127         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5128         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5129         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5130         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5131     ],
5132
5133     // private
5134     onRender : function(container, position){
5135         var t = new Roo.MasterTemplate(
5136             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5137         );
5138         var c = this.colors;
5139         for(var i = 0, len = c.length; i < len; i++){
5140             t.add([c[i]]);
5141         }
5142         var el = document.createElement("div");
5143         el.className = this.itemCls;
5144         t.overwrite(el);
5145         container.dom.insertBefore(el, position);
5146         this.el = Roo.get(el);
5147         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5148         if(this.clickEvent != 'click'){
5149             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5150         }
5151     },
5152
5153     // private
5154     afterRender : function(){
5155         Roo.ColorPalette.superclass.afterRender.call(this);
5156         if(this.value){
5157             var s = this.value;
5158             this.value = null;
5159             this.select(s);
5160         }
5161     },
5162
5163     // private
5164     handleClick : function(e, t){
5165         e.preventDefault();
5166         if(!this.disabled){
5167             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5168             this.select(c.toUpperCase());
5169         }
5170     },
5171
5172     /**
5173      * Selects the specified color in the palette (fires the select event)
5174      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5175      */
5176     select : function(color){
5177         color = color.replace("#", "");
5178         if(color != this.value || this.allowReselect){
5179             var el = this.el;
5180             if(this.value){
5181                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5182             }
5183             el.child("a.color-"+color).addClass("x-color-palette-sel");
5184             this.value = color;
5185             this.fireEvent("select", this, color);
5186         }
5187     }
5188 });/*
5189  * Based on:
5190  * Ext JS Library 1.1.1
5191  * Copyright(c) 2006-2007, Ext JS, LLC.
5192  *
5193  * Originally Released Under LGPL - original licence link has changed is not relivant.
5194  *
5195  * Fork - LGPL
5196  * <script type="text/javascript">
5197  */
5198  
5199 /**
5200  * @class Roo.DatePicker
5201  * @extends Roo.Component
5202  * Simple date picker class.
5203  * @constructor
5204  * Create a new DatePicker
5205  * @param {Object} config The config object
5206  */
5207 Roo.DatePicker = function(config){
5208     Roo.DatePicker.superclass.constructor.call(this, config);
5209
5210     this.value = config && config.value ?
5211                  config.value.clearTime() : new Date().clearTime();
5212
5213     this.addEvents({
5214         /**
5215              * @event select
5216              * Fires when a date is selected
5217              * @param {DatePicker} this
5218              * @param {Date} date The selected date
5219              */
5220         'select': true,
5221         /**
5222              * @event monthchange
5223              * Fires when the displayed month changes 
5224              * @param {DatePicker} this
5225              * @param {Date} date The selected month
5226              */
5227         'monthchange': true
5228     });
5229
5230     if(this.handler){
5231         this.on("select", this.handler,  this.scope || this);
5232     }
5233     // build the disabledDatesRE
5234     if(!this.disabledDatesRE && this.disabledDates){
5235         var dd = this.disabledDates;
5236         var re = "(?:";
5237         for(var i = 0; i < dd.length; i++){
5238             re += dd[i];
5239             if(i != dd.length-1) {
5240                 re += "|";
5241             }
5242         }
5243         this.disabledDatesRE = new RegExp(re + ")");
5244     }
5245 };
5246
5247 Roo.extend(Roo.DatePicker, Roo.Component, {
5248     /**
5249      * @cfg {String} todayText
5250      * The text to display on the button that selects the current date (defaults to "Today")
5251      */
5252     todayText : "Today",
5253     /**
5254      * @cfg {String} okText
5255      * The text to display on the ok button
5256      */
5257     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5258     /**
5259      * @cfg {String} cancelText
5260      * The text to display on the cancel button
5261      */
5262     cancelText : "Cancel",
5263     /**
5264      * @cfg {String} todayTip
5265      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5266      */
5267     todayTip : "{0} (Spacebar)",
5268     /**
5269      * @cfg {Date} minDate
5270      * Minimum allowable date (JavaScript date object, defaults to null)
5271      */
5272     minDate : null,
5273     /**
5274      * @cfg {Date} maxDate
5275      * Maximum allowable date (JavaScript date object, defaults to null)
5276      */
5277     maxDate : null,
5278     /**
5279      * @cfg {String} minText
5280      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5281      */
5282     minText : "This date is before the minimum date",
5283     /**
5284      * @cfg {String} maxText
5285      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5286      */
5287     maxText : "This date is after the maximum date",
5288     /**
5289      * @cfg {String} format
5290      * The default date format string which can be overriden for localization support.  The format must be
5291      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5292      */
5293     format : "m/d/y",
5294     /**
5295      * @cfg {Array} disabledDays
5296      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5297      */
5298     disabledDays : null,
5299     /**
5300      * @cfg {String} disabledDaysText
5301      * The tooltip to display when the date falls on a disabled day (defaults to "")
5302      */
5303     disabledDaysText : "",
5304     /**
5305      * @cfg {RegExp} disabledDatesRE
5306      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5307      */
5308     disabledDatesRE : null,
5309     /**
5310      * @cfg {String} disabledDatesText
5311      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5312      */
5313     disabledDatesText : "",
5314     /**
5315      * @cfg {Boolean} constrainToViewport
5316      * True to constrain the date picker to the viewport (defaults to true)
5317      */
5318     constrainToViewport : true,
5319     /**
5320      * @cfg {Array} monthNames
5321      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5322      */
5323     monthNames : Date.monthNames,
5324     /**
5325      * @cfg {Array} dayNames
5326      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5327      */
5328     dayNames : Date.dayNames,
5329     /**
5330      * @cfg {String} nextText
5331      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5332      */
5333     nextText: 'Next Month (Control+Right)',
5334     /**
5335      * @cfg {String} prevText
5336      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5337      */
5338     prevText: 'Previous Month (Control+Left)',
5339     /**
5340      * @cfg {String} monthYearText
5341      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5342      */
5343     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5344     /**
5345      * @cfg {Number} startDay
5346      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5347      */
5348     startDay : 0,
5349     /**
5350      * @cfg {Bool} showClear
5351      * Show a clear button (usefull for date form elements that can be blank.)
5352      */
5353     
5354     showClear: false,
5355     
5356     /**
5357      * Sets the value of the date field
5358      * @param {Date} value The date to set
5359      */
5360     setValue : function(value){
5361         var old = this.value;
5362         
5363         if (typeof(value) == 'string') {
5364          
5365             value = Date.parseDate(value, this.format);
5366         }
5367         if (!value) {
5368             value = new Date();
5369         }
5370         
5371         this.value = value.clearTime(true);
5372         if(this.el){
5373             this.update(this.value);
5374         }
5375     },
5376
5377     /**
5378      * Gets the current selected value of the date field
5379      * @return {Date} The selected date
5380      */
5381     getValue : function(){
5382         return this.value;
5383     },
5384
5385     // private
5386     focus : function(){
5387         if(this.el){
5388             this.update(this.activeDate);
5389         }
5390     },
5391
5392     // privateval
5393     onRender : function(container, position){
5394         
5395         var m = [
5396              '<table cellspacing="0">',
5397                 '<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>',
5398                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5399         var dn = this.dayNames;
5400         for(var i = 0; i < 7; i++){
5401             var d = this.startDay+i;
5402             if(d > 6){
5403                 d = d-7;
5404             }
5405             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5406         }
5407         m[m.length] = "</tr></thead><tbody><tr>";
5408         for(var i = 0; i < 42; i++) {
5409             if(i % 7 == 0 && i != 0){
5410                 m[m.length] = "</tr><tr>";
5411             }
5412             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5413         }
5414         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5415             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5416
5417         var el = document.createElement("div");
5418         el.className = "x-date-picker";
5419         el.innerHTML = m.join("");
5420
5421         container.dom.insertBefore(el, position);
5422
5423         this.el = Roo.get(el);
5424         this.eventEl = Roo.get(el.firstChild);
5425
5426         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5427             handler: this.showPrevMonth,
5428             scope: this,
5429             preventDefault:true,
5430             stopDefault:true
5431         });
5432
5433         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5434             handler: this.showNextMonth,
5435             scope: this,
5436             preventDefault:true,
5437             stopDefault:true
5438         });
5439
5440         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5441
5442         this.monthPicker = this.el.down('div.x-date-mp');
5443         this.monthPicker.enableDisplayMode('block');
5444         
5445         var kn = new Roo.KeyNav(this.eventEl, {
5446             "left" : function(e){
5447                 e.ctrlKey ?
5448                     this.showPrevMonth() :
5449                     this.update(this.activeDate.add("d", -1));
5450             },
5451
5452             "right" : function(e){
5453                 e.ctrlKey ?
5454                     this.showNextMonth() :
5455                     this.update(this.activeDate.add("d", 1));
5456             },
5457
5458             "up" : function(e){
5459                 e.ctrlKey ?
5460                     this.showNextYear() :
5461                     this.update(this.activeDate.add("d", -7));
5462             },
5463
5464             "down" : function(e){
5465                 e.ctrlKey ?
5466                     this.showPrevYear() :
5467                     this.update(this.activeDate.add("d", 7));
5468             },
5469
5470             "pageUp" : function(e){
5471                 this.showNextMonth();
5472             },
5473
5474             "pageDown" : function(e){
5475                 this.showPrevMonth();
5476             },
5477
5478             "enter" : function(e){
5479                 e.stopPropagation();
5480                 return true;
5481             },
5482
5483             scope : this
5484         });
5485
5486         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5487
5488         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5489
5490         this.el.unselectable();
5491         
5492         this.cells = this.el.select("table.x-date-inner tbody td");
5493         this.textNodes = this.el.query("table.x-date-inner tbody span");
5494
5495         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5496             text: "&#160;",
5497             tooltip: this.monthYearText
5498         });
5499
5500         this.mbtn.on('click', this.showMonthPicker, this);
5501         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5502
5503
5504         var today = (new Date()).dateFormat(this.format);
5505         
5506         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5507         if (this.showClear) {
5508             baseTb.add( new Roo.Toolbar.Fill());
5509         }
5510         baseTb.add({
5511             text: String.format(this.todayText, today),
5512             tooltip: String.format(this.todayTip, today),
5513             handler: this.selectToday,
5514             scope: this
5515         });
5516         
5517         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5518             
5519         //});
5520         if (this.showClear) {
5521             
5522             baseTb.add( new Roo.Toolbar.Fill());
5523             baseTb.add({
5524                 text: '&#160;',
5525                 cls: 'x-btn-icon x-btn-clear',
5526                 handler: function() {
5527                     //this.value = '';
5528                     this.fireEvent("select", this, '');
5529                 },
5530                 scope: this
5531             });
5532         }
5533         
5534         
5535         if(Roo.isIE){
5536             this.el.repaint();
5537         }
5538         this.update(this.value);
5539     },
5540
5541     createMonthPicker : function(){
5542         if(!this.monthPicker.dom.firstChild){
5543             var buf = ['<table border="0" cellspacing="0">'];
5544             for(var i = 0; i < 6; i++){
5545                 buf.push(
5546                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5547                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5548                     i == 0 ?
5549                     '<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>' :
5550                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5551                 );
5552             }
5553             buf.push(
5554                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5555                     this.okText,
5556                     '</button><button type="button" class="x-date-mp-cancel">',
5557                     this.cancelText,
5558                     '</button></td></tr>',
5559                 '</table>'
5560             );
5561             this.monthPicker.update(buf.join(''));
5562             this.monthPicker.on('click', this.onMonthClick, this);
5563             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5564
5565             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5566             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5567
5568             this.mpMonths.each(function(m, a, i){
5569                 i += 1;
5570                 if((i%2) == 0){
5571                     m.dom.xmonth = 5 + Math.round(i * .5);
5572                 }else{
5573                     m.dom.xmonth = Math.round((i-1) * .5);
5574                 }
5575             });
5576         }
5577     },
5578
5579     showMonthPicker : function(){
5580         this.createMonthPicker();
5581         var size = this.el.getSize();
5582         this.monthPicker.setSize(size);
5583         this.monthPicker.child('table').setSize(size);
5584
5585         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5586         this.updateMPMonth(this.mpSelMonth);
5587         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5588         this.updateMPYear(this.mpSelYear);
5589
5590         this.monthPicker.slideIn('t', {duration:.2});
5591     },
5592
5593     updateMPYear : function(y){
5594         this.mpyear = y;
5595         var ys = this.mpYears.elements;
5596         for(var i = 1; i <= 10; i++){
5597             var td = ys[i-1], y2;
5598             if((i%2) == 0){
5599                 y2 = y + Math.round(i * .5);
5600                 td.firstChild.innerHTML = y2;
5601                 td.xyear = y2;
5602             }else{
5603                 y2 = y - (5-Math.round(i * .5));
5604                 td.firstChild.innerHTML = y2;
5605                 td.xyear = y2;
5606             }
5607             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5608         }
5609     },
5610
5611     updateMPMonth : function(sm){
5612         this.mpMonths.each(function(m, a, i){
5613             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5614         });
5615     },
5616
5617     selectMPMonth: function(m){
5618         
5619     },
5620
5621     onMonthClick : function(e, t){
5622         e.stopEvent();
5623         var el = new Roo.Element(t), pn;
5624         if(el.is('button.x-date-mp-cancel')){
5625             this.hideMonthPicker();
5626         }
5627         else if(el.is('button.x-date-mp-ok')){
5628             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5629             this.hideMonthPicker();
5630         }
5631         else if(pn = el.up('td.x-date-mp-month', 2)){
5632             this.mpMonths.removeClass('x-date-mp-sel');
5633             pn.addClass('x-date-mp-sel');
5634             this.mpSelMonth = pn.dom.xmonth;
5635         }
5636         else if(pn = el.up('td.x-date-mp-year', 2)){
5637             this.mpYears.removeClass('x-date-mp-sel');
5638             pn.addClass('x-date-mp-sel');
5639             this.mpSelYear = pn.dom.xyear;
5640         }
5641         else if(el.is('a.x-date-mp-prev')){
5642             this.updateMPYear(this.mpyear-10);
5643         }
5644         else if(el.is('a.x-date-mp-next')){
5645             this.updateMPYear(this.mpyear+10);
5646         }
5647     },
5648
5649     onMonthDblClick : function(e, t){
5650         e.stopEvent();
5651         var el = new Roo.Element(t), pn;
5652         if(pn = el.up('td.x-date-mp-month', 2)){
5653             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5654             this.hideMonthPicker();
5655         }
5656         else if(pn = el.up('td.x-date-mp-year', 2)){
5657             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5658             this.hideMonthPicker();
5659         }
5660     },
5661
5662     hideMonthPicker : function(disableAnim){
5663         if(this.monthPicker){
5664             if(disableAnim === true){
5665                 this.monthPicker.hide();
5666             }else{
5667                 this.monthPicker.slideOut('t', {duration:.2});
5668             }
5669         }
5670     },
5671
5672     // private
5673     showPrevMonth : function(e){
5674         this.update(this.activeDate.add("mo", -1));
5675     },
5676
5677     // private
5678     showNextMonth : function(e){
5679         this.update(this.activeDate.add("mo", 1));
5680     },
5681
5682     // private
5683     showPrevYear : function(){
5684         this.update(this.activeDate.add("y", -1));
5685     },
5686
5687     // private
5688     showNextYear : function(){
5689         this.update(this.activeDate.add("y", 1));
5690     },
5691
5692     // private
5693     handleMouseWheel : function(e){
5694         var delta = e.getWheelDelta();
5695         if(delta > 0){
5696             this.showPrevMonth();
5697             e.stopEvent();
5698         } else if(delta < 0){
5699             this.showNextMonth();
5700             e.stopEvent();
5701         }
5702     },
5703
5704     // private
5705     handleDateClick : function(e, t){
5706         e.stopEvent();
5707         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5708             this.setValue(new Date(t.dateValue));
5709             this.fireEvent("select", this, this.value);
5710         }
5711     },
5712
5713     // private
5714     selectToday : function(){
5715         this.setValue(new Date().clearTime());
5716         this.fireEvent("select", this, this.value);
5717     },
5718
5719     // private
5720     update : function(date)
5721     {
5722         var vd = this.activeDate;
5723         this.activeDate = date;
5724         if(vd && this.el){
5725             var t = date.getTime();
5726             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5727                 this.cells.removeClass("x-date-selected");
5728                 this.cells.each(function(c){
5729                    if(c.dom.firstChild.dateValue == t){
5730                        c.addClass("x-date-selected");
5731                        setTimeout(function(){
5732                             try{c.dom.firstChild.focus();}catch(e){}
5733                        }, 50);
5734                        return false;
5735                    }
5736                 });
5737                 return;
5738             }
5739         }
5740         
5741         var days = date.getDaysInMonth();
5742         var firstOfMonth = date.getFirstDateOfMonth();
5743         var startingPos = firstOfMonth.getDay()-this.startDay;
5744
5745         if(startingPos <= this.startDay){
5746             startingPos += 7;
5747         }
5748
5749         var pm = date.add("mo", -1);
5750         var prevStart = pm.getDaysInMonth()-startingPos;
5751
5752         var cells = this.cells.elements;
5753         var textEls = this.textNodes;
5754         days += startingPos;
5755
5756         // convert everything to numbers so it's fast
5757         var day = 86400000;
5758         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5759         var today = new Date().clearTime().getTime();
5760         var sel = date.clearTime().getTime();
5761         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5762         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5763         var ddMatch = this.disabledDatesRE;
5764         var ddText = this.disabledDatesText;
5765         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5766         var ddaysText = this.disabledDaysText;
5767         var format = this.format;
5768
5769         var setCellClass = function(cal, cell){
5770             cell.title = "";
5771             var t = d.getTime();
5772             cell.firstChild.dateValue = t;
5773             if(t == today){
5774                 cell.className += " x-date-today";
5775                 cell.title = cal.todayText;
5776             }
5777             if(t == sel){
5778                 cell.className += " x-date-selected";
5779                 setTimeout(function(){
5780                     try{cell.firstChild.focus();}catch(e){}
5781                 }, 50);
5782             }
5783             // disabling
5784             if(t < min) {
5785                 cell.className = " x-date-disabled";
5786                 cell.title = cal.minText;
5787                 return;
5788             }
5789             if(t > max) {
5790                 cell.className = " x-date-disabled";
5791                 cell.title = cal.maxText;
5792                 return;
5793             }
5794             if(ddays){
5795                 if(ddays.indexOf(d.getDay()) != -1){
5796                     cell.title = ddaysText;
5797                     cell.className = " x-date-disabled";
5798                 }
5799             }
5800             if(ddMatch && format){
5801                 var fvalue = d.dateFormat(format);
5802                 if(ddMatch.test(fvalue)){
5803                     cell.title = ddText.replace("%0", fvalue);
5804                     cell.className = " x-date-disabled";
5805                 }
5806             }
5807         };
5808
5809         var i = 0;
5810         for(; i < startingPos; i++) {
5811             textEls[i].innerHTML = (++prevStart);
5812             d.setDate(d.getDate()+1);
5813             cells[i].className = "x-date-prevday";
5814             setCellClass(this, cells[i]);
5815         }
5816         for(; i < days; i++){
5817             intDay = i - startingPos + 1;
5818             textEls[i].innerHTML = (intDay);
5819             d.setDate(d.getDate()+1);
5820             cells[i].className = "x-date-active";
5821             setCellClass(this, cells[i]);
5822         }
5823         var extraDays = 0;
5824         for(; i < 42; i++) {
5825              textEls[i].innerHTML = (++extraDays);
5826              d.setDate(d.getDate()+1);
5827              cells[i].className = "x-date-nextday";
5828              setCellClass(this, cells[i]);
5829         }
5830
5831         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5832         this.fireEvent('monthchange', this, date);
5833         
5834         if(!this.internalRender){
5835             var main = this.el.dom.firstChild;
5836             var w = main.offsetWidth;
5837             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5838             Roo.fly(main).setWidth(w);
5839             this.internalRender = true;
5840             // opera does not respect the auto grow header center column
5841             // then, after it gets a width opera refuses to recalculate
5842             // without a second pass
5843             if(Roo.isOpera && !this.secondPass){
5844                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5845                 this.secondPass = true;
5846                 this.update.defer(10, this, [date]);
5847             }
5848         }
5849         
5850         
5851     }
5852 });        /*
5853  * Based on:
5854  * Ext JS Library 1.1.1
5855  * Copyright(c) 2006-2007, Ext JS, LLC.
5856  *
5857  * Originally Released Under LGPL - original licence link has changed is not relivant.
5858  *
5859  * Fork - LGPL
5860  * <script type="text/javascript">
5861  */
5862 /**
5863  * @class Roo.TabPanel
5864  * @extends Roo.util.Observable
5865  * A lightweight tab container.
5866  * <br><br>
5867  * Usage:
5868  * <pre><code>
5869 // basic tabs 1, built from existing content
5870 var tabs = new Roo.TabPanel("tabs1");
5871 tabs.addTab("script", "View Script");
5872 tabs.addTab("markup", "View Markup");
5873 tabs.activate("script");
5874
5875 // more advanced tabs, built from javascript
5876 var jtabs = new Roo.TabPanel("jtabs");
5877 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5878
5879 // set up the UpdateManager
5880 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5881 var updater = tab2.getUpdateManager();
5882 updater.setDefaultUrl("ajax1.htm");
5883 tab2.on('activate', updater.refresh, updater, true);
5884
5885 // Use setUrl for Ajax loading
5886 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5887 tab3.setUrl("ajax2.htm", null, true);
5888
5889 // Disabled tab
5890 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5891 tab4.disable();
5892
5893 jtabs.activate("jtabs-1");
5894  * </code></pre>
5895  * @constructor
5896  * Create a new TabPanel.
5897  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5898  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5899  */
5900 Roo.TabPanel = function(container, config){
5901     /**
5902     * The container element for this TabPanel.
5903     * @type Roo.Element
5904     */
5905     this.el = Roo.get(container, true);
5906     if(config){
5907         if(typeof config == "boolean"){
5908             this.tabPosition = config ? "bottom" : "top";
5909         }else{
5910             Roo.apply(this, config);
5911         }
5912     }
5913     if(this.tabPosition == "bottom"){
5914         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5915         this.el.addClass("x-tabs-bottom");
5916     }
5917     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5918     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5919     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5920     if(Roo.isIE){
5921         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5922     }
5923     if(this.tabPosition != "bottom"){
5924         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5925          * @type Roo.Element
5926          */
5927         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5928         this.el.addClass("x-tabs-top");
5929     }
5930     this.items = [];
5931
5932     this.bodyEl.setStyle("position", "relative");
5933
5934     this.active = null;
5935     this.activateDelegate = this.activate.createDelegate(this);
5936
5937     this.addEvents({
5938         /**
5939          * @event tabchange
5940          * Fires when the active tab changes
5941          * @param {Roo.TabPanel} this
5942          * @param {Roo.TabPanelItem} activePanel The new active tab
5943          */
5944         "tabchange": true,
5945         /**
5946          * @event beforetabchange
5947          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5948          * @param {Roo.TabPanel} this
5949          * @param {Object} e Set cancel to true on this object to cancel the tab change
5950          * @param {Roo.TabPanelItem} tab The tab being changed to
5951          */
5952         "beforetabchange" : true
5953     });
5954
5955     Roo.EventManager.onWindowResize(this.onResize, this);
5956     this.cpad = this.el.getPadding("lr");
5957     this.hiddenCount = 0;
5958
5959
5960     // toolbar on the tabbar support...
5961     if (this.toolbar) {
5962         var tcfg = this.toolbar;
5963         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5964         this.toolbar = new Roo.Toolbar(tcfg);
5965         if (Roo.isSafari) {
5966             var tbl = tcfg.container.child('table', true);
5967             tbl.setAttribute('width', '100%');
5968         }
5969         
5970     }
5971    
5972
5973
5974     Roo.TabPanel.superclass.constructor.call(this);
5975 };
5976
5977 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5978     /*
5979      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5980      */
5981     tabPosition : "top",
5982     /*
5983      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5984      */
5985     currentTabWidth : 0,
5986     /*
5987      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5988      */
5989     minTabWidth : 40,
5990     /*
5991      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5992      */
5993     maxTabWidth : 250,
5994     /*
5995      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5996      */
5997     preferredTabWidth : 175,
5998     /*
5999      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6000      */
6001     resizeTabs : false,
6002     /*
6003      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6004      */
6005     monitorResize : true,
6006     /*
6007      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6008      */
6009     toolbar : false,
6010
6011     /**
6012      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6013      * @param {String} id The id of the div to use <b>or create</b>
6014      * @param {String} text The text for the tab
6015      * @param {String} content (optional) Content to put in the TabPanelItem body
6016      * @param {Boolean} closable (optional) True to create a close icon on the tab
6017      * @return {Roo.TabPanelItem} The created TabPanelItem
6018      */
6019     addTab : function(id, text, content, closable){
6020         var item = new Roo.TabPanelItem(this, id, text, closable);
6021         this.addTabItem(item);
6022         if(content){
6023             item.setContent(content);
6024         }
6025         return item;
6026     },
6027
6028     /**
6029      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6030      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6031      * @return {Roo.TabPanelItem}
6032      */
6033     getTab : function(id){
6034         return this.items[id];
6035     },
6036
6037     /**
6038      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6039      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6040      */
6041     hideTab : function(id){
6042         var t = this.items[id];
6043         if(!t.isHidden()){
6044            t.setHidden(true);
6045            this.hiddenCount++;
6046            this.autoSizeTabs();
6047         }
6048     },
6049
6050     /**
6051      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6052      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6053      */
6054     unhideTab : function(id){
6055         var t = this.items[id];
6056         if(t.isHidden()){
6057            t.setHidden(false);
6058            this.hiddenCount--;
6059            this.autoSizeTabs();
6060         }
6061     },
6062
6063     /**
6064      * Adds an existing {@link Roo.TabPanelItem}.
6065      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6066      */
6067     addTabItem : function(item){
6068         this.items[item.id] = item;
6069         this.items.push(item);
6070         if(this.resizeTabs){
6071            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6072            this.autoSizeTabs();
6073         }else{
6074             item.autoSize();
6075         }
6076     },
6077
6078     /**
6079      * Removes a {@link Roo.TabPanelItem}.
6080      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6081      */
6082     removeTab : function(id){
6083         var items = this.items;
6084         var tab = items[id];
6085         if(!tab) { return; }
6086         var index = items.indexOf(tab);
6087         if(this.active == tab && items.length > 1){
6088             var newTab = this.getNextAvailable(index);
6089             if(newTab) {
6090                 newTab.activate();
6091             }
6092         }
6093         this.stripEl.dom.removeChild(tab.pnode.dom);
6094         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6095             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6096         }
6097         items.splice(index, 1);
6098         delete this.items[tab.id];
6099         tab.fireEvent("close", tab);
6100         tab.purgeListeners();
6101         this.autoSizeTabs();
6102     },
6103
6104     getNextAvailable : function(start){
6105         var items = this.items;
6106         var index = start;
6107         // look for a next tab that will slide over to
6108         // replace the one being removed
6109         while(index < items.length){
6110             var item = items[++index];
6111             if(item && !item.isHidden()){
6112                 return item;
6113             }
6114         }
6115         // if one isn't found select the previous tab (on the left)
6116         index = start;
6117         while(index >= 0){
6118             var item = items[--index];
6119             if(item && !item.isHidden()){
6120                 return item;
6121             }
6122         }
6123         return null;
6124     },
6125
6126     /**
6127      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6128      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6129      */
6130     disableTab : function(id){
6131         var tab = this.items[id];
6132         if(tab && this.active != tab){
6133             tab.disable();
6134         }
6135     },
6136
6137     /**
6138      * Enables a {@link Roo.TabPanelItem} that is disabled.
6139      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6140      */
6141     enableTab : function(id){
6142         var tab = this.items[id];
6143         tab.enable();
6144     },
6145
6146     /**
6147      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6148      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6149      * @return {Roo.TabPanelItem} The TabPanelItem.
6150      */
6151     activate : function(id){
6152         var tab = this.items[id];
6153         if(!tab){
6154             return null;
6155         }
6156         if(tab == this.active || tab.disabled){
6157             return tab;
6158         }
6159         var e = {};
6160         this.fireEvent("beforetabchange", this, e, tab);
6161         if(e.cancel !== true && !tab.disabled){
6162             if(this.active){
6163                 this.active.hide();
6164             }
6165             this.active = this.items[id];
6166             this.active.show();
6167             this.fireEvent("tabchange", this, this.active);
6168         }
6169         return tab;
6170     },
6171
6172     /**
6173      * Gets the active {@link Roo.TabPanelItem}.
6174      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6175      */
6176     getActiveTab : function(){
6177         return this.active;
6178     },
6179
6180     /**
6181      * Updates the tab body element to fit the height of the container element
6182      * for overflow scrolling
6183      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6184      */
6185     syncHeight : function(targetHeight){
6186         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6187         var bm = this.bodyEl.getMargins();
6188         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6189         this.bodyEl.setHeight(newHeight);
6190         return newHeight;
6191     },
6192
6193     onResize : function(){
6194         if(this.monitorResize){
6195             this.autoSizeTabs();
6196         }
6197     },
6198
6199     /**
6200      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6201      */
6202     beginUpdate : function(){
6203         this.updating = true;
6204     },
6205
6206     /**
6207      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6208      */
6209     endUpdate : function(){
6210         this.updating = false;
6211         this.autoSizeTabs();
6212     },
6213
6214     /**
6215      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6216      */
6217     autoSizeTabs : function(){
6218         var count = this.items.length;
6219         var vcount = count - this.hiddenCount;
6220         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6221             return;
6222         }
6223         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6224         var availWidth = Math.floor(w / vcount);
6225         var b = this.stripBody;
6226         if(b.getWidth() > w){
6227             var tabs = this.items;
6228             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6229             if(availWidth < this.minTabWidth){
6230                 /*if(!this.sleft){    // incomplete scrolling code
6231                     this.createScrollButtons();
6232                 }
6233                 this.showScroll();
6234                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6235             }
6236         }else{
6237             if(this.currentTabWidth < this.preferredTabWidth){
6238                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6239             }
6240         }
6241     },
6242
6243     /**
6244      * Returns the number of tabs in this TabPanel.
6245      * @return {Number}
6246      */
6247      getCount : function(){
6248          return this.items.length;
6249      },
6250
6251     /**
6252      * Resizes all the tabs to the passed width
6253      * @param {Number} The new width
6254      */
6255     setTabWidth : function(width){
6256         this.currentTabWidth = width;
6257         for(var i = 0, len = this.items.length; i < len; i++) {
6258                 if(!this.items[i].isHidden()) {
6259                 this.items[i].setWidth(width);
6260             }
6261         }
6262     },
6263
6264     /**
6265      * Destroys this TabPanel
6266      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6267      */
6268     destroy : function(removeEl){
6269         Roo.EventManager.removeResizeListener(this.onResize, this);
6270         for(var i = 0, len = this.items.length; i < len; i++){
6271             this.items[i].purgeListeners();
6272         }
6273         if(removeEl === true){
6274             this.el.update("");
6275             this.el.remove();
6276         }
6277     }
6278 });
6279
6280 /**
6281  * @class Roo.TabPanelItem
6282  * @extends Roo.util.Observable
6283  * Represents an individual item (tab plus body) in a TabPanel.
6284  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6285  * @param {String} id The id of this TabPanelItem
6286  * @param {String} text The text for the tab of this TabPanelItem
6287  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6288  */
6289 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6290     /**
6291      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6292      * @type Roo.TabPanel
6293      */
6294     this.tabPanel = tabPanel;
6295     /**
6296      * The id for this TabPanelItem
6297      * @type String
6298      */
6299     this.id = id;
6300     /** @private */
6301     this.disabled = false;
6302     /** @private */
6303     this.text = text;
6304     /** @private */
6305     this.loaded = false;
6306     this.closable = closable;
6307
6308     /**
6309      * The body element for this TabPanelItem.
6310      * @type Roo.Element
6311      */
6312     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6313     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6314     this.bodyEl.setStyle("display", "block");
6315     this.bodyEl.setStyle("zoom", "1");
6316     this.hideAction();
6317
6318     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6319     /** @private */
6320     this.el = Roo.get(els.el, true);
6321     this.inner = Roo.get(els.inner, true);
6322     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6323     this.pnode = Roo.get(els.el.parentNode, true);
6324     this.el.on("mousedown", this.onTabMouseDown, this);
6325     this.el.on("click", this.onTabClick, this);
6326     /** @private */
6327     if(closable){
6328         var c = Roo.get(els.close, true);
6329         c.dom.title = this.closeText;
6330         c.addClassOnOver("close-over");
6331         c.on("click", this.closeClick, this);
6332      }
6333
6334     this.addEvents({
6335          /**
6336          * @event activate
6337          * Fires when this tab becomes the active tab.
6338          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6339          * @param {Roo.TabPanelItem} this
6340          */
6341         "activate": true,
6342         /**
6343          * @event beforeclose
6344          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6345          * @param {Roo.TabPanelItem} this
6346          * @param {Object} e Set cancel to true on this object to cancel the close.
6347          */
6348         "beforeclose": true,
6349         /**
6350          * @event close
6351          * Fires when this tab is closed.
6352          * @param {Roo.TabPanelItem} this
6353          */
6354          "close": true,
6355         /**
6356          * @event deactivate
6357          * Fires when this tab is no longer the active tab.
6358          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6359          * @param {Roo.TabPanelItem} this
6360          */
6361          "deactivate" : true
6362     });
6363     this.hidden = false;
6364
6365     Roo.TabPanelItem.superclass.constructor.call(this);
6366 };
6367
6368 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6369     purgeListeners : function(){
6370        Roo.util.Observable.prototype.purgeListeners.call(this);
6371        this.el.removeAllListeners();
6372     },
6373     /**
6374      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6375      */
6376     show : function(){
6377         this.pnode.addClass("on");
6378         this.showAction();
6379         if(Roo.isOpera){
6380             this.tabPanel.stripWrap.repaint();
6381         }
6382         this.fireEvent("activate", this.tabPanel, this);
6383     },
6384
6385     /**
6386      * Returns true if this tab is the active tab.
6387      * @return {Boolean}
6388      */
6389     isActive : function(){
6390         return this.tabPanel.getActiveTab() == this;
6391     },
6392
6393     /**
6394      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6395      */
6396     hide : function(){
6397         this.pnode.removeClass("on");
6398         this.hideAction();
6399         this.fireEvent("deactivate", this.tabPanel, this);
6400     },
6401
6402     hideAction : function(){
6403         this.bodyEl.hide();
6404         this.bodyEl.setStyle("position", "absolute");
6405         this.bodyEl.setLeft("-20000px");
6406         this.bodyEl.setTop("-20000px");
6407     },
6408
6409     showAction : function(){
6410         this.bodyEl.setStyle("position", "relative");
6411         this.bodyEl.setTop("");
6412         this.bodyEl.setLeft("");
6413         this.bodyEl.show();
6414     },
6415
6416     /**
6417      * Set the tooltip for the tab.
6418      * @param {String} tooltip The tab's tooltip
6419      */
6420     setTooltip : function(text){
6421         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6422             this.textEl.dom.qtip = text;
6423             this.textEl.dom.removeAttribute('title');
6424         }else{
6425             this.textEl.dom.title = text;
6426         }
6427     },
6428
6429     onTabClick : function(e){
6430         e.preventDefault();
6431         this.tabPanel.activate(this.id);
6432     },
6433
6434     onTabMouseDown : function(e){
6435         e.preventDefault();
6436         this.tabPanel.activate(this.id);
6437     },
6438
6439     getWidth : function(){
6440         return this.inner.getWidth();
6441     },
6442
6443     setWidth : function(width){
6444         var iwidth = width - this.pnode.getPadding("lr");
6445         this.inner.setWidth(iwidth);
6446         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6447         this.pnode.setWidth(width);
6448     },
6449
6450     /**
6451      * Show or hide the tab
6452      * @param {Boolean} hidden True to hide or false to show.
6453      */
6454     setHidden : function(hidden){
6455         this.hidden = hidden;
6456         this.pnode.setStyle("display", hidden ? "none" : "");
6457     },
6458
6459     /**
6460      * Returns true if this tab is "hidden"
6461      * @return {Boolean}
6462      */
6463     isHidden : function(){
6464         return this.hidden;
6465     },
6466
6467     /**
6468      * Returns the text for this tab
6469      * @return {String}
6470      */
6471     getText : function(){
6472         return this.text;
6473     },
6474
6475     autoSize : function(){
6476         //this.el.beginMeasure();
6477         this.textEl.setWidth(1);
6478         /*
6479          *  #2804 [new] Tabs in Roojs
6480          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6481          */
6482         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6483         //this.el.endMeasure();
6484     },
6485
6486     /**
6487      * Sets the text for the tab (Note: this also sets the tooltip text)
6488      * @param {String} text The tab's text and tooltip
6489      */
6490     setText : function(text){
6491         this.text = text;
6492         this.textEl.update(text);
6493         this.setTooltip(text);
6494         if(!this.tabPanel.resizeTabs){
6495             this.autoSize();
6496         }
6497     },
6498     /**
6499      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6500      */
6501     activate : function(){
6502         this.tabPanel.activate(this.id);
6503     },
6504
6505     /**
6506      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6507      */
6508     disable : function(){
6509         if(this.tabPanel.active != this){
6510             this.disabled = true;
6511             this.pnode.addClass("disabled");
6512         }
6513     },
6514
6515     /**
6516      * Enables this TabPanelItem if it was previously disabled.
6517      */
6518     enable : function(){
6519         this.disabled = false;
6520         this.pnode.removeClass("disabled");
6521     },
6522
6523     /**
6524      * Sets the content for this TabPanelItem.
6525      * @param {String} content The content
6526      * @param {Boolean} loadScripts true to look for and load scripts
6527      */
6528     setContent : function(content, loadScripts){
6529         this.bodyEl.update(content, loadScripts);
6530     },
6531
6532     /**
6533      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6534      * @return {Roo.UpdateManager} The UpdateManager
6535      */
6536     getUpdateManager : function(){
6537         return this.bodyEl.getUpdateManager();
6538     },
6539
6540     /**
6541      * Set a URL to be used to load the content for this TabPanelItem.
6542      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6543      * @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)
6544      * @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)
6545      * @return {Roo.UpdateManager} The UpdateManager
6546      */
6547     setUrl : function(url, params, loadOnce){
6548         if(this.refreshDelegate){
6549             this.un('activate', this.refreshDelegate);
6550         }
6551         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6552         this.on("activate", this.refreshDelegate);
6553         return this.bodyEl.getUpdateManager();
6554     },
6555
6556     /** @private */
6557     _handleRefresh : function(url, params, loadOnce){
6558         if(!loadOnce || !this.loaded){
6559             var updater = this.bodyEl.getUpdateManager();
6560             updater.update(url, params, this._setLoaded.createDelegate(this));
6561         }
6562     },
6563
6564     /**
6565      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6566      *   Will fail silently if the setUrl method has not been called.
6567      *   This does not activate the panel, just updates its content.
6568      */
6569     refresh : function(){
6570         if(this.refreshDelegate){
6571            this.loaded = false;
6572            this.refreshDelegate();
6573         }
6574     },
6575
6576     /** @private */
6577     _setLoaded : function(){
6578         this.loaded = true;
6579     },
6580
6581     /** @private */
6582     closeClick : function(e){
6583         var o = {};
6584         e.stopEvent();
6585         this.fireEvent("beforeclose", this, o);
6586         if(o.cancel !== true){
6587             this.tabPanel.removeTab(this.id);
6588         }
6589     },
6590     /**
6591      * The text displayed in the tooltip for the close icon.
6592      * @type String
6593      */
6594     closeText : "Close this tab"
6595 });
6596
6597 /** @private */
6598 Roo.TabPanel.prototype.createStrip = function(container){
6599     var strip = document.createElement("div");
6600     strip.className = "x-tabs-wrap";
6601     container.appendChild(strip);
6602     return strip;
6603 };
6604 /** @private */
6605 Roo.TabPanel.prototype.createStripList = function(strip){
6606     // div wrapper for retard IE
6607     // returns the "tr" element.
6608     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6609         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6610         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6611     return strip.firstChild.firstChild.firstChild.firstChild;
6612 };
6613 /** @private */
6614 Roo.TabPanel.prototype.createBody = function(container){
6615     var body = document.createElement("div");
6616     Roo.id(body, "tab-body");
6617     Roo.fly(body).addClass("x-tabs-body");
6618     container.appendChild(body);
6619     return body;
6620 };
6621 /** @private */
6622 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6623     var body = Roo.getDom(id);
6624     if(!body){
6625         body = document.createElement("div");
6626         body.id = id;
6627     }
6628     Roo.fly(body).addClass("x-tabs-item-body");
6629     bodyEl.insertBefore(body, bodyEl.firstChild);
6630     return body;
6631 };
6632 /** @private */
6633 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6634     var td = document.createElement("td");
6635     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6636     //stripEl.appendChild(td);
6637     if(closable){
6638         td.className = "x-tabs-closable";
6639         if(!this.closeTpl){
6640             this.closeTpl = new Roo.Template(
6641                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6642                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6643                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6644             );
6645         }
6646         var el = this.closeTpl.overwrite(td, {"text": text});
6647         var close = el.getElementsByTagName("div")[0];
6648         var inner = el.getElementsByTagName("em")[0];
6649         return {"el": el, "close": close, "inner": inner};
6650     } else {
6651         if(!this.tabTpl){
6652             this.tabTpl = new Roo.Template(
6653                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6654                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6655             );
6656         }
6657         var el = this.tabTpl.overwrite(td, {"text": text});
6658         var inner = el.getElementsByTagName("em")[0];
6659         return {"el": el, "inner": inner};
6660     }
6661 };/*
6662  * Based on:
6663  * Ext JS Library 1.1.1
6664  * Copyright(c) 2006-2007, Ext JS, LLC.
6665  *
6666  * Originally Released Under LGPL - original licence link has changed is not relivant.
6667  *
6668  * Fork - LGPL
6669  * <script type="text/javascript">
6670  */
6671
6672 /**
6673  * @class Roo.Button
6674  * @extends Roo.util.Observable
6675  * Simple Button class
6676  * @cfg {String} text The button text
6677  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6678  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6679  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6680  * @cfg {Object} scope The scope of the handler
6681  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6682  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6683  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6684  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6685  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6686  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6687    applies if enableToggle = true)
6688  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6689  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6690   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6691  * @constructor
6692  * Create a new button
6693  * @param {Object} config The config object
6694  */
6695 Roo.Button = function(renderTo, config)
6696 {
6697     if (!config) {
6698         config = renderTo;
6699         renderTo = config.renderTo || false;
6700     }
6701     
6702     Roo.apply(this, config);
6703     this.addEvents({
6704         /**
6705              * @event click
6706              * Fires when this button is clicked
6707              * @param {Button} this
6708              * @param {EventObject} e The click event
6709              */
6710             "click" : true,
6711         /**
6712              * @event toggle
6713              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6714              * @param {Button} this
6715              * @param {Boolean} pressed
6716              */
6717             "toggle" : true,
6718         /**
6719              * @event mouseover
6720              * Fires when the mouse hovers over the button
6721              * @param {Button} this
6722              * @param {Event} e The event object
6723              */
6724         'mouseover' : true,
6725         /**
6726              * @event mouseout
6727              * Fires when the mouse exits the button
6728              * @param {Button} this
6729              * @param {Event} e The event object
6730              */
6731         'mouseout': true,
6732          /**
6733              * @event render
6734              * Fires when the button is rendered
6735              * @param {Button} this
6736              */
6737         'render': true
6738     });
6739     if(this.menu){
6740         this.menu = Roo.menu.MenuMgr.get(this.menu);
6741     }
6742     // register listeners first!!  - so render can be captured..
6743     Roo.util.Observable.call(this);
6744     if(renderTo){
6745         this.render(renderTo);
6746     }
6747     
6748   
6749 };
6750
6751 Roo.extend(Roo.Button, Roo.util.Observable, {
6752     /**
6753      * 
6754      */
6755     
6756     /**
6757      * Read-only. True if this button is hidden
6758      * @type Boolean
6759      */
6760     hidden : false,
6761     /**
6762      * Read-only. True if this button is disabled
6763      * @type Boolean
6764      */
6765     disabled : false,
6766     /**
6767      * Read-only. True if this button is pressed (only if enableToggle = true)
6768      * @type Boolean
6769      */
6770     pressed : false,
6771
6772     /**
6773      * @cfg {Number} tabIndex 
6774      * The DOM tabIndex for this button (defaults to undefined)
6775      */
6776     tabIndex : undefined,
6777
6778     /**
6779      * @cfg {Boolean} enableToggle
6780      * True to enable pressed/not pressed toggling (defaults to false)
6781      */
6782     enableToggle: false,
6783     /**
6784      * @cfg {Mixed} menu
6785      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6786      */
6787     menu : undefined,
6788     /**
6789      * @cfg {String} menuAlign
6790      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6791      */
6792     menuAlign : "tl-bl?",
6793
6794     /**
6795      * @cfg {String} iconCls
6796      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6797      */
6798     iconCls : undefined,
6799     /**
6800      * @cfg {String} type
6801      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6802      */
6803     type : 'button',
6804
6805     // private
6806     menuClassTarget: 'tr',
6807
6808     /**
6809      * @cfg {String} clickEvent
6810      * The type of event to map to the button's event handler (defaults to 'click')
6811      */
6812     clickEvent : 'click',
6813
6814     /**
6815      * @cfg {Boolean} handleMouseEvents
6816      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6817      */
6818     handleMouseEvents : true,
6819
6820     /**
6821      * @cfg {String} tooltipType
6822      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6823      */
6824     tooltipType : 'qtip',
6825
6826     /**
6827      * @cfg {String} cls
6828      * A CSS class to apply to the button's main element.
6829      */
6830     
6831     /**
6832      * @cfg {Roo.Template} template (Optional)
6833      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6834      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6835      * require code modifications if required elements (e.g. a button) aren't present.
6836      */
6837
6838     // private
6839     render : function(renderTo){
6840         var btn;
6841         if(this.hideParent){
6842             this.parentEl = Roo.get(renderTo);
6843         }
6844         if(!this.dhconfig){
6845             if(!this.template){
6846                 if(!Roo.Button.buttonTemplate){
6847                     // hideous table template
6848                     Roo.Button.buttonTemplate = new Roo.Template(
6849                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6850                         '<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>',
6851                         "</tr></tbody></table>");
6852                 }
6853                 this.template = Roo.Button.buttonTemplate;
6854             }
6855             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6856             var btnEl = btn.child("button:first");
6857             btnEl.on('focus', this.onFocus, this);
6858             btnEl.on('blur', this.onBlur, this);
6859             if(this.cls){
6860                 btn.addClass(this.cls);
6861             }
6862             if(this.icon){
6863                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6864             }
6865             if(this.iconCls){
6866                 btnEl.addClass(this.iconCls);
6867                 if(!this.cls){
6868                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6869                 }
6870             }
6871             if(this.tabIndex !== undefined){
6872                 btnEl.dom.tabIndex = this.tabIndex;
6873             }
6874             if(this.tooltip){
6875                 if(typeof this.tooltip == 'object'){
6876                     Roo.QuickTips.tips(Roo.apply({
6877                           target: btnEl.id
6878                     }, this.tooltip));
6879                 } else {
6880                     btnEl.dom[this.tooltipType] = this.tooltip;
6881                 }
6882             }
6883         }else{
6884             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6885         }
6886         this.el = btn;
6887         if(this.id){
6888             this.el.dom.id = this.el.id = this.id;
6889         }
6890         if(this.menu){
6891             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6892             this.menu.on("show", this.onMenuShow, this);
6893             this.menu.on("hide", this.onMenuHide, this);
6894         }
6895         btn.addClass("x-btn");
6896         if(Roo.isIE && !Roo.isIE7){
6897             this.autoWidth.defer(1, this);
6898         }else{
6899             this.autoWidth();
6900         }
6901         if(this.handleMouseEvents){
6902             btn.on("mouseover", this.onMouseOver, this);
6903             btn.on("mouseout", this.onMouseOut, this);
6904             btn.on("mousedown", this.onMouseDown, this);
6905         }
6906         btn.on(this.clickEvent, this.onClick, this);
6907         //btn.on("mouseup", this.onMouseUp, this);
6908         if(this.hidden){
6909             this.hide();
6910         }
6911         if(this.disabled){
6912             this.disable();
6913         }
6914         Roo.ButtonToggleMgr.register(this);
6915         if(this.pressed){
6916             this.el.addClass("x-btn-pressed");
6917         }
6918         if(this.repeat){
6919             var repeater = new Roo.util.ClickRepeater(btn,
6920                 typeof this.repeat == "object" ? this.repeat : {}
6921             );
6922             repeater.on("click", this.onClick,  this);
6923         }
6924         
6925         this.fireEvent('render', this);
6926         
6927     },
6928     /**
6929      * Returns the button's underlying element
6930      * @return {Roo.Element} The element
6931      */
6932     getEl : function(){
6933         return this.el;  
6934     },
6935     
6936     /**
6937      * Destroys this Button and removes any listeners.
6938      */
6939     destroy : function(){
6940         Roo.ButtonToggleMgr.unregister(this);
6941         this.el.removeAllListeners();
6942         this.purgeListeners();
6943         this.el.remove();
6944     },
6945
6946     // private
6947     autoWidth : function(){
6948         if(this.el){
6949             this.el.setWidth("auto");
6950             if(Roo.isIE7 && Roo.isStrict){
6951                 var ib = this.el.child('button');
6952                 if(ib && ib.getWidth() > 20){
6953                     ib.clip();
6954                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6955                 }
6956             }
6957             if(this.minWidth){
6958                 if(this.hidden){
6959                     this.el.beginMeasure();
6960                 }
6961                 if(this.el.getWidth() < this.minWidth){
6962                     this.el.setWidth(this.minWidth);
6963                 }
6964                 if(this.hidden){
6965                     this.el.endMeasure();
6966                 }
6967             }
6968         }
6969     },
6970
6971     /**
6972      * Assigns this button's click handler
6973      * @param {Function} handler The function to call when the button is clicked
6974      * @param {Object} scope (optional) Scope for the function passed in
6975      */
6976     setHandler : function(handler, scope){
6977         this.handler = handler;
6978         this.scope = scope;  
6979     },
6980     
6981     /**
6982      * Sets this button's text
6983      * @param {String} text The button text
6984      */
6985     setText : function(text){
6986         this.text = text;
6987         if(this.el){
6988             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6989         }
6990         this.autoWidth();
6991     },
6992     
6993     /**
6994      * Gets the text for this button
6995      * @return {String} The button text
6996      */
6997     getText : function(){
6998         return this.text;  
6999     },
7000     
7001     /**
7002      * Show this button
7003      */
7004     show: function(){
7005         this.hidden = false;
7006         if(this.el){
7007             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7008         }
7009     },
7010     
7011     /**
7012      * Hide this button
7013      */
7014     hide: function(){
7015         this.hidden = true;
7016         if(this.el){
7017             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7018         }
7019     },
7020     
7021     /**
7022      * Convenience function for boolean show/hide
7023      * @param {Boolean} visible True to show, false to hide
7024      */
7025     setVisible: function(visible){
7026         if(visible) {
7027             this.show();
7028         }else{
7029             this.hide();
7030         }
7031     },
7032     
7033     /**
7034      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7035      * @param {Boolean} state (optional) Force a particular state
7036      */
7037     toggle : function(state){
7038         state = state === undefined ? !this.pressed : state;
7039         if(state != this.pressed){
7040             if(state){
7041                 this.el.addClass("x-btn-pressed");
7042                 this.pressed = true;
7043                 this.fireEvent("toggle", this, true);
7044             }else{
7045                 this.el.removeClass("x-btn-pressed");
7046                 this.pressed = false;
7047                 this.fireEvent("toggle", this, false);
7048             }
7049             if(this.toggleHandler){
7050                 this.toggleHandler.call(this.scope || this, this, state);
7051             }
7052         }
7053     },
7054     
7055     /**
7056      * Focus the button
7057      */
7058     focus : function(){
7059         this.el.child('button:first').focus();
7060     },
7061     
7062     /**
7063      * Disable this button
7064      */
7065     disable : function(){
7066         if(this.el){
7067             this.el.addClass("x-btn-disabled");
7068         }
7069         this.disabled = true;
7070     },
7071     
7072     /**
7073      * Enable this button
7074      */
7075     enable : function(){
7076         if(this.el){
7077             this.el.removeClass("x-btn-disabled");
7078         }
7079         this.disabled = false;
7080     },
7081
7082     /**
7083      * Convenience function for boolean enable/disable
7084      * @param {Boolean} enabled True to enable, false to disable
7085      */
7086     setDisabled : function(v){
7087         this[v !== true ? "enable" : "disable"]();
7088     },
7089
7090     // private
7091     onClick : function(e)
7092     {
7093         if(e){
7094             e.preventDefault();
7095         }
7096         if(e.button != 0){
7097             return;
7098         }
7099         if(!this.disabled){
7100             if(this.enableToggle){
7101                 this.toggle();
7102             }
7103             if(this.menu && !this.menu.isVisible()){
7104                 this.menu.show(this.el, this.menuAlign);
7105             }
7106             this.fireEvent("click", this, e);
7107             if(this.handler){
7108                 this.el.removeClass("x-btn-over");
7109                 this.handler.call(this.scope || this, this, e);
7110             }
7111         }
7112     },
7113     // private
7114     onMouseOver : function(e){
7115         if(!this.disabled){
7116             this.el.addClass("x-btn-over");
7117             this.fireEvent('mouseover', this, e);
7118         }
7119     },
7120     // private
7121     onMouseOut : function(e){
7122         if(!e.within(this.el,  true)){
7123             this.el.removeClass("x-btn-over");
7124             this.fireEvent('mouseout', this, e);
7125         }
7126     },
7127     // private
7128     onFocus : function(e){
7129         if(!this.disabled){
7130             this.el.addClass("x-btn-focus");
7131         }
7132     },
7133     // private
7134     onBlur : function(e){
7135         this.el.removeClass("x-btn-focus");
7136     },
7137     // private
7138     onMouseDown : function(e){
7139         if(!this.disabled && e.button == 0){
7140             this.el.addClass("x-btn-click");
7141             Roo.get(document).on('mouseup', this.onMouseUp, this);
7142         }
7143     },
7144     // private
7145     onMouseUp : function(e){
7146         if(e.button == 0){
7147             this.el.removeClass("x-btn-click");
7148             Roo.get(document).un('mouseup', this.onMouseUp, this);
7149         }
7150     },
7151     // private
7152     onMenuShow : function(e){
7153         this.el.addClass("x-btn-menu-active");
7154     },
7155     // private
7156     onMenuHide : function(e){
7157         this.el.removeClass("x-btn-menu-active");
7158     }   
7159 });
7160
7161 // Private utility class used by Button
7162 Roo.ButtonToggleMgr = function(){
7163    var groups = {};
7164    
7165    function toggleGroup(btn, state){
7166        if(state){
7167            var g = groups[btn.toggleGroup];
7168            for(var i = 0, l = g.length; i < l; i++){
7169                if(g[i] != btn){
7170                    g[i].toggle(false);
7171                }
7172            }
7173        }
7174    }
7175    
7176    return {
7177        register : function(btn){
7178            if(!btn.toggleGroup){
7179                return;
7180            }
7181            var g = groups[btn.toggleGroup];
7182            if(!g){
7183                g = groups[btn.toggleGroup] = [];
7184            }
7185            g.push(btn);
7186            btn.on("toggle", toggleGroup);
7187        },
7188        
7189        unregister : function(btn){
7190            if(!btn.toggleGroup){
7191                return;
7192            }
7193            var g = groups[btn.toggleGroup];
7194            if(g){
7195                g.remove(btn);
7196                btn.un("toggle", toggleGroup);
7197            }
7198        }
7199    };
7200 }();/*
7201  * Based on:
7202  * Ext JS Library 1.1.1
7203  * Copyright(c) 2006-2007, Ext JS, LLC.
7204  *
7205  * Originally Released Under LGPL - original licence link has changed is not relivant.
7206  *
7207  * Fork - LGPL
7208  * <script type="text/javascript">
7209  */
7210  
7211 /**
7212  * @class Roo.SplitButton
7213  * @extends Roo.Button
7214  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7215  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7216  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7217  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7218  * @cfg {String} arrowTooltip The title attribute of the arrow
7219  * @constructor
7220  * Create a new menu button
7221  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7222  * @param {Object} config The config object
7223  */
7224 Roo.SplitButton = function(renderTo, config){
7225     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7226     /**
7227      * @event arrowclick
7228      * Fires when this button's arrow is clicked
7229      * @param {SplitButton} this
7230      * @param {EventObject} e The click event
7231      */
7232     this.addEvents({"arrowclick":true});
7233 };
7234
7235 Roo.extend(Roo.SplitButton, Roo.Button, {
7236     render : function(renderTo){
7237         // this is one sweet looking template!
7238         var tpl = new Roo.Template(
7239             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7240             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7241             '<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>',
7242             "</tbody></table></td><td>",
7243             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7244             '<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>',
7245             "</tbody></table></td></tr></table>"
7246         );
7247         var btn = tpl.append(renderTo, [this.text, this.type], true);
7248         var btnEl = btn.child("button");
7249         if(this.cls){
7250             btn.addClass(this.cls);
7251         }
7252         if(this.icon){
7253             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7254         }
7255         if(this.iconCls){
7256             btnEl.addClass(this.iconCls);
7257             if(!this.cls){
7258                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7259             }
7260         }
7261         this.el = btn;
7262         if(this.handleMouseEvents){
7263             btn.on("mouseover", this.onMouseOver, this);
7264             btn.on("mouseout", this.onMouseOut, this);
7265             btn.on("mousedown", this.onMouseDown, this);
7266             btn.on("mouseup", this.onMouseUp, this);
7267         }
7268         btn.on(this.clickEvent, this.onClick, this);
7269         if(this.tooltip){
7270             if(typeof this.tooltip == 'object'){
7271                 Roo.QuickTips.tips(Roo.apply({
7272                       target: btnEl.id
7273                 }, this.tooltip));
7274             } else {
7275                 btnEl.dom[this.tooltipType] = this.tooltip;
7276             }
7277         }
7278         if(this.arrowTooltip){
7279             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7280         }
7281         if(this.hidden){
7282             this.hide();
7283         }
7284         if(this.disabled){
7285             this.disable();
7286         }
7287         if(this.pressed){
7288             this.el.addClass("x-btn-pressed");
7289         }
7290         if(Roo.isIE && !Roo.isIE7){
7291             this.autoWidth.defer(1, this);
7292         }else{
7293             this.autoWidth();
7294         }
7295         if(this.menu){
7296             this.menu.on("show", this.onMenuShow, this);
7297             this.menu.on("hide", this.onMenuHide, this);
7298         }
7299         this.fireEvent('render', this);
7300     },
7301
7302     // private
7303     autoWidth : function(){
7304         if(this.el){
7305             var tbl = this.el.child("table:first");
7306             var tbl2 = this.el.child("table:last");
7307             this.el.setWidth("auto");
7308             tbl.setWidth("auto");
7309             if(Roo.isIE7 && Roo.isStrict){
7310                 var ib = this.el.child('button:first');
7311                 if(ib && ib.getWidth() > 20){
7312                     ib.clip();
7313                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7314                 }
7315             }
7316             if(this.minWidth){
7317                 if(this.hidden){
7318                     this.el.beginMeasure();
7319                 }
7320                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7321                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7322                 }
7323                 if(this.hidden){
7324                     this.el.endMeasure();
7325                 }
7326             }
7327             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7328         } 
7329     },
7330     /**
7331      * Sets this button's click handler
7332      * @param {Function} handler The function to call when the button is clicked
7333      * @param {Object} scope (optional) Scope for the function passed above
7334      */
7335     setHandler : function(handler, scope){
7336         this.handler = handler;
7337         this.scope = scope;  
7338     },
7339     
7340     /**
7341      * Sets this button's arrow click handler
7342      * @param {Function} handler The function to call when the arrow is clicked
7343      * @param {Object} scope (optional) Scope for the function passed above
7344      */
7345     setArrowHandler : function(handler, scope){
7346         this.arrowHandler = handler;
7347         this.scope = scope;  
7348     },
7349     
7350     /**
7351      * Focus the button
7352      */
7353     focus : function(){
7354         if(this.el){
7355             this.el.child("button:first").focus();
7356         }
7357     },
7358
7359     // private
7360     onClick : function(e){
7361         e.preventDefault();
7362         if(!this.disabled){
7363             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7364                 if(this.menu && !this.menu.isVisible()){
7365                     this.menu.show(this.el, this.menuAlign);
7366                 }
7367                 this.fireEvent("arrowclick", this, e);
7368                 if(this.arrowHandler){
7369                     this.arrowHandler.call(this.scope || this, this, e);
7370                 }
7371             }else{
7372                 this.fireEvent("click", this, e);
7373                 if(this.handler){
7374                     this.handler.call(this.scope || this, this, e);
7375                 }
7376             }
7377         }
7378     },
7379     // private
7380     onMouseDown : function(e){
7381         if(!this.disabled){
7382             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7383         }
7384     },
7385     // private
7386     onMouseUp : function(e){
7387         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7388     }   
7389 });
7390
7391
7392 // backwards compat
7393 Roo.MenuButton = Roo.SplitButton;/*
7394  * Based on:
7395  * Ext JS Library 1.1.1
7396  * Copyright(c) 2006-2007, Ext JS, LLC.
7397  *
7398  * Originally Released Under LGPL - original licence link has changed is not relivant.
7399  *
7400  * Fork - LGPL
7401  * <script type="text/javascript">
7402  */
7403
7404 /**
7405  * @class Roo.Toolbar
7406  * Basic Toolbar class.
7407  * @constructor
7408  * Creates a new Toolbar
7409  * @param {Object} container The config object
7410  */ 
7411 Roo.Toolbar = function(container, buttons, config)
7412 {
7413     /// old consturctor format still supported..
7414     if(container instanceof Array){ // omit the container for later rendering
7415         buttons = container;
7416         config = buttons;
7417         container = null;
7418     }
7419     if (typeof(container) == 'object' && container.xtype) {
7420         config = container;
7421         container = config.container;
7422         buttons = config.buttons || []; // not really - use items!!
7423     }
7424     var xitems = [];
7425     if (config && config.items) {
7426         xitems = config.items;
7427         delete config.items;
7428     }
7429     Roo.apply(this, config);
7430     this.buttons = buttons;
7431     
7432     if(container){
7433         this.render(container);
7434     }
7435     this.xitems = xitems;
7436     Roo.each(xitems, function(b) {
7437         this.add(b);
7438     }, this);
7439     
7440 };
7441
7442 Roo.Toolbar.prototype = {
7443     /**
7444      * @cfg {Array} items
7445      * array of button configs or elements to add (will be converted to a MixedCollection)
7446      */
7447     
7448     /**
7449      * @cfg {String/HTMLElement/Element} container
7450      * The id or element that will contain the toolbar
7451      */
7452     // private
7453     render : function(ct){
7454         this.el = Roo.get(ct);
7455         if(this.cls){
7456             this.el.addClass(this.cls);
7457         }
7458         // using a table allows for vertical alignment
7459         // 100% width is needed by Safari...
7460         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7461         this.tr = this.el.child("tr", true);
7462         var autoId = 0;
7463         this.items = new Roo.util.MixedCollection(false, function(o){
7464             return o.id || ("item" + (++autoId));
7465         });
7466         if(this.buttons){
7467             this.add.apply(this, this.buttons);
7468             delete this.buttons;
7469         }
7470     },
7471
7472     /**
7473      * Adds element(s) to the toolbar -- this function takes a variable number of 
7474      * arguments of mixed type and adds them to the toolbar.
7475      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7476      * <ul>
7477      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7478      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7479      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7480      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7481      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7482      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7483      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7484      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7485      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7486      * </ul>
7487      * @param {Mixed} arg2
7488      * @param {Mixed} etc.
7489      */
7490     add : function(){
7491         var a = arguments, l = a.length;
7492         for(var i = 0; i < l; i++){
7493             this._add(a[i]);
7494         }
7495     },
7496     // private..
7497     _add : function(el) {
7498         
7499         if (el.xtype) {
7500             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7501         }
7502         
7503         if (el.applyTo){ // some kind of form field
7504             return this.addField(el);
7505         } 
7506         if (el.render){ // some kind of Toolbar.Item
7507             return this.addItem(el);
7508         }
7509         if (typeof el == "string"){ // string
7510             if(el == "separator" || el == "-"){
7511                 return this.addSeparator();
7512             }
7513             if (el == " "){
7514                 return this.addSpacer();
7515             }
7516             if(el == "->"){
7517                 return this.addFill();
7518             }
7519             return this.addText(el);
7520             
7521         }
7522         if(el.tagName){ // element
7523             return this.addElement(el);
7524         }
7525         if(typeof el == "object"){ // must be button config?
7526             return this.addButton(el);
7527         }
7528         // and now what?!?!
7529         return false;
7530         
7531     },
7532     
7533     /**
7534      * Add an Xtype element
7535      * @param {Object} xtype Xtype Object
7536      * @return {Object} created Object
7537      */
7538     addxtype : function(e){
7539         return this.add(e);  
7540     },
7541     
7542     /**
7543      * Returns the Element for this toolbar.
7544      * @return {Roo.Element}
7545      */
7546     getEl : function(){
7547         return this.el;  
7548     },
7549     
7550     /**
7551      * Adds a separator
7552      * @return {Roo.Toolbar.Item} The separator item
7553      */
7554     addSeparator : function(){
7555         return this.addItem(new Roo.Toolbar.Separator());
7556     },
7557
7558     /**
7559      * Adds a spacer element
7560      * @return {Roo.Toolbar.Spacer} The spacer item
7561      */
7562     addSpacer : function(){
7563         return this.addItem(new Roo.Toolbar.Spacer());
7564     },
7565
7566     /**
7567      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7568      * @return {Roo.Toolbar.Fill} The fill item
7569      */
7570     addFill : function(){
7571         return this.addItem(new Roo.Toolbar.Fill());
7572     },
7573
7574     /**
7575      * Adds any standard HTML element to the toolbar
7576      * @param {String/HTMLElement/Element} el The element or id of the element to add
7577      * @return {Roo.Toolbar.Item} The element's item
7578      */
7579     addElement : function(el){
7580         return this.addItem(new Roo.Toolbar.Item(el));
7581     },
7582     /**
7583      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7584      * @type Roo.util.MixedCollection  
7585      */
7586     items : false,
7587      
7588     /**
7589      * Adds any Toolbar.Item or subclass
7590      * @param {Roo.Toolbar.Item} item
7591      * @return {Roo.Toolbar.Item} The item
7592      */
7593     addItem : function(item){
7594         var td = this.nextBlock();
7595         item.render(td);
7596         this.items.add(item);
7597         return item;
7598     },
7599     
7600     /**
7601      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7602      * @param {Object/Array} config A button config or array of configs
7603      * @return {Roo.Toolbar.Button/Array}
7604      */
7605     addButton : function(config){
7606         if(config instanceof Array){
7607             var buttons = [];
7608             for(var i = 0, len = config.length; i < len; i++) {
7609                 buttons.push(this.addButton(config[i]));
7610             }
7611             return buttons;
7612         }
7613         var b = config;
7614         if(!(config instanceof Roo.Toolbar.Button)){
7615             b = config.split ?
7616                 new Roo.Toolbar.SplitButton(config) :
7617                 new Roo.Toolbar.Button(config);
7618         }
7619         var td = this.nextBlock();
7620         b.render(td);
7621         this.items.add(b);
7622         return b;
7623     },
7624     
7625     /**
7626      * Adds text to the toolbar
7627      * @param {String} text The text to add
7628      * @return {Roo.Toolbar.Item} The element's item
7629      */
7630     addText : function(text){
7631         return this.addItem(new Roo.Toolbar.TextItem(text));
7632     },
7633     
7634     /**
7635      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7636      * @param {Number} index The index where the item is to be inserted
7637      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7638      * @return {Roo.Toolbar.Button/Item}
7639      */
7640     insertButton : function(index, item){
7641         if(item instanceof Array){
7642             var buttons = [];
7643             for(var i = 0, len = item.length; i < len; i++) {
7644                buttons.push(this.insertButton(index + i, item[i]));
7645             }
7646             return buttons;
7647         }
7648         if (!(item instanceof Roo.Toolbar.Button)){
7649            item = new Roo.Toolbar.Button(item);
7650         }
7651         var td = document.createElement("td");
7652         this.tr.insertBefore(td, this.tr.childNodes[index]);
7653         item.render(td);
7654         this.items.insert(index, item);
7655         return item;
7656     },
7657     
7658     /**
7659      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7660      * @param {Object} config
7661      * @return {Roo.Toolbar.Item} The element's item
7662      */
7663     addDom : function(config, returnEl){
7664         var td = this.nextBlock();
7665         Roo.DomHelper.overwrite(td, config);
7666         var ti = new Roo.Toolbar.Item(td.firstChild);
7667         ti.render(td);
7668         this.items.add(ti);
7669         return ti;
7670     },
7671
7672     /**
7673      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7674      * @type Roo.util.MixedCollection  
7675      */
7676     fields : false,
7677     
7678     /**
7679      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7680      * Note: the field should not have been rendered yet. For a field that has already been
7681      * rendered, use {@link #addElement}.
7682      * @param {Roo.form.Field} field
7683      * @return {Roo.ToolbarItem}
7684      */
7685      
7686       
7687     addField : function(field) {
7688         if (!this.fields) {
7689             var autoId = 0;
7690             this.fields = new Roo.util.MixedCollection(false, function(o){
7691                 return o.id || ("item" + (++autoId));
7692             });
7693
7694         }
7695         
7696         var td = this.nextBlock();
7697         field.render(td);
7698         var ti = new Roo.Toolbar.Item(td.firstChild);
7699         ti.render(td);
7700         this.items.add(ti);
7701         this.fields.add(field);
7702         return ti;
7703     },
7704     /**
7705      * Hide the toolbar
7706      * @method hide
7707      */
7708      
7709       
7710     hide : function()
7711     {
7712         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7713         this.el.child('div').hide();
7714     },
7715     /**
7716      * Show the toolbar
7717      * @method show
7718      */
7719     show : function()
7720     {
7721         this.el.child('div').show();
7722     },
7723       
7724     // private
7725     nextBlock : function(){
7726         var td = document.createElement("td");
7727         this.tr.appendChild(td);
7728         return td;
7729     },
7730
7731     // private
7732     destroy : function(){
7733         if(this.items){ // rendered?
7734             Roo.destroy.apply(Roo, this.items.items);
7735         }
7736         if(this.fields){ // rendered?
7737             Roo.destroy.apply(Roo, this.fields.items);
7738         }
7739         Roo.Element.uncache(this.el, this.tr);
7740     }
7741 };
7742
7743 /**
7744  * @class Roo.Toolbar.Item
7745  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7746  * @constructor
7747  * Creates a new Item
7748  * @param {HTMLElement} el 
7749  */
7750 Roo.Toolbar.Item = function(el){
7751     var cfg = {};
7752     if (typeof (el.xtype) != 'undefined') {
7753         cfg = el;
7754         el = cfg.el;
7755     }
7756     
7757     this.el = Roo.getDom(el);
7758     this.id = Roo.id(this.el);
7759     this.hidden = false;
7760     
7761     this.addEvents({
7762          /**
7763              * @event render
7764              * Fires when the button is rendered
7765              * @param {Button} this
7766              */
7767         'render': true
7768     });
7769     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7770 };
7771 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7772 //Roo.Toolbar.Item.prototype = {
7773     
7774     /**
7775      * Get this item's HTML Element
7776      * @return {HTMLElement}
7777      */
7778     getEl : function(){
7779        return this.el;  
7780     },
7781
7782     // private
7783     render : function(td){
7784         
7785          this.td = td;
7786         td.appendChild(this.el);
7787         
7788         this.fireEvent('render', this);
7789     },
7790     
7791     /**
7792      * Removes and destroys this item.
7793      */
7794     destroy : function(){
7795         this.td.parentNode.removeChild(this.td);
7796     },
7797     
7798     /**
7799      * Shows this item.
7800      */
7801     show: function(){
7802         this.hidden = false;
7803         this.td.style.display = "";
7804     },
7805     
7806     /**
7807      * Hides this item.
7808      */
7809     hide: function(){
7810         this.hidden = true;
7811         this.td.style.display = "none";
7812     },
7813     
7814     /**
7815      * Convenience function for boolean show/hide.
7816      * @param {Boolean} visible true to show/false to hide
7817      */
7818     setVisible: function(visible){
7819         if(visible) {
7820             this.show();
7821         }else{
7822             this.hide();
7823         }
7824     },
7825     
7826     /**
7827      * Try to focus this item.
7828      */
7829     focus : function(){
7830         Roo.fly(this.el).focus();
7831     },
7832     
7833     /**
7834      * Disables this item.
7835      */
7836     disable : function(){
7837         Roo.fly(this.td).addClass("x-item-disabled");
7838         this.disabled = true;
7839         this.el.disabled = true;
7840     },
7841     
7842     /**
7843      * Enables this item.
7844      */
7845     enable : function(){
7846         Roo.fly(this.td).removeClass("x-item-disabled");
7847         this.disabled = false;
7848         this.el.disabled = false;
7849     }
7850 });
7851
7852
7853 /**
7854  * @class Roo.Toolbar.Separator
7855  * @extends Roo.Toolbar.Item
7856  * A simple toolbar separator class
7857  * @constructor
7858  * Creates a new Separator
7859  */
7860 Roo.Toolbar.Separator = function(cfg){
7861     
7862     var s = document.createElement("span");
7863     s.className = "ytb-sep";
7864     if (cfg) {
7865         cfg.el = s;
7866     }
7867     
7868     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7869 };
7870 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7871     enable:Roo.emptyFn,
7872     disable:Roo.emptyFn,
7873     focus:Roo.emptyFn
7874 });
7875
7876 /**
7877  * @class Roo.Toolbar.Spacer
7878  * @extends Roo.Toolbar.Item
7879  * A simple element that adds extra horizontal space to a toolbar.
7880  * @constructor
7881  * Creates a new Spacer
7882  */
7883 Roo.Toolbar.Spacer = function(cfg){
7884     var s = document.createElement("div");
7885     s.className = "ytb-spacer";
7886     if (cfg) {
7887         cfg.el = s;
7888     }
7889     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7890 };
7891 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7892     enable:Roo.emptyFn,
7893     disable:Roo.emptyFn,
7894     focus:Roo.emptyFn
7895 });
7896
7897 /**
7898  * @class Roo.Toolbar.Fill
7899  * @extends Roo.Toolbar.Spacer
7900  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7901  * @constructor
7902  * Creates a new Spacer
7903  */
7904 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7905     // private
7906     render : function(td){
7907         td.style.width = '100%';
7908         Roo.Toolbar.Fill.superclass.render.call(this, td);
7909     }
7910 });
7911
7912 /**
7913  * @class Roo.Toolbar.TextItem
7914  * @extends Roo.Toolbar.Item
7915  * A simple class that renders text directly into a toolbar.
7916  * @constructor
7917  * Creates a new TextItem
7918  * @param {String} text
7919  */
7920 Roo.Toolbar.TextItem = function(cfg){
7921     var  text = cfg || "";
7922     if (typeof(cfg) == 'object') {
7923         text = cfg.text || "";
7924     }  else {
7925         cfg = null;
7926     }
7927     var s = document.createElement("span");
7928     s.className = "ytb-text";
7929     s.innerHTML = text;
7930     if (cfg) {
7931         cfg.el  = s;
7932     }
7933     
7934     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7935 };
7936 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7937     
7938      
7939     enable:Roo.emptyFn,
7940     disable:Roo.emptyFn,
7941     focus:Roo.emptyFn
7942 });
7943
7944 /**
7945  * @class Roo.Toolbar.Button
7946  * @extends Roo.Button
7947  * A button that renders into a toolbar.
7948  * @constructor
7949  * Creates a new Button
7950  * @param {Object} config A standard {@link Roo.Button} config object
7951  */
7952 Roo.Toolbar.Button = function(config){
7953     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7954 };
7955 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7956     render : function(td){
7957         this.td = td;
7958         Roo.Toolbar.Button.superclass.render.call(this, td);
7959     },
7960     
7961     /**
7962      * Removes and destroys this button
7963      */
7964     destroy : function(){
7965         Roo.Toolbar.Button.superclass.destroy.call(this);
7966         this.td.parentNode.removeChild(this.td);
7967     },
7968     
7969     /**
7970      * Shows this button
7971      */
7972     show: function(){
7973         this.hidden = false;
7974         this.td.style.display = "";
7975     },
7976     
7977     /**
7978      * Hides this button
7979      */
7980     hide: function(){
7981         this.hidden = true;
7982         this.td.style.display = "none";
7983     },
7984
7985     /**
7986      * Disables this item
7987      */
7988     disable : function(){
7989         Roo.fly(this.td).addClass("x-item-disabled");
7990         this.disabled = true;
7991     },
7992
7993     /**
7994      * Enables this item
7995      */
7996     enable : function(){
7997         Roo.fly(this.td).removeClass("x-item-disabled");
7998         this.disabled = false;
7999     }
8000 });
8001 // backwards compat
8002 Roo.ToolbarButton = Roo.Toolbar.Button;
8003
8004 /**
8005  * @class Roo.Toolbar.SplitButton
8006  * @extends Roo.SplitButton
8007  * A menu button that renders into a toolbar.
8008  * @constructor
8009  * Creates a new SplitButton
8010  * @param {Object} config A standard {@link Roo.SplitButton} config object
8011  */
8012 Roo.Toolbar.SplitButton = function(config){
8013     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8014 };
8015 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8016     render : function(td){
8017         this.td = td;
8018         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8019     },
8020     
8021     /**
8022      * Removes and destroys this button
8023      */
8024     destroy : function(){
8025         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8026         this.td.parentNode.removeChild(this.td);
8027     },
8028     
8029     /**
8030      * Shows this button
8031      */
8032     show: function(){
8033         this.hidden = false;
8034         this.td.style.display = "";
8035     },
8036     
8037     /**
8038      * Hides this button
8039      */
8040     hide: function(){
8041         this.hidden = true;
8042         this.td.style.display = "none";
8043     }
8044 });
8045
8046 // backwards compat
8047 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8048  * Based on:
8049  * Ext JS Library 1.1.1
8050  * Copyright(c) 2006-2007, Ext JS, LLC.
8051  *
8052  * Originally Released Under LGPL - original licence link has changed is not relivant.
8053  *
8054  * Fork - LGPL
8055  * <script type="text/javascript">
8056  */
8057  
8058 /**
8059  * @class Roo.PagingToolbar
8060  * @extends Roo.Toolbar
8061  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8062  * @constructor
8063  * Create a new PagingToolbar
8064  * @param {Object} config The config object
8065  */
8066 Roo.PagingToolbar = function(el, ds, config)
8067 {
8068     // old args format still supported... - xtype is prefered..
8069     if (typeof(el) == 'object' && el.xtype) {
8070         // created from xtype...
8071         config = el;
8072         ds = el.dataSource;
8073         el = config.container;
8074     }
8075     var items = [];
8076     if (config.items) {
8077         items = config.items;
8078         config.items = [];
8079     }
8080     
8081     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8082     this.ds = ds;
8083     this.cursor = 0;
8084     this.renderButtons(this.el);
8085     this.bind(ds);
8086     
8087     // supprot items array.
8088    
8089     Roo.each(items, function(e) {
8090         this.add(Roo.factory(e));
8091     },this);
8092     
8093 };
8094
8095 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8096     /**
8097      * @cfg {Roo.data.Store} dataSource
8098      * The underlying data store providing the paged data
8099      */
8100     /**
8101      * @cfg {String/HTMLElement/Element} container
8102      * container The id or element that will contain the toolbar
8103      */
8104     /**
8105      * @cfg {Boolean} displayInfo
8106      * True to display the displayMsg (defaults to false)
8107      */
8108     /**
8109      * @cfg {Number} pageSize
8110      * The number of records to display per page (defaults to 20)
8111      */
8112     pageSize: 20,
8113     /**
8114      * @cfg {String} displayMsg
8115      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8116      */
8117     displayMsg : 'Displaying {0} - {1} of {2}',
8118     /**
8119      * @cfg {String} emptyMsg
8120      * The message to display when no records are found (defaults to "No data to display")
8121      */
8122     emptyMsg : 'No data to display',
8123     /**
8124      * Customizable piece of the default paging text (defaults to "Page")
8125      * @type String
8126      */
8127     beforePageText : "Page",
8128     /**
8129      * Customizable piece of the default paging text (defaults to "of %0")
8130      * @type String
8131      */
8132     afterPageText : "of {0}",
8133     /**
8134      * Customizable piece of the default paging text (defaults to "First Page")
8135      * @type String
8136      */
8137     firstText : "First Page",
8138     /**
8139      * Customizable piece of the default paging text (defaults to "Previous Page")
8140      * @type String
8141      */
8142     prevText : "Previous Page",
8143     /**
8144      * Customizable piece of the default paging text (defaults to "Next Page")
8145      * @type String
8146      */
8147     nextText : "Next Page",
8148     /**
8149      * Customizable piece of the default paging text (defaults to "Last Page")
8150      * @type String
8151      */
8152     lastText : "Last Page",
8153     /**
8154      * Customizable piece of the default paging text (defaults to "Refresh")
8155      * @type String
8156      */
8157     refreshText : "Refresh",
8158
8159     // private
8160     renderButtons : function(el){
8161         Roo.PagingToolbar.superclass.render.call(this, el);
8162         this.first = this.addButton({
8163             tooltip: this.firstText,
8164             cls: "x-btn-icon x-grid-page-first",
8165             disabled: true,
8166             handler: this.onClick.createDelegate(this, ["first"])
8167         });
8168         this.prev = this.addButton({
8169             tooltip: this.prevText,
8170             cls: "x-btn-icon x-grid-page-prev",
8171             disabled: true,
8172             handler: this.onClick.createDelegate(this, ["prev"])
8173         });
8174         //this.addSeparator();
8175         this.add(this.beforePageText);
8176         this.field = Roo.get(this.addDom({
8177            tag: "input",
8178            type: "text",
8179            size: "3",
8180            value: "1",
8181            cls: "x-grid-page-number"
8182         }).el);
8183         this.field.on("keydown", this.onPagingKeydown, this);
8184         this.field.on("focus", function(){this.dom.select();});
8185         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8186         this.field.setHeight(18);
8187         //this.addSeparator();
8188         this.next = this.addButton({
8189             tooltip: this.nextText,
8190             cls: "x-btn-icon x-grid-page-next",
8191             disabled: true,
8192             handler: this.onClick.createDelegate(this, ["next"])
8193         });
8194         this.last = this.addButton({
8195             tooltip: this.lastText,
8196             cls: "x-btn-icon x-grid-page-last",
8197             disabled: true,
8198             handler: this.onClick.createDelegate(this, ["last"])
8199         });
8200         //this.addSeparator();
8201         this.loading = this.addButton({
8202             tooltip: this.refreshText,
8203             cls: "x-btn-icon x-grid-loading",
8204             handler: this.onClick.createDelegate(this, ["refresh"])
8205         });
8206
8207         if(this.displayInfo){
8208             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8209         }
8210     },
8211
8212     // private
8213     updateInfo : function(){
8214         if(this.displayEl){
8215             var count = this.ds.getCount();
8216             var msg = count == 0 ?
8217                 this.emptyMsg :
8218                 String.format(
8219                     this.displayMsg,
8220                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8221                 );
8222             this.displayEl.update(msg);
8223         }
8224     },
8225
8226     // private
8227     onLoad : function(ds, r, o){
8228        this.cursor = o.params ? o.params.start : 0;
8229        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8230
8231        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8232        this.field.dom.value = ap;
8233        this.first.setDisabled(ap == 1);
8234        this.prev.setDisabled(ap == 1);
8235        this.next.setDisabled(ap == ps);
8236        this.last.setDisabled(ap == ps);
8237        this.loading.enable();
8238        this.updateInfo();
8239     },
8240
8241     // private
8242     getPageData : function(){
8243         var total = this.ds.getTotalCount();
8244         return {
8245             total : total,
8246             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8247             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8248         };
8249     },
8250
8251     // private
8252     onLoadError : function(){
8253         this.loading.enable();
8254     },
8255
8256     // private
8257     onPagingKeydown : function(e){
8258         var k = e.getKey();
8259         var d = this.getPageData();
8260         if(k == e.RETURN){
8261             var v = this.field.dom.value, pageNum;
8262             if(!v || isNaN(pageNum = parseInt(v, 10))){
8263                 this.field.dom.value = d.activePage;
8264                 return;
8265             }
8266             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8267             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8268             e.stopEvent();
8269         }
8270         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))
8271         {
8272           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8273           this.field.dom.value = pageNum;
8274           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8275           e.stopEvent();
8276         }
8277         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8278         {
8279           var v = this.field.dom.value, pageNum; 
8280           var increment = (e.shiftKey) ? 10 : 1;
8281           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8282             increment *= -1;
8283           }
8284           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8285             this.field.dom.value = d.activePage;
8286             return;
8287           }
8288           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8289           {
8290             this.field.dom.value = parseInt(v, 10) + increment;
8291             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8292             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8293           }
8294           e.stopEvent();
8295         }
8296     },
8297
8298     // private
8299     beforeLoad : function(){
8300         if(this.loading){
8301             this.loading.disable();
8302         }
8303     },
8304
8305     // private
8306     onClick : function(which){
8307         var ds = this.ds;
8308         switch(which){
8309             case "first":
8310                 ds.load({params:{start: 0, limit: this.pageSize}});
8311             break;
8312             case "prev":
8313                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8314             break;
8315             case "next":
8316                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8317             break;
8318             case "last":
8319                 var total = ds.getTotalCount();
8320                 var extra = total % this.pageSize;
8321                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8322                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8323             break;
8324             case "refresh":
8325                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8326             break;
8327         }
8328     },
8329
8330     /**
8331      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8332      * @param {Roo.data.Store} store The data store to unbind
8333      */
8334     unbind : function(ds){
8335         ds.un("beforeload", this.beforeLoad, this);
8336         ds.un("load", this.onLoad, this);
8337         ds.un("loadexception", this.onLoadError, this);
8338         ds.un("remove", this.updateInfo, this);
8339         ds.un("add", this.updateInfo, this);
8340         this.ds = undefined;
8341     },
8342
8343     /**
8344      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8345      * @param {Roo.data.Store} store The data store to bind
8346      */
8347     bind : function(ds){
8348         ds.on("beforeload", this.beforeLoad, this);
8349         ds.on("load", this.onLoad, this);
8350         ds.on("loadexception", this.onLoadError, this);
8351         ds.on("remove", this.updateInfo, this);
8352         ds.on("add", this.updateInfo, this);
8353         this.ds = ds;
8354     }
8355 });/*
8356  * Based on:
8357  * Ext JS Library 1.1.1
8358  * Copyright(c) 2006-2007, Ext JS, LLC.
8359  *
8360  * Originally Released Under LGPL - original licence link has changed is not relivant.
8361  *
8362  * Fork - LGPL
8363  * <script type="text/javascript">
8364  */
8365
8366 /**
8367  * @class Roo.Resizable
8368  * @extends Roo.util.Observable
8369  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8370  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8371  * 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
8372  * the element will be wrapped for you automatically.</p>
8373  * <p>Here is the list of valid resize handles:</p>
8374  * <pre>
8375 Value   Description
8376 ------  -------------------
8377  'n'     north
8378  's'     south
8379  'e'     east
8380  'w'     west
8381  'nw'    northwest
8382  'sw'    southwest
8383  'se'    southeast
8384  'ne'    northeast
8385  'hd'    horizontal drag
8386  'all'   all
8387 </pre>
8388  * <p>Here's an example showing the creation of a typical Resizable:</p>
8389  * <pre><code>
8390 var resizer = new Roo.Resizable("element-id", {
8391     handles: 'all',
8392     minWidth: 200,
8393     minHeight: 100,
8394     maxWidth: 500,
8395     maxHeight: 400,
8396     pinned: true
8397 });
8398 resizer.on("resize", myHandler);
8399 </code></pre>
8400  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8401  * resizer.east.setDisplayed(false);</p>
8402  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8403  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8404  * resize operation's new size (defaults to [0, 0])
8405  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8406  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8407  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8408  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8409  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8410  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8411  * @cfg {Number} width The width of the element in pixels (defaults to null)
8412  * @cfg {Number} height The height of the element in pixels (defaults to null)
8413  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8414  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8415  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8416  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8417  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8418  * in favor of the handles config option (defaults to false)
8419  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8420  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8421  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8422  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8423  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8424  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8425  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8426  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8427  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8428  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8429  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8430  * @constructor
8431  * Create a new resizable component
8432  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8433  * @param {Object} config configuration options
8434   */
8435 Roo.Resizable = function(el, config)
8436 {
8437     this.el = Roo.get(el);
8438
8439     if(config && config.wrap){
8440         config.resizeChild = this.el;
8441         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8442         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8443         this.el.setStyle("overflow", "hidden");
8444         this.el.setPositioning(config.resizeChild.getPositioning());
8445         config.resizeChild.clearPositioning();
8446         if(!config.width || !config.height){
8447             var csize = config.resizeChild.getSize();
8448             this.el.setSize(csize.width, csize.height);
8449         }
8450         if(config.pinned && !config.adjustments){
8451             config.adjustments = "auto";
8452         }
8453     }
8454
8455     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8456     this.proxy.unselectable();
8457     this.proxy.enableDisplayMode('block');
8458
8459     Roo.apply(this, config);
8460
8461     if(this.pinned){
8462         this.disableTrackOver = true;
8463         this.el.addClass("x-resizable-pinned");
8464     }
8465     // if the element isn't positioned, make it relative
8466     var position = this.el.getStyle("position");
8467     if(position != "absolute" && position != "fixed"){
8468         this.el.setStyle("position", "relative");
8469     }
8470     if(!this.handles){ // no handles passed, must be legacy style
8471         this.handles = 's,e,se';
8472         if(this.multiDirectional){
8473             this.handles += ',n,w';
8474         }
8475     }
8476     if(this.handles == "all"){
8477         this.handles = "n s e w ne nw se sw";
8478     }
8479     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8480     var ps = Roo.Resizable.positions;
8481     for(var i = 0, len = hs.length; i < len; i++){
8482         if(hs[i] && ps[hs[i]]){
8483             var pos = ps[hs[i]];
8484             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8485         }
8486     }
8487     // legacy
8488     this.corner = this.southeast;
8489     
8490     // updateBox = the box can move..
8491     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8492         this.updateBox = true;
8493     }
8494
8495     this.activeHandle = null;
8496
8497     if(this.resizeChild){
8498         if(typeof this.resizeChild == "boolean"){
8499             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8500         }else{
8501             this.resizeChild = Roo.get(this.resizeChild, true);
8502         }
8503     }
8504     
8505     if(this.adjustments == "auto"){
8506         var rc = this.resizeChild;
8507         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8508         if(rc && (hw || hn)){
8509             rc.position("relative");
8510             rc.setLeft(hw ? hw.el.getWidth() : 0);
8511             rc.setTop(hn ? hn.el.getHeight() : 0);
8512         }
8513         this.adjustments = [
8514             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8515             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8516         ];
8517     }
8518
8519     if(this.draggable){
8520         this.dd = this.dynamic ?
8521             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8522         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8523     }
8524
8525     // public events
8526     this.addEvents({
8527         /**
8528          * @event beforeresize
8529          * Fired before resize is allowed. Set enabled to false to cancel resize.
8530          * @param {Roo.Resizable} this
8531          * @param {Roo.EventObject} e The mousedown event
8532          */
8533         "beforeresize" : true,
8534         /**
8535          * @event resizing
8536          * Fired a resizing.
8537          * @param {Roo.Resizable} this
8538          * @param {Number} x The new x position
8539          * @param {Number} y The new y position
8540          * @param {Number} w The new w width
8541          * @param {Number} h The new h hight
8542          * @param {Roo.EventObject} e The mouseup event
8543          */
8544         "resizing" : true,
8545         /**
8546          * @event resize
8547          * Fired after a resize.
8548          * @param {Roo.Resizable} this
8549          * @param {Number} width The new width
8550          * @param {Number} height The new height
8551          * @param {Roo.EventObject} e The mouseup event
8552          */
8553         "resize" : true
8554     });
8555
8556     if(this.width !== null && this.height !== null){
8557         this.resizeTo(this.width, this.height);
8558     }else{
8559         this.updateChildSize();
8560     }
8561     if(Roo.isIE){
8562         this.el.dom.style.zoom = 1;
8563     }
8564     Roo.Resizable.superclass.constructor.call(this);
8565 };
8566
8567 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8568         resizeChild : false,
8569         adjustments : [0, 0],
8570         minWidth : 5,
8571         minHeight : 5,
8572         maxWidth : 10000,
8573         maxHeight : 10000,
8574         enabled : true,
8575         animate : false,
8576         duration : .35,
8577         dynamic : false,
8578         handles : false,
8579         multiDirectional : false,
8580         disableTrackOver : false,
8581         easing : 'easeOutStrong',
8582         widthIncrement : 0,
8583         heightIncrement : 0,
8584         pinned : false,
8585         width : null,
8586         height : null,
8587         preserveRatio : false,
8588         transparent: false,
8589         minX: 0,
8590         minY: 0,
8591         draggable: false,
8592
8593         /**
8594          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8595          */
8596         constrainTo: undefined,
8597         /**
8598          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8599          */
8600         resizeRegion: undefined,
8601
8602
8603     /**
8604      * Perform a manual resize
8605      * @param {Number} width
8606      * @param {Number} height
8607      */
8608     resizeTo : function(width, height){
8609         this.el.setSize(width, height);
8610         this.updateChildSize();
8611         this.fireEvent("resize", this, width, height, null);
8612     },
8613
8614     // private
8615     startSizing : function(e, handle){
8616         this.fireEvent("beforeresize", this, e);
8617         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8618
8619             if(!this.overlay){
8620                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8621                 this.overlay.unselectable();
8622                 this.overlay.enableDisplayMode("block");
8623                 this.overlay.on("mousemove", this.onMouseMove, this);
8624                 this.overlay.on("mouseup", this.onMouseUp, this);
8625             }
8626             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8627
8628             this.resizing = true;
8629             this.startBox = this.el.getBox();
8630             this.startPoint = e.getXY();
8631             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8632                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8633
8634             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8635             this.overlay.show();
8636
8637             if(this.constrainTo) {
8638                 var ct = Roo.get(this.constrainTo);
8639                 this.resizeRegion = ct.getRegion().adjust(
8640                     ct.getFrameWidth('t'),
8641                     ct.getFrameWidth('l'),
8642                     -ct.getFrameWidth('b'),
8643                     -ct.getFrameWidth('r')
8644                 );
8645             }
8646
8647             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8648             this.proxy.show();
8649             this.proxy.setBox(this.startBox);
8650             if(!this.dynamic){
8651                 this.proxy.setStyle('visibility', 'visible');
8652             }
8653         }
8654     },
8655
8656     // private
8657     onMouseDown : function(handle, e){
8658         if(this.enabled){
8659             e.stopEvent();
8660             this.activeHandle = handle;
8661             this.startSizing(e, handle);
8662         }
8663     },
8664
8665     // private
8666     onMouseUp : function(e){
8667         var size = this.resizeElement();
8668         this.resizing = false;
8669         this.handleOut();
8670         this.overlay.hide();
8671         this.proxy.hide();
8672         this.fireEvent("resize", this, size.width, size.height, e);
8673     },
8674
8675     // private
8676     updateChildSize : function(){
8677         
8678         if(this.resizeChild){
8679             var el = this.el;
8680             var child = this.resizeChild;
8681             var adj = this.adjustments;
8682             if(el.dom.offsetWidth){
8683                 var b = el.getSize(true);
8684                 child.setSize(b.width+adj[0], b.height+adj[1]);
8685             }
8686             // Second call here for IE
8687             // The first call enables instant resizing and
8688             // the second call corrects scroll bars if they
8689             // exist
8690             if(Roo.isIE){
8691                 setTimeout(function(){
8692                     if(el.dom.offsetWidth){
8693                         var b = el.getSize(true);
8694                         child.setSize(b.width+adj[0], b.height+adj[1]);
8695                     }
8696                 }, 10);
8697             }
8698         }
8699     },
8700
8701     // private
8702     snap : function(value, inc, min){
8703         if(!inc || !value) {
8704             return value;
8705         }
8706         var newValue = value;
8707         var m = value % inc;
8708         if(m > 0){
8709             if(m > (inc/2)){
8710                 newValue = value + (inc-m);
8711             }else{
8712                 newValue = value - m;
8713             }
8714         }
8715         return Math.max(min, newValue);
8716     },
8717
8718     // private
8719     resizeElement : function(){
8720         var box = this.proxy.getBox();
8721         if(this.updateBox){
8722             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8723         }else{
8724             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8725         }
8726         this.updateChildSize();
8727         if(!this.dynamic){
8728             this.proxy.hide();
8729         }
8730         return box;
8731     },
8732
8733     // private
8734     constrain : function(v, diff, m, mx){
8735         if(v - diff < m){
8736             diff = v - m;
8737         }else if(v - diff > mx){
8738             diff = mx - v;
8739         }
8740         return diff;
8741     },
8742
8743     // private
8744     onMouseMove : function(e){
8745         
8746         if(this.enabled){
8747             try{// try catch so if something goes wrong the user doesn't get hung
8748
8749             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8750                 return;
8751             }
8752
8753             //var curXY = this.startPoint;
8754             var curSize = this.curSize || this.startBox;
8755             var x = this.startBox.x, y = this.startBox.y;
8756             var ox = x, oy = y;
8757             var w = curSize.width, h = curSize.height;
8758             var ow = w, oh = h;
8759             var mw = this.minWidth, mh = this.minHeight;
8760             var mxw = this.maxWidth, mxh = this.maxHeight;
8761             var wi = this.widthIncrement;
8762             var hi = this.heightIncrement;
8763
8764             var eventXY = e.getXY();
8765             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8766             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8767
8768             var pos = this.activeHandle.position;
8769
8770             switch(pos){
8771                 case "east":
8772                     w += diffX;
8773                     w = Math.min(Math.max(mw, w), mxw);
8774                     break;
8775              
8776                 case "south":
8777                     h += diffY;
8778                     h = Math.min(Math.max(mh, h), mxh);
8779                     break;
8780                 case "southeast":
8781                     w += diffX;
8782                     h += diffY;
8783                     w = Math.min(Math.max(mw, w), mxw);
8784                     h = Math.min(Math.max(mh, h), mxh);
8785                     break;
8786                 case "north":
8787                     diffY = this.constrain(h, diffY, mh, mxh);
8788                     y += diffY;
8789                     h -= diffY;
8790                     break;
8791                 case "hdrag":
8792                     
8793                     if (wi) {
8794                         var adiffX = Math.abs(diffX);
8795                         var sub = (adiffX % wi); // how much 
8796                         if (sub > (wi/2)) { // far enough to snap
8797                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8798                         } else {
8799                             // remove difference.. 
8800                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8801                         }
8802                     }
8803                     x += diffX;
8804                     x = Math.max(this.minX, x);
8805                     break;
8806                 case "west":
8807                     diffX = this.constrain(w, diffX, mw, mxw);
8808                     x += diffX;
8809                     w -= diffX;
8810                     break;
8811                 case "northeast":
8812                     w += diffX;
8813                     w = Math.min(Math.max(mw, w), mxw);
8814                     diffY = this.constrain(h, diffY, mh, mxh);
8815                     y += diffY;
8816                     h -= diffY;
8817                     break;
8818                 case "northwest":
8819                     diffX = this.constrain(w, diffX, mw, mxw);
8820                     diffY = this.constrain(h, diffY, mh, mxh);
8821                     y += diffY;
8822                     h -= diffY;
8823                     x += diffX;
8824                     w -= diffX;
8825                     break;
8826                case "southwest":
8827                     diffX = this.constrain(w, diffX, mw, mxw);
8828                     h += diffY;
8829                     h = Math.min(Math.max(mh, h), mxh);
8830                     x += diffX;
8831                     w -= diffX;
8832                     break;
8833             }
8834
8835             var sw = this.snap(w, wi, mw);
8836             var sh = this.snap(h, hi, mh);
8837             if(sw != w || sh != h){
8838                 switch(pos){
8839                     case "northeast":
8840                         y -= sh - h;
8841                     break;
8842                     case "north":
8843                         y -= sh - h;
8844                         break;
8845                     case "southwest":
8846                         x -= sw - w;
8847                     break;
8848                     case "west":
8849                         x -= sw - w;
8850                         break;
8851                     case "northwest":
8852                         x -= sw - w;
8853                         y -= sh - h;
8854                     break;
8855                 }
8856                 w = sw;
8857                 h = sh;
8858             }
8859
8860             if(this.preserveRatio){
8861                 switch(pos){
8862                     case "southeast":
8863                     case "east":
8864                         h = oh * (w/ow);
8865                         h = Math.min(Math.max(mh, h), mxh);
8866                         w = ow * (h/oh);
8867                        break;
8868                     case "south":
8869                         w = ow * (h/oh);
8870                         w = Math.min(Math.max(mw, w), mxw);
8871                         h = oh * (w/ow);
8872                         break;
8873                     case "northeast":
8874                         w = ow * (h/oh);
8875                         w = Math.min(Math.max(mw, w), mxw);
8876                         h = oh * (w/ow);
8877                     break;
8878                     case "north":
8879                         var tw = w;
8880                         w = ow * (h/oh);
8881                         w = Math.min(Math.max(mw, w), mxw);
8882                         h = oh * (w/ow);
8883                         x += (tw - w) / 2;
8884                         break;
8885                     case "southwest":
8886                         h = oh * (w/ow);
8887                         h = Math.min(Math.max(mh, h), mxh);
8888                         var tw = w;
8889                         w = ow * (h/oh);
8890                         x += tw - w;
8891                         break;
8892                     case "west":
8893                         var th = h;
8894                         h = oh * (w/ow);
8895                         h = Math.min(Math.max(mh, h), mxh);
8896                         y += (th - h) / 2;
8897                         var tw = w;
8898                         w = ow * (h/oh);
8899                         x += tw - w;
8900                        break;
8901                     case "northwest":
8902                         var tw = w;
8903                         var th = h;
8904                         h = oh * (w/ow);
8905                         h = Math.min(Math.max(mh, h), mxh);
8906                         w = ow * (h/oh);
8907                         y += th - h;
8908                         x += tw - w;
8909                        break;
8910
8911                 }
8912             }
8913             if (pos == 'hdrag') {
8914                 w = ow;
8915             }
8916             this.proxy.setBounds(x, y, w, h);
8917             if(this.dynamic){
8918                 this.resizeElement();
8919             }
8920             }catch(e){}
8921         }
8922         this.fireEvent("resizing", this, x, y, w, h, e);
8923     },
8924
8925     // private
8926     handleOver : function(){
8927         if(this.enabled){
8928             this.el.addClass("x-resizable-over");
8929         }
8930     },
8931
8932     // private
8933     handleOut : function(){
8934         if(!this.resizing){
8935             this.el.removeClass("x-resizable-over");
8936         }
8937     },
8938
8939     /**
8940      * Returns the element this component is bound to.
8941      * @return {Roo.Element}
8942      */
8943     getEl : function(){
8944         return this.el;
8945     },
8946
8947     /**
8948      * Returns the resizeChild element (or null).
8949      * @return {Roo.Element}
8950      */
8951     getResizeChild : function(){
8952         return this.resizeChild;
8953     },
8954     groupHandler : function()
8955     {
8956         
8957     },
8958     /**
8959      * Destroys this resizable. If the element was wrapped and
8960      * removeEl is not true then the element remains.
8961      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8962      */
8963     destroy : function(removeEl){
8964         this.proxy.remove();
8965         if(this.overlay){
8966             this.overlay.removeAllListeners();
8967             this.overlay.remove();
8968         }
8969         var ps = Roo.Resizable.positions;
8970         for(var k in ps){
8971             if(typeof ps[k] != "function" && this[ps[k]]){
8972                 var h = this[ps[k]];
8973                 h.el.removeAllListeners();
8974                 h.el.remove();
8975             }
8976         }
8977         if(removeEl){
8978             this.el.update("");
8979             this.el.remove();
8980         }
8981     }
8982 });
8983
8984 // private
8985 // hash to map config positions to true positions
8986 Roo.Resizable.positions = {
8987     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8988     hd: "hdrag"
8989 };
8990
8991 // private
8992 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8993     if(!this.tpl){
8994         // only initialize the template if resizable is used
8995         var tpl = Roo.DomHelper.createTemplate(
8996             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8997         );
8998         tpl.compile();
8999         Roo.Resizable.Handle.prototype.tpl = tpl;
9000     }
9001     this.position = pos;
9002     this.rz = rz;
9003     // show north drag fro topdra
9004     var handlepos = pos == 'hdrag' ? 'north' : pos;
9005     
9006     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9007     if (pos == 'hdrag') {
9008         this.el.setStyle('cursor', 'pointer');
9009     }
9010     this.el.unselectable();
9011     if(transparent){
9012         this.el.setOpacity(0);
9013     }
9014     this.el.on("mousedown", this.onMouseDown, this);
9015     if(!disableTrackOver){
9016         this.el.on("mouseover", this.onMouseOver, this);
9017         this.el.on("mouseout", this.onMouseOut, this);
9018     }
9019 };
9020
9021 // private
9022 Roo.Resizable.Handle.prototype = {
9023     afterResize : function(rz){
9024         Roo.log('after?');
9025         // do nothing
9026     },
9027     // private
9028     onMouseDown : function(e){
9029         this.rz.onMouseDown(this, e);
9030     },
9031     // private
9032     onMouseOver : function(e){
9033         this.rz.handleOver(this, e);
9034     },
9035     // private
9036     onMouseOut : function(e){
9037         this.rz.handleOut(this, e);
9038     }
9039 };/*
9040  * Based on:
9041  * Ext JS Library 1.1.1
9042  * Copyright(c) 2006-2007, Ext JS, LLC.
9043  *
9044  * Originally Released Under LGPL - original licence link has changed is not relivant.
9045  *
9046  * Fork - LGPL
9047  * <script type="text/javascript">
9048  */
9049
9050 /**
9051  * @class Roo.Editor
9052  * @extends Roo.Component
9053  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9054  * @constructor
9055  * Create a new Editor
9056  * @param {Roo.form.Field} field The Field object (or descendant)
9057  * @param {Object} config The config object
9058  */
9059 Roo.Editor = function(field, config){
9060     Roo.Editor.superclass.constructor.call(this, config);
9061     this.field = field;
9062     this.addEvents({
9063         /**
9064              * @event beforestartedit
9065              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9066              * false from the handler of this event.
9067              * @param {Editor} this
9068              * @param {Roo.Element} boundEl The underlying element bound to this editor
9069              * @param {Mixed} value The field value being set
9070              */
9071         "beforestartedit" : true,
9072         /**
9073              * @event startedit
9074              * Fires when this editor is displayed
9075              * @param {Roo.Element} boundEl The underlying element bound to this editor
9076              * @param {Mixed} value The starting field value
9077              */
9078         "startedit" : true,
9079         /**
9080              * @event beforecomplete
9081              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9082              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9083              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9084              * event will not fire since no edit actually occurred.
9085              * @param {Editor} this
9086              * @param {Mixed} value The current field value
9087              * @param {Mixed} startValue The original field value
9088              */
9089         "beforecomplete" : true,
9090         /**
9091              * @event complete
9092              * Fires after editing is complete and any changed value has been written to the underlying field.
9093              * @param {Editor} this
9094              * @param {Mixed} value The current field value
9095              * @param {Mixed} startValue The original field value
9096              */
9097         "complete" : true,
9098         /**
9099          * @event specialkey
9100          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9101          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9102          * @param {Roo.form.Field} this
9103          * @param {Roo.EventObject} e The event object
9104          */
9105         "specialkey" : true
9106     });
9107 };
9108
9109 Roo.extend(Roo.Editor, Roo.Component, {
9110     /**
9111      * @cfg {Boolean/String} autosize
9112      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9113      * or "height" to adopt the height only (defaults to false)
9114      */
9115     /**
9116      * @cfg {Boolean} revertInvalid
9117      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9118      * validation fails (defaults to true)
9119      */
9120     /**
9121      * @cfg {Boolean} ignoreNoChange
9122      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9123      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9124      * will never be ignored.
9125      */
9126     /**
9127      * @cfg {Boolean} hideEl
9128      * False to keep the bound element visible while the editor is displayed (defaults to true)
9129      */
9130     /**
9131      * @cfg {Mixed} value
9132      * The data value of the underlying field (defaults to "")
9133      */
9134     value : "",
9135     /**
9136      * @cfg {String} alignment
9137      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9138      */
9139     alignment: "c-c?",
9140     /**
9141      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9142      * for bottom-right shadow (defaults to "frame")
9143      */
9144     shadow : "frame",
9145     /**
9146      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9147      */
9148     constrain : false,
9149     /**
9150      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9151      */
9152     completeOnEnter : false,
9153     /**
9154      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9155      */
9156     cancelOnEsc : false,
9157     /**
9158      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9159      */
9160     updateEl : false,
9161
9162     // private
9163     onRender : function(ct, position){
9164         this.el = new Roo.Layer({
9165             shadow: this.shadow,
9166             cls: "x-editor",
9167             parentEl : ct,
9168             shim : this.shim,
9169             shadowOffset:4,
9170             id: this.id,
9171             constrain: this.constrain
9172         });
9173         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9174         if(this.field.msgTarget != 'title'){
9175             this.field.msgTarget = 'qtip';
9176         }
9177         this.field.render(this.el);
9178         if(Roo.isGecko){
9179             this.field.el.dom.setAttribute('autocomplete', 'off');
9180         }
9181         this.field.on("specialkey", this.onSpecialKey, this);
9182         if(this.swallowKeys){
9183             this.field.el.swallowEvent(['keydown','keypress']);
9184         }
9185         this.field.show();
9186         this.field.on("blur", this.onBlur, this);
9187         if(this.field.grow){
9188             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9189         }
9190     },
9191
9192     onSpecialKey : function(field, e)
9193     {
9194         //Roo.log('editor onSpecialKey');
9195         if(this.completeOnEnter && e.getKey() == e.ENTER){
9196             e.stopEvent();
9197             this.completeEdit();
9198             return;
9199         }
9200         // do not fire special key otherwise it might hide close the editor...
9201         if(e.getKey() == e.ENTER){    
9202             return;
9203         }
9204         if(this.cancelOnEsc && e.getKey() == e.ESC){
9205             this.cancelEdit();
9206             return;
9207         } 
9208         this.fireEvent('specialkey', field, e);
9209     
9210     },
9211
9212     /**
9213      * Starts the editing process and shows the editor.
9214      * @param {String/HTMLElement/Element} el The element to edit
9215      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9216       * to the innerHTML of el.
9217      */
9218     startEdit : function(el, value){
9219         if(this.editing){
9220             this.completeEdit();
9221         }
9222         this.boundEl = Roo.get(el);
9223         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9224         if(!this.rendered){
9225             this.render(this.parentEl || document.body);
9226         }
9227         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9228             return;
9229         }
9230         this.startValue = v;
9231         this.field.setValue(v);
9232         if(this.autoSize){
9233             var sz = this.boundEl.getSize();
9234             switch(this.autoSize){
9235                 case "width":
9236                 this.setSize(sz.width,  "");
9237                 break;
9238                 case "height":
9239                 this.setSize("",  sz.height);
9240                 break;
9241                 default:
9242                 this.setSize(sz.width,  sz.height);
9243             }
9244         }
9245         this.el.alignTo(this.boundEl, this.alignment);
9246         this.editing = true;
9247         if(Roo.QuickTips){
9248             Roo.QuickTips.disable();
9249         }
9250         this.show();
9251     },
9252
9253     /**
9254      * Sets the height and width of this editor.
9255      * @param {Number} width The new width
9256      * @param {Number} height The new height
9257      */
9258     setSize : function(w, h){
9259         this.field.setSize(w, h);
9260         if(this.el){
9261             this.el.sync();
9262         }
9263     },
9264
9265     /**
9266      * Realigns the editor to the bound field based on the current alignment config value.
9267      */
9268     realign : function(){
9269         this.el.alignTo(this.boundEl, this.alignment);
9270     },
9271
9272     /**
9273      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9274      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9275      */
9276     completeEdit : function(remainVisible){
9277         if(!this.editing){
9278             return;
9279         }
9280         var v = this.getValue();
9281         if(this.revertInvalid !== false && !this.field.isValid()){
9282             v = this.startValue;
9283             this.cancelEdit(true);
9284         }
9285         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9286             this.editing = false;
9287             this.hide();
9288             return;
9289         }
9290         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9291             this.editing = false;
9292             if(this.updateEl && this.boundEl){
9293                 this.boundEl.update(v);
9294             }
9295             if(remainVisible !== true){
9296                 this.hide();
9297             }
9298             this.fireEvent("complete", this, v, this.startValue);
9299         }
9300     },
9301
9302     // private
9303     onShow : function(){
9304         this.el.show();
9305         if(this.hideEl !== false){
9306             this.boundEl.hide();
9307         }
9308         this.field.show();
9309         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9310             this.fixIEFocus = true;
9311             this.deferredFocus.defer(50, this);
9312         }else{
9313             this.field.focus();
9314         }
9315         this.fireEvent("startedit", this.boundEl, this.startValue);
9316     },
9317
9318     deferredFocus : function(){
9319         if(this.editing){
9320             this.field.focus();
9321         }
9322     },
9323
9324     /**
9325      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9326      * reverted to the original starting value.
9327      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9328      * cancel (defaults to false)
9329      */
9330     cancelEdit : function(remainVisible){
9331         if(this.editing){
9332             this.setValue(this.startValue);
9333             if(remainVisible !== true){
9334                 this.hide();
9335             }
9336         }
9337     },
9338
9339     // private
9340     onBlur : function(){
9341         if(this.allowBlur !== true && this.editing){
9342             this.completeEdit();
9343         }
9344     },
9345
9346     // private
9347     onHide : function(){
9348         if(this.editing){
9349             this.completeEdit();
9350             return;
9351         }
9352         this.field.blur();
9353         if(this.field.collapse){
9354             this.field.collapse();
9355         }
9356         this.el.hide();
9357         if(this.hideEl !== false){
9358             this.boundEl.show();
9359         }
9360         if(Roo.QuickTips){
9361             Roo.QuickTips.enable();
9362         }
9363     },
9364
9365     /**
9366      * Sets the data value of the editor
9367      * @param {Mixed} value Any valid value supported by the underlying field
9368      */
9369     setValue : function(v){
9370         this.field.setValue(v);
9371     },
9372
9373     /**
9374      * Gets the data value of the editor
9375      * @return {Mixed} The data value
9376      */
9377     getValue : function(){
9378         return this.field.getValue();
9379     }
9380 });/*
9381  * Based on:
9382  * Ext JS Library 1.1.1
9383  * Copyright(c) 2006-2007, Ext JS, LLC.
9384  *
9385  * Originally Released Under LGPL - original licence link has changed is not relivant.
9386  *
9387  * Fork - LGPL
9388  * <script type="text/javascript">
9389  */
9390  
9391 /**
9392  * @class Roo.BasicDialog
9393  * @extends Roo.util.Observable
9394  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9395  * <pre><code>
9396 var dlg = new Roo.BasicDialog("my-dlg", {
9397     height: 200,
9398     width: 300,
9399     minHeight: 100,
9400     minWidth: 150,
9401     modal: true,
9402     proxyDrag: true,
9403     shadow: true
9404 });
9405 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9406 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9407 dlg.addButton('Cancel', dlg.hide, dlg);
9408 dlg.show();
9409 </code></pre>
9410   <b>A Dialog should always be a direct child of the body element.</b>
9411  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9412  * @cfg {String} title Default text to display in the title bar (defaults to null)
9413  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9414  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9415  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9416  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9417  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9418  * (defaults to null with no animation)
9419  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9420  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9421  * property for valid values (defaults to 'all')
9422  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9423  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9424  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9425  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9426  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9427  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9428  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9429  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9430  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9431  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9432  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9433  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9434  * draggable = true (defaults to false)
9435  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9436  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9437  * shadow (defaults to false)
9438  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9439  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9440  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9441  * @cfg {Array} buttons Array of buttons
9442  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9443  * @constructor
9444  * Create a new BasicDialog.
9445  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9446  * @param {Object} config Configuration options
9447  */
9448 Roo.BasicDialog = function(el, config){
9449     this.el = Roo.get(el);
9450     var dh = Roo.DomHelper;
9451     if(!this.el && config && config.autoCreate){
9452         if(typeof config.autoCreate == "object"){
9453             if(!config.autoCreate.id){
9454                 config.autoCreate.id = el;
9455             }
9456             this.el = dh.append(document.body,
9457                         config.autoCreate, true);
9458         }else{
9459             this.el = dh.append(document.body,
9460                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9461         }
9462     }
9463     el = this.el;
9464     el.setDisplayed(true);
9465     el.hide = this.hideAction;
9466     this.id = el.id;
9467     el.addClass("x-dlg");
9468
9469     Roo.apply(this, config);
9470
9471     this.proxy = el.createProxy("x-dlg-proxy");
9472     this.proxy.hide = this.hideAction;
9473     this.proxy.setOpacity(.5);
9474     this.proxy.hide();
9475
9476     if(config.width){
9477         el.setWidth(config.width);
9478     }
9479     if(config.height){
9480         el.setHeight(config.height);
9481     }
9482     this.size = el.getSize();
9483     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9484         this.xy = [config.x,config.y];
9485     }else{
9486         this.xy = el.getCenterXY(true);
9487     }
9488     /** The header element @type Roo.Element */
9489     this.header = el.child("> .x-dlg-hd");
9490     /** The body element @type Roo.Element */
9491     this.body = el.child("> .x-dlg-bd");
9492     /** The footer element @type Roo.Element */
9493     this.footer = el.child("> .x-dlg-ft");
9494
9495     if(!this.header){
9496         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9497     }
9498     if(!this.body){
9499         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9500     }
9501
9502     this.header.unselectable();
9503     if(this.title){
9504         this.header.update(this.title);
9505     }
9506     // this element allows the dialog to be focused for keyboard event
9507     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9508     this.focusEl.swallowEvent("click", true);
9509
9510     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9511
9512     // wrap the body and footer for special rendering
9513     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9514     if(this.footer){
9515         this.bwrap.dom.appendChild(this.footer.dom);
9516     }
9517
9518     this.bg = this.el.createChild({
9519         tag: "div", cls:"x-dlg-bg",
9520         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9521     });
9522     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9523
9524
9525     if(this.autoScroll !== false && !this.autoTabs){
9526         this.body.setStyle("overflow", "auto");
9527     }
9528
9529     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9530
9531     if(this.closable !== false){
9532         this.el.addClass("x-dlg-closable");
9533         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9534         this.close.on("click", this.closeClick, this);
9535         this.close.addClassOnOver("x-dlg-close-over");
9536     }
9537     if(this.collapsible !== false){
9538         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9539         this.collapseBtn.on("click", this.collapseClick, this);
9540         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9541         this.header.on("dblclick", this.collapseClick, this);
9542     }
9543     if(this.resizable !== false){
9544         this.el.addClass("x-dlg-resizable");
9545         this.resizer = new Roo.Resizable(el, {
9546             minWidth: this.minWidth || 80,
9547             minHeight:this.minHeight || 80,
9548             handles: this.resizeHandles || "all",
9549             pinned: true
9550         });
9551         this.resizer.on("beforeresize", this.beforeResize, this);
9552         this.resizer.on("resize", this.onResize, this);
9553     }
9554     if(this.draggable !== false){
9555         el.addClass("x-dlg-draggable");
9556         if (!this.proxyDrag) {
9557             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9558         }
9559         else {
9560             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9561         }
9562         dd.setHandleElId(this.header.id);
9563         dd.endDrag = this.endMove.createDelegate(this);
9564         dd.startDrag = this.startMove.createDelegate(this);
9565         dd.onDrag = this.onDrag.createDelegate(this);
9566         dd.scroll = false;
9567         this.dd = dd;
9568     }
9569     if(this.modal){
9570         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9571         this.mask.enableDisplayMode("block");
9572         this.mask.hide();
9573         this.el.addClass("x-dlg-modal");
9574     }
9575     if(this.shadow){
9576         this.shadow = new Roo.Shadow({
9577             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9578             offset : this.shadowOffset
9579         });
9580     }else{
9581         this.shadowOffset = 0;
9582     }
9583     if(Roo.useShims && this.shim !== false){
9584         this.shim = this.el.createShim();
9585         this.shim.hide = this.hideAction;
9586         this.shim.hide();
9587     }else{
9588         this.shim = false;
9589     }
9590     if(this.autoTabs){
9591         this.initTabs();
9592     }
9593     if (this.buttons) { 
9594         var bts= this.buttons;
9595         this.buttons = [];
9596         Roo.each(bts, function(b) {
9597             this.addButton(b);
9598         }, this);
9599     }
9600     
9601     
9602     this.addEvents({
9603         /**
9604          * @event keydown
9605          * Fires when a key is pressed
9606          * @param {Roo.BasicDialog} this
9607          * @param {Roo.EventObject} e
9608          */
9609         "keydown" : true,
9610         /**
9611          * @event move
9612          * Fires when this dialog is moved by the user.
9613          * @param {Roo.BasicDialog} this
9614          * @param {Number} x The new page X
9615          * @param {Number} y The new page Y
9616          */
9617         "move" : true,
9618         /**
9619          * @event resize
9620          * Fires when this dialog is resized by the user.
9621          * @param {Roo.BasicDialog} this
9622          * @param {Number} width The new width
9623          * @param {Number} height The new height
9624          */
9625         "resize" : true,
9626         /**
9627          * @event beforehide
9628          * Fires before this dialog is hidden.
9629          * @param {Roo.BasicDialog} this
9630          */
9631         "beforehide" : true,
9632         /**
9633          * @event hide
9634          * Fires when this dialog is hidden.
9635          * @param {Roo.BasicDialog} this
9636          */
9637         "hide" : true,
9638         /**
9639          * @event beforeshow
9640          * Fires before this dialog is shown.
9641          * @param {Roo.BasicDialog} this
9642          */
9643         "beforeshow" : true,
9644         /**
9645          * @event show
9646          * Fires when this dialog is shown.
9647          * @param {Roo.BasicDialog} this
9648          */
9649         "show" : true
9650     });
9651     el.on("keydown", this.onKeyDown, this);
9652     el.on("mousedown", this.toFront, this);
9653     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9654     this.el.hide();
9655     Roo.DialogManager.register(this);
9656     Roo.BasicDialog.superclass.constructor.call(this);
9657 };
9658
9659 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9660     shadowOffset: Roo.isIE ? 6 : 5,
9661     minHeight: 80,
9662     minWidth: 200,
9663     minButtonWidth: 75,
9664     defaultButton: null,
9665     buttonAlign: "right",
9666     tabTag: 'div',
9667     firstShow: true,
9668
9669     /**
9670      * Sets the dialog title text
9671      * @param {String} text The title text to display
9672      * @return {Roo.BasicDialog} this
9673      */
9674     setTitle : function(text){
9675         this.header.update(text);
9676         return this;
9677     },
9678
9679     // private
9680     closeClick : function(){
9681         this.hide();
9682     },
9683
9684     // private
9685     collapseClick : function(){
9686         this[this.collapsed ? "expand" : "collapse"]();
9687     },
9688
9689     /**
9690      * Collapses the dialog to its minimized state (only the title bar is visible).
9691      * Equivalent to the user clicking the collapse dialog button.
9692      */
9693     collapse : function(){
9694         if(!this.collapsed){
9695             this.collapsed = true;
9696             this.el.addClass("x-dlg-collapsed");
9697             this.restoreHeight = this.el.getHeight();
9698             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9699         }
9700     },
9701
9702     /**
9703      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9704      * clicking the expand dialog button.
9705      */
9706     expand : function(){
9707         if(this.collapsed){
9708             this.collapsed = false;
9709             this.el.removeClass("x-dlg-collapsed");
9710             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9711         }
9712     },
9713
9714     /**
9715      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9716      * @return {Roo.TabPanel} The tabs component
9717      */
9718     initTabs : function(){
9719         var tabs = this.getTabs();
9720         while(tabs.getTab(0)){
9721             tabs.removeTab(0);
9722         }
9723         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9724             var dom = el.dom;
9725             tabs.addTab(Roo.id(dom), dom.title);
9726             dom.title = "";
9727         });
9728         tabs.activate(0);
9729         return tabs;
9730     },
9731
9732     // private
9733     beforeResize : function(){
9734         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9735     },
9736
9737     // private
9738     onResize : function(){
9739         this.refreshSize();
9740         this.syncBodyHeight();
9741         this.adjustAssets();
9742         this.focus();
9743         this.fireEvent("resize", this, this.size.width, this.size.height);
9744     },
9745
9746     // private
9747     onKeyDown : function(e){
9748         if(this.isVisible()){
9749             this.fireEvent("keydown", this, e);
9750         }
9751     },
9752
9753     /**
9754      * Resizes the dialog.
9755      * @param {Number} width
9756      * @param {Number} height
9757      * @return {Roo.BasicDialog} this
9758      */
9759     resizeTo : function(width, height){
9760         this.el.setSize(width, height);
9761         this.size = {width: width, height: height};
9762         this.syncBodyHeight();
9763         if(this.fixedcenter){
9764             this.center();
9765         }
9766         if(this.isVisible()){
9767             this.constrainXY();
9768             this.adjustAssets();
9769         }
9770         this.fireEvent("resize", this, width, height);
9771         return this;
9772     },
9773
9774
9775     /**
9776      * Resizes the dialog to fit the specified content size.
9777      * @param {Number} width
9778      * @param {Number} height
9779      * @return {Roo.BasicDialog} this
9780      */
9781     setContentSize : function(w, h){
9782         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9783         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9784         //if(!this.el.isBorderBox()){
9785             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9786             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9787         //}
9788         if(this.tabs){
9789             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9790             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9791         }
9792         this.resizeTo(w, h);
9793         return this;
9794     },
9795
9796     /**
9797      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9798      * executed in response to a particular key being pressed while the dialog is active.
9799      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9800      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9801      * @param {Function} fn The function to call
9802      * @param {Object} scope (optional) The scope of the function
9803      * @return {Roo.BasicDialog} this
9804      */
9805     addKeyListener : function(key, fn, scope){
9806         var keyCode, shift, ctrl, alt;
9807         if(typeof key == "object" && !(key instanceof Array)){
9808             keyCode = key["key"];
9809             shift = key["shift"];
9810             ctrl = key["ctrl"];
9811             alt = key["alt"];
9812         }else{
9813             keyCode = key;
9814         }
9815         var handler = function(dlg, e){
9816             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9817                 var k = e.getKey();
9818                 if(keyCode instanceof Array){
9819                     for(var i = 0, len = keyCode.length; i < len; i++){
9820                         if(keyCode[i] == k){
9821                           fn.call(scope || window, dlg, k, e);
9822                           return;
9823                         }
9824                     }
9825                 }else{
9826                     if(k == keyCode){
9827                         fn.call(scope || window, dlg, k, e);
9828                     }
9829                 }
9830             }
9831         };
9832         this.on("keydown", handler);
9833         return this;
9834     },
9835
9836     /**
9837      * Returns the TabPanel component (creates it if it doesn't exist).
9838      * Note: If you wish to simply check for the existence of tabs without creating them,
9839      * check for a null 'tabs' property.
9840      * @return {Roo.TabPanel} The tabs component
9841      */
9842     getTabs : function(){
9843         if(!this.tabs){
9844             this.el.addClass("x-dlg-auto-tabs");
9845             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9846             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9847         }
9848         return this.tabs;
9849     },
9850
9851     /**
9852      * Adds a button to the footer section of the dialog.
9853      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9854      * object or a valid Roo.DomHelper element config
9855      * @param {Function} handler The function called when the button is clicked
9856      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9857      * @return {Roo.Button} The new button
9858      */
9859     addButton : function(config, handler, scope){
9860         var dh = Roo.DomHelper;
9861         if(!this.footer){
9862             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9863         }
9864         if(!this.btnContainer){
9865             var tb = this.footer.createChild({
9866
9867                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9868                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9869             }, null, true);
9870             this.btnContainer = tb.firstChild.firstChild.firstChild;
9871         }
9872         var bconfig = {
9873             handler: handler,
9874             scope: scope,
9875             minWidth: this.minButtonWidth,
9876             hideParent:true
9877         };
9878         if(typeof config == "string"){
9879             bconfig.text = config;
9880         }else{
9881             if(config.tag){
9882                 bconfig.dhconfig = config;
9883             }else{
9884                 Roo.apply(bconfig, config);
9885             }
9886         }
9887         var fc = false;
9888         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9889             bconfig.position = Math.max(0, bconfig.position);
9890             fc = this.btnContainer.childNodes[bconfig.position];
9891         }
9892          
9893         var btn = new Roo.Button(
9894             fc ? 
9895                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9896                 : this.btnContainer.appendChild(document.createElement("td")),
9897             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9898             bconfig
9899         );
9900         this.syncBodyHeight();
9901         if(!this.buttons){
9902             /**
9903              * Array of all the buttons that have been added to this dialog via addButton
9904              * @type Array
9905              */
9906             this.buttons = [];
9907         }
9908         this.buttons.push(btn);
9909         return btn;
9910     },
9911
9912     /**
9913      * Sets the default button to be focused when the dialog is displayed.
9914      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9915      * @return {Roo.BasicDialog} this
9916      */
9917     setDefaultButton : function(btn){
9918         this.defaultButton = btn;
9919         return this;
9920     },
9921
9922     // private
9923     getHeaderFooterHeight : function(safe){
9924         var height = 0;
9925         if(this.header){
9926            height += this.header.getHeight();
9927         }
9928         if(this.footer){
9929            var fm = this.footer.getMargins();
9930             height += (this.footer.getHeight()+fm.top+fm.bottom);
9931         }
9932         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9933         height += this.centerBg.getPadding("tb");
9934         return height;
9935     },
9936
9937     // private
9938     syncBodyHeight : function()
9939     {
9940         var bd = this.body, // the text
9941             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9942             bw = this.bwrap;
9943         var height = this.size.height - this.getHeaderFooterHeight(false);
9944         bd.setHeight(height-bd.getMargins("tb"));
9945         var hh = this.header.getHeight();
9946         var h = this.size.height-hh;
9947         cb.setHeight(h);
9948         
9949         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9950         bw.setHeight(h-cb.getPadding("tb"));
9951         
9952         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9953         bd.setWidth(bw.getWidth(true));
9954         if(this.tabs){
9955             this.tabs.syncHeight();
9956             if(Roo.isIE){
9957                 this.tabs.el.repaint();
9958             }
9959         }
9960     },
9961
9962     /**
9963      * Restores the previous state of the dialog if Roo.state is configured.
9964      * @return {Roo.BasicDialog} this
9965      */
9966     restoreState : function(){
9967         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9968         if(box && box.width){
9969             this.xy = [box.x, box.y];
9970             this.resizeTo(box.width, box.height);
9971         }
9972         return this;
9973     },
9974
9975     // private
9976     beforeShow : function(){
9977         this.expand();
9978         if(this.fixedcenter){
9979             this.xy = this.el.getCenterXY(true);
9980         }
9981         if(this.modal){
9982             Roo.get(document.body).addClass("x-body-masked");
9983             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9984             this.mask.show();
9985         }
9986         this.constrainXY();
9987     },
9988
9989     // private
9990     animShow : function(){
9991         var b = Roo.get(this.animateTarget).getBox();
9992         this.proxy.setSize(b.width, b.height);
9993         this.proxy.setLocation(b.x, b.y);
9994         this.proxy.show();
9995         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9996                     true, .35, this.showEl.createDelegate(this));
9997     },
9998
9999     /**
10000      * Shows the dialog.
10001      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10002      * @return {Roo.BasicDialog} this
10003      */
10004     show : function(animateTarget){
10005         if (this.fireEvent("beforeshow", this) === false){
10006             return;
10007         }
10008         if(this.syncHeightBeforeShow){
10009             this.syncBodyHeight();
10010         }else if(this.firstShow){
10011             this.firstShow = false;
10012             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10013         }
10014         this.animateTarget = animateTarget || this.animateTarget;
10015         if(!this.el.isVisible()){
10016             this.beforeShow();
10017             if(this.animateTarget && Roo.get(this.animateTarget)){
10018                 this.animShow();
10019             }else{
10020                 this.showEl();
10021             }
10022         }
10023         return this;
10024     },
10025
10026     // private
10027     showEl : function(){
10028         this.proxy.hide();
10029         this.el.setXY(this.xy);
10030         this.el.show();
10031         this.adjustAssets(true);
10032         this.toFront();
10033         this.focus();
10034         // IE peekaboo bug - fix found by Dave Fenwick
10035         if(Roo.isIE){
10036             this.el.repaint();
10037         }
10038         this.fireEvent("show", this);
10039     },
10040
10041     /**
10042      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10043      * dialog itself will receive focus.
10044      */
10045     focus : function(){
10046         if(this.defaultButton){
10047             this.defaultButton.focus();
10048         }else{
10049             this.focusEl.focus();
10050         }
10051     },
10052
10053     // private
10054     constrainXY : function(){
10055         if(this.constraintoviewport !== false){
10056             if(!this.viewSize){
10057                 if(this.container){
10058                     var s = this.container.getSize();
10059                     this.viewSize = [s.width, s.height];
10060                 }else{
10061                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10062                 }
10063             }
10064             var s = Roo.get(this.container||document).getScroll();
10065
10066             var x = this.xy[0], y = this.xy[1];
10067             var w = this.size.width, h = this.size.height;
10068             var vw = this.viewSize[0], vh = this.viewSize[1];
10069             // only move it if it needs it
10070             var moved = false;
10071             // first validate right/bottom
10072             if(x + w > vw+s.left){
10073                 x = vw - w;
10074                 moved = true;
10075             }
10076             if(y + h > vh+s.top){
10077                 y = vh - h;
10078                 moved = true;
10079             }
10080             // then make sure top/left isn't negative
10081             if(x < s.left){
10082                 x = s.left;
10083                 moved = true;
10084             }
10085             if(y < s.top){
10086                 y = s.top;
10087                 moved = true;
10088             }
10089             if(moved){
10090                 // cache xy
10091                 this.xy = [x, y];
10092                 if(this.isVisible()){
10093                     this.el.setLocation(x, y);
10094                     this.adjustAssets();
10095                 }
10096             }
10097         }
10098     },
10099
10100     // private
10101     onDrag : function(){
10102         if(!this.proxyDrag){
10103             this.xy = this.el.getXY();
10104             this.adjustAssets();
10105         }
10106     },
10107
10108     // private
10109     adjustAssets : function(doShow){
10110         var x = this.xy[0], y = this.xy[1];
10111         var w = this.size.width, h = this.size.height;
10112         if(doShow === true){
10113             if(this.shadow){
10114                 this.shadow.show(this.el);
10115             }
10116             if(this.shim){
10117                 this.shim.show();
10118             }
10119         }
10120         if(this.shadow && this.shadow.isVisible()){
10121             this.shadow.show(this.el);
10122         }
10123         if(this.shim && this.shim.isVisible()){
10124             this.shim.setBounds(x, y, w, h);
10125         }
10126     },
10127
10128     // private
10129     adjustViewport : function(w, h){
10130         if(!w || !h){
10131             w = Roo.lib.Dom.getViewWidth();
10132             h = Roo.lib.Dom.getViewHeight();
10133         }
10134         // cache the size
10135         this.viewSize = [w, h];
10136         if(this.modal && this.mask.isVisible()){
10137             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10138             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10139         }
10140         if(this.isVisible()){
10141             this.constrainXY();
10142         }
10143     },
10144
10145     /**
10146      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10147      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10148      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10149      */
10150     destroy : function(removeEl){
10151         if(this.isVisible()){
10152             this.animateTarget = null;
10153             this.hide();
10154         }
10155         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10156         if(this.tabs){
10157             this.tabs.destroy(removeEl);
10158         }
10159         Roo.destroy(
10160              this.shim,
10161              this.proxy,
10162              this.resizer,
10163              this.close,
10164              this.mask
10165         );
10166         if(this.dd){
10167             this.dd.unreg();
10168         }
10169         if(this.buttons){
10170            for(var i = 0, len = this.buttons.length; i < len; i++){
10171                this.buttons[i].destroy();
10172            }
10173         }
10174         this.el.removeAllListeners();
10175         if(removeEl === true){
10176             this.el.update("");
10177             this.el.remove();
10178         }
10179         Roo.DialogManager.unregister(this);
10180     },
10181
10182     // private
10183     startMove : function(){
10184         if(this.proxyDrag){
10185             this.proxy.show();
10186         }
10187         if(this.constraintoviewport !== false){
10188             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10189         }
10190     },
10191
10192     // private
10193     endMove : function(){
10194         if(!this.proxyDrag){
10195             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10196         }else{
10197             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10198             this.proxy.hide();
10199         }
10200         this.refreshSize();
10201         this.adjustAssets();
10202         this.focus();
10203         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10204     },
10205
10206     /**
10207      * Brings this dialog to the front of any other visible dialogs
10208      * @return {Roo.BasicDialog} this
10209      */
10210     toFront : function(){
10211         Roo.DialogManager.bringToFront(this);
10212         return this;
10213     },
10214
10215     /**
10216      * Sends this dialog to the back (under) of any other visible dialogs
10217      * @return {Roo.BasicDialog} this
10218      */
10219     toBack : function(){
10220         Roo.DialogManager.sendToBack(this);
10221         return this;
10222     },
10223
10224     /**
10225      * Centers this dialog in the viewport
10226      * @return {Roo.BasicDialog} this
10227      */
10228     center : function(){
10229         var xy = this.el.getCenterXY(true);
10230         this.moveTo(xy[0], xy[1]);
10231         return this;
10232     },
10233
10234     /**
10235      * Moves the dialog's top-left corner to the specified point
10236      * @param {Number} x
10237      * @param {Number} y
10238      * @return {Roo.BasicDialog} this
10239      */
10240     moveTo : function(x, y){
10241         this.xy = [x,y];
10242         if(this.isVisible()){
10243             this.el.setXY(this.xy);
10244             this.adjustAssets();
10245         }
10246         return this;
10247     },
10248
10249     /**
10250      * Aligns the dialog to the specified element
10251      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10252      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10253      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10254      * @return {Roo.BasicDialog} this
10255      */
10256     alignTo : function(element, position, offsets){
10257         this.xy = this.el.getAlignToXY(element, position, offsets);
10258         if(this.isVisible()){
10259             this.el.setXY(this.xy);
10260             this.adjustAssets();
10261         }
10262         return this;
10263     },
10264
10265     /**
10266      * Anchors an element to another element and realigns it when the window is resized.
10267      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10268      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10269      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10270      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10271      * is a number, it is used as the buffer delay (defaults to 50ms).
10272      * @return {Roo.BasicDialog} this
10273      */
10274     anchorTo : function(el, alignment, offsets, monitorScroll){
10275         var action = function(){
10276             this.alignTo(el, alignment, offsets);
10277         };
10278         Roo.EventManager.onWindowResize(action, this);
10279         var tm = typeof monitorScroll;
10280         if(tm != 'undefined'){
10281             Roo.EventManager.on(window, 'scroll', action, this,
10282                 {buffer: tm == 'number' ? monitorScroll : 50});
10283         }
10284         action.call(this);
10285         return this;
10286     },
10287
10288     /**
10289      * Returns true if the dialog is visible
10290      * @return {Boolean}
10291      */
10292     isVisible : function(){
10293         return this.el.isVisible();
10294     },
10295
10296     // private
10297     animHide : function(callback){
10298         var b = Roo.get(this.animateTarget).getBox();
10299         this.proxy.show();
10300         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10301         this.el.hide();
10302         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10303                     this.hideEl.createDelegate(this, [callback]));
10304     },
10305
10306     /**
10307      * Hides the dialog.
10308      * @param {Function} callback (optional) Function to call when the dialog is hidden
10309      * @return {Roo.BasicDialog} this
10310      */
10311     hide : function(callback){
10312         if (this.fireEvent("beforehide", this) === false){
10313             return;
10314         }
10315         if(this.shadow){
10316             this.shadow.hide();
10317         }
10318         if(this.shim) {
10319           this.shim.hide();
10320         }
10321         // sometimes animateTarget seems to get set.. causing problems...
10322         // this just double checks..
10323         if(this.animateTarget && Roo.get(this.animateTarget)) {
10324            this.animHide(callback);
10325         }else{
10326             this.el.hide();
10327             this.hideEl(callback);
10328         }
10329         return this;
10330     },
10331
10332     // private
10333     hideEl : function(callback){
10334         this.proxy.hide();
10335         if(this.modal){
10336             this.mask.hide();
10337             Roo.get(document.body).removeClass("x-body-masked");
10338         }
10339         this.fireEvent("hide", this);
10340         if(typeof callback == "function"){
10341             callback();
10342         }
10343     },
10344
10345     // private
10346     hideAction : function(){
10347         this.setLeft("-10000px");
10348         this.setTop("-10000px");
10349         this.setStyle("visibility", "hidden");
10350     },
10351
10352     // private
10353     refreshSize : function(){
10354         this.size = this.el.getSize();
10355         this.xy = this.el.getXY();
10356         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10357     },
10358
10359     // private
10360     // z-index is managed by the DialogManager and may be overwritten at any time
10361     setZIndex : function(index){
10362         if(this.modal){
10363             this.mask.setStyle("z-index", index);
10364         }
10365         if(this.shim){
10366             this.shim.setStyle("z-index", ++index);
10367         }
10368         if(this.shadow){
10369             this.shadow.setZIndex(++index);
10370         }
10371         this.el.setStyle("z-index", ++index);
10372         if(this.proxy){
10373             this.proxy.setStyle("z-index", ++index);
10374         }
10375         if(this.resizer){
10376             this.resizer.proxy.setStyle("z-index", ++index);
10377         }
10378
10379         this.lastZIndex = index;
10380     },
10381
10382     /**
10383      * Returns the element for this dialog
10384      * @return {Roo.Element} The underlying dialog Element
10385      */
10386     getEl : function(){
10387         return this.el;
10388     }
10389 });
10390
10391 /**
10392  * @class Roo.DialogManager
10393  * Provides global access to BasicDialogs that have been created and
10394  * support for z-indexing (layering) multiple open dialogs.
10395  */
10396 Roo.DialogManager = function(){
10397     var list = {};
10398     var accessList = [];
10399     var front = null;
10400
10401     // private
10402     var sortDialogs = function(d1, d2){
10403         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10404     };
10405
10406     // private
10407     var orderDialogs = function(){
10408         accessList.sort(sortDialogs);
10409         var seed = Roo.DialogManager.zseed;
10410         for(var i = 0, len = accessList.length; i < len; i++){
10411             var dlg = accessList[i];
10412             if(dlg){
10413                 dlg.setZIndex(seed + (i*10));
10414             }
10415         }
10416     };
10417
10418     return {
10419         /**
10420          * The starting z-index for BasicDialogs (defaults to 9000)
10421          * @type Number The z-index value
10422          */
10423         zseed : 9000,
10424
10425         // private
10426         register : function(dlg){
10427             list[dlg.id] = dlg;
10428             accessList.push(dlg);
10429         },
10430
10431         // private
10432         unregister : function(dlg){
10433             delete list[dlg.id];
10434             var i=0;
10435             var len=0;
10436             if(!accessList.indexOf){
10437                 for(  i = 0, len = accessList.length; i < len; i++){
10438                     if(accessList[i] == dlg){
10439                         accessList.splice(i, 1);
10440                         return;
10441                     }
10442                 }
10443             }else{
10444                  i = accessList.indexOf(dlg);
10445                 if(i != -1){
10446                     accessList.splice(i, 1);
10447                 }
10448             }
10449         },
10450
10451         /**
10452          * Gets a registered dialog by id
10453          * @param {String/Object} id The id of the dialog or a dialog
10454          * @return {Roo.BasicDialog} this
10455          */
10456         get : function(id){
10457             return typeof id == "object" ? id : list[id];
10458         },
10459
10460         /**
10461          * Brings the specified dialog to the front
10462          * @param {String/Object} dlg The id of the dialog or a dialog
10463          * @return {Roo.BasicDialog} this
10464          */
10465         bringToFront : function(dlg){
10466             dlg = this.get(dlg);
10467             if(dlg != front){
10468                 front = dlg;
10469                 dlg._lastAccess = new Date().getTime();
10470                 orderDialogs();
10471             }
10472             return dlg;
10473         },
10474
10475         /**
10476          * Sends the specified dialog to the back
10477          * @param {String/Object} dlg The id of the dialog or a dialog
10478          * @return {Roo.BasicDialog} this
10479          */
10480         sendToBack : function(dlg){
10481             dlg = this.get(dlg);
10482             dlg._lastAccess = -(new Date().getTime());
10483             orderDialogs();
10484             return dlg;
10485         },
10486
10487         /**
10488          * Hides all dialogs
10489          */
10490         hideAll : function(){
10491             for(var id in list){
10492                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10493                     list[id].hide();
10494                 }
10495             }
10496         }
10497     };
10498 }();
10499
10500 /**
10501  * @class Roo.LayoutDialog
10502  * @extends Roo.BasicDialog
10503  * Dialog which provides adjustments for working with a layout in a Dialog.
10504  * Add your necessary layout config options to the dialog's config.<br>
10505  * Example usage (including a nested layout):
10506  * <pre><code>
10507 if(!dialog){
10508     dialog = new Roo.LayoutDialog("download-dlg", {
10509         modal: true,
10510         width:600,
10511         height:450,
10512         shadow:true,
10513         minWidth:500,
10514         minHeight:350,
10515         autoTabs:true,
10516         proxyDrag:true,
10517         // layout config merges with the dialog config
10518         center:{
10519             tabPosition: "top",
10520             alwaysShowTabs: true
10521         }
10522     });
10523     dialog.addKeyListener(27, dialog.hide, dialog);
10524     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10525     dialog.addButton("Build It!", this.getDownload, this);
10526
10527     // we can even add nested layouts
10528     var innerLayout = new Roo.BorderLayout("dl-inner", {
10529         east: {
10530             initialSize: 200,
10531             autoScroll:true,
10532             split:true
10533         },
10534         center: {
10535             autoScroll:true
10536         }
10537     });
10538     innerLayout.beginUpdate();
10539     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10540     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10541     innerLayout.endUpdate(true);
10542
10543     var layout = dialog.getLayout();
10544     layout.beginUpdate();
10545     layout.add("center", new Roo.ContentPanel("standard-panel",
10546                         {title: "Download the Source", fitToFrame:true}));
10547     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10548                {title: "Build your own roo.js"}));
10549     layout.getRegion("center").showPanel(sp);
10550     layout.endUpdate();
10551 }
10552 </code></pre>
10553     * @constructor
10554     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10555     * @param {Object} config configuration options
10556   */
10557 Roo.LayoutDialog = function(el, cfg){
10558     
10559     var config=  cfg;
10560     if (typeof(cfg) == 'undefined') {
10561         config = Roo.apply({}, el);
10562         // not sure why we use documentElement here.. - it should always be body.
10563         // IE7 borks horribly if we use documentElement.
10564         // webkit also does not like documentElement - it creates a body element...
10565         el = Roo.get( document.body || document.documentElement ).createChild();
10566         //config.autoCreate = true;
10567     }
10568     
10569     
10570     config.autoTabs = false;
10571     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10572     this.body.setStyle({overflow:"hidden", position:"relative"});
10573     this.layout = new Roo.BorderLayout(this.body.dom, config);
10574     this.layout.monitorWindowResize = false;
10575     this.el.addClass("x-dlg-auto-layout");
10576     // fix case when center region overwrites center function
10577     this.center = Roo.BasicDialog.prototype.center;
10578     this.on("show", this.layout.layout, this.layout, true);
10579     if (config.items) {
10580         var xitems = config.items;
10581         delete config.items;
10582         Roo.each(xitems, this.addxtype, this);
10583     }
10584     
10585     
10586 };
10587 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10588     /**
10589      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10590      * @deprecated
10591      */
10592     endUpdate : function(){
10593         this.layout.endUpdate();
10594     },
10595
10596     /**
10597      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10598      *  @deprecated
10599      */
10600     beginUpdate : function(){
10601         this.layout.beginUpdate();
10602     },
10603
10604     /**
10605      * Get the BorderLayout for this dialog
10606      * @return {Roo.BorderLayout}
10607      */
10608     getLayout : function(){
10609         return this.layout;
10610     },
10611
10612     showEl : function(){
10613         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10614         if(Roo.isIE7){
10615             this.layout.layout();
10616         }
10617     },
10618
10619     // private
10620     // Use the syncHeightBeforeShow config option to control this automatically
10621     syncBodyHeight : function(){
10622         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10623         if(this.layout){this.layout.layout();}
10624     },
10625     
10626       /**
10627      * Add an xtype element (actually adds to the layout.)
10628      * @return {Object} xdata xtype object data.
10629      */
10630     
10631     addxtype : function(c) {
10632         return this.layout.addxtype(c);
10633     }
10634 });/*
10635  * Based on:
10636  * Ext JS Library 1.1.1
10637  * Copyright(c) 2006-2007, Ext JS, LLC.
10638  *
10639  * Originally Released Under LGPL - original licence link has changed is not relivant.
10640  *
10641  * Fork - LGPL
10642  * <script type="text/javascript">
10643  */
10644  
10645 /**
10646  * @class Roo.MessageBox
10647  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10648  * Example usage:
10649  *<pre><code>
10650 // Basic alert:
10651 Roo.Msg.alert('Status', 'Changes saved successfully.');
10652
10653 // Prompt for user data:
10654 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10655     if (btn == 'ok'){
10656         // process text value...
10657     }
10658 });
10659
10660 // Show a dialog using config options:
10661 Roo.Msg.show({
10662    title:'Save Changes?',
10663    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10664    buttons: Roo.Msg.YESNOCANCEL,
10665    fn: processResult,
10666    animEl: 'elId'
10667 });
10668 </code></pre>
10669  * @singleton
10670  */
10671 Roo.MessageBox = function(){
10672     var dlg, opt, mask, waitTimer;
10673     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10674     var buttons, activeTextEl, bwidth;
10675
10676     // private
10677     var handleButton = function(button){
10678         dlg.hide();
10679         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10680     };
10681
10682     // private
10683     var handleHide = function(){
10684         if(opt && opt.cls){
10685             dlg.el.removeClass(opt.cls);
10686         }
10687         if(waitTimer){
10688             Roo.TaskMgr.stop(waitTimer);
10689             waitTimer = null;
10690         }
10691     };
10692
10693     // private
10694     var updateButtons = function(b){
10695         var width = 0;
10696         if(!b){
10697             buttons["ok"].hide();
10698             buttons["cancel"].hide();
10699             buttons["yes"].hide();
10700             buttons["no"].hide();
10701             dlg.footer.dom.style.display = 'none';
10702             return width;
10703         }
10704         dlg.footer.dom.style.display = '';
10705         for(var k in buttons){
10706             if(typeof buttons[k] != "function"){
10707                 if(b[k]){
10708                     buttons[k].show();
10709                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10710                     width += buttons[k].el.getWidth()+15;
10711                 }else{
10712                     buttons[k].hide();
10713                 }
10714             }
10715         }
10716         return width;
10717     };
10718
10719     // private
10720     var handleEsc = function(d, k, e){
10721         if(opt && opt.closable !== false){
10722             dlg.hide();
10723         }
10724         if(e){
10725             e.stopEvent();
10726         }
10727     };
10728
10729     return {
10730         /**
10731          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10732          * @return {Roo.BasicDialog} The BasicDialog element
10733          */
10734         getDialog : function(){
10735            if(!dlg){
10736                 dlg = new Roo.BasicDialog("x-msg-box", {
10737                     autoCreate : true,
10738                     shadow: true,
10739                     draggable: true,
10740                     resizable:false,
10741                     constraintoviewport:false,
10742                     fixedcenter:true,
10743                     collapsible : false,
10744                     shim:true,
10745                     modal: true,
10746                     width:400, height:100,
10747                     buttonAlign:"center",
10748                     closeClick : function(){
10749                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10750                             handleButton("no");
10751                         }else{
10752                             handleButton("cancel");
10753                         }
10754                     }
10755                 });
10756                 dlg.on("hide", handleHide);
10757                 mask = dlg.mask;
10758                 dlg.addKeyListener(27, handleEsc);
10759                 buttons = {};
10760                 var bt = this.buttonText;
10761                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10762                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10763                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10764                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10765                 bodyEl = dlg.body.createChild({
10766
10767                     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>'
10768                 });
10769                 msgEl = bodyEl.dom.firstChild;
10770                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10771                 textboxEl.enableDisplayMode();
10772                 textboxEl.addKeyListener([10,13], function(){
10773                     if(dlg.isVisible() && opt && opt.buttons){
10774                         if(opt.buttons.ok){
10775                             handleButton("ok");
10776                         }else if(opt.buttons.yes){
10777                             handleButton("yes");
10778                         }
10779                     }
10780                 });
10781                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10782                 textareaEl.enableDisplayMode();
10783                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10784                 progressEl.enableDisplayMode();
10785                 var pf = progressEl.dom.firstChild;
10786                 if (pf) {
10787                     pp = Roo.get(pf.firstChild);
10788                     pp.setHeight(pf.offsetHeight);
10789                 }
10790                 
10791             }
10792             return dlg;
10793         },
10794
10795         /**
10796          * Updates the message box body text
10797          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10798          * the XHTML-compliant non-breaking space character '&amp;#160;')
10799          * @return {Roo.MessageBox} This message box
10800          */
10801         updateText : function(text){
10802             if(!dlg.isVisible() && !opt.width){
10803                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10804             }
10805             msgEl.innerHTML = text || '&#160;';
10806       
10807             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10808             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10809             var w = Math.max(
10810                     Math.min(opt.width || cw , this.maxWidth), 
10811                     Math.max(opt.minWidth || this.minWidth, bwidth)
10812             );
10813             if(opt.prompt){
10814                 activeTextEl.setWidth(w);
10815             }
10816             if(dlg.isVisible()){
10817                 dlg.fixedcenter = false;
10818             }
10819             // to big, make it scroll. = But as usual stupid IE does not support
10820             // !important..
10821             
10822             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10823                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10824                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10825             } else {
10826                 bodyEl.dom.style.height = '';
10827                 bodyEl.dom.style.overflowY = '';
10828             }
10829             if (cw > w) {
10830                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10831             } else {
10832                 bodyEl.dom.style.overflowX = '';
10833             }
10834             
10835             dlg.setContentSize(w, bodyEl.getHeight());
10836             if(dlg.isVisible()){
10837                 dlg.fixedcenter = true;
10838             }
10839             return this;
10840         },
10841
10842         /**
10843          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10844          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10845          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10846          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10847          * @return {Roo.MessageBox} This message box
10848          */
10849         updateProgress : function(value, text){
10850             if(text){
10851                 this.updateText(text);
10852             }
10853             if (pp) { // weird bug on my firefox - for some reason this is not defined
10854                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10855             }
10856             return this;
10857         },        
10858
10859         /**
10860          * Returns true if the message box is currently displayed
10861          * @return {Boolean} True if the message box is visible, else false
10862          */
10863         isVisible : function(){
10864             return dlg && dlg.isVisible();  
10865         },
10866
10867         /**
10868          * Hides the message box if it is displayed
10869          */
10870         hide : function(){
10871             if(this.isVisible()){
10872                 dlg.hide();
10873             }  
10874         },
10875
10876         /**
10877          * Displays a new message box, or reinitializes an existing message box, based on the config options
10878          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10879          * The following config object properties are supported:
10880          * <pre>
10881 Property    Type             Description
10882 ----------  ---------------  ------------------------------------------------------------------------------------
10883 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10884                                    closes (defaults to undefined)
10885 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10886                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10887 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10888                                    progress and wait dialogs will ignore this property and always hide the
10889                                    close button as they can only be closed programmatically.
10890 cls               String           A custom CSS class to apply to the message box element
10891 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10892                                    displayed (defaults to 75)
10893 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10894                                    function will be btn (the name of the button that was clicked, if applicable,
10895                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10896                                    Progress and wait dialogs will ignore this option since they do not respond to
10897                                    user actions and can only be closed programmatically, so any required function
10898                                    should be called by the same code after it closes the dialog.
10899 icon              String           A CSS class that provides a background image to be used as an icon for
10900                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10901 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10902 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10903 modal             Boolean          False to allow user interaction with the page while the message box is
10904                                    displayed (defaults to true)
10905 msg               String           A string that will replace the existing message box body text (defaults
10906                                    to the XHTML-compliant non-breaking space character '&#160;')
10907 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10908 progress          Boolean          True to display a progress bar (defaults to false)
10909 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10910 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10911 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10912 title             String           The title text
10913 value             String           The string value to set into the active textbox element if displayed
10914 wait              Boolean          True to display a progress bar (defaults to false)
10915 width             Number           The width of the dialog in pixels
10916 </pre>
10917          *
10918          * Example usage:
10919          * <pre><code>
10920 Roo.Msg.show({
10921    title: 'Address',
10922    msg: 'Please enter your address:',
10923    width: 300,
10924    buttons: Roo.MessageBox.OKCANCEL,
10925    multiline: true,
10926    fn: saveAddress,
10927    animEl: 'addAddressBtn'
10928 });
10929 </code></pre>
10930          * @param {Object} config Configuration options
10931          * @return {Roo.MessageBox} This message box
10932          */
10933         show : function(options)
10934         {
10935             
10936             // this causes nightmares if you show one dialog after another
10937             // especially on callbacks..
10938              
10939             if(this.isVisible()){
10940                 
10941                 this.hide();
10942                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10943                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10944                 Roo.log("New Dialog Message:" +  options.msg )
10945                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10946                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10947                 
10948             }
10949             var d = this.getDialog();
10950             opt = options;
10951             d.setTitle(opt.title || "&#160;");
10952             d.close.setDisplayed(opt.closable !== false);
10953             activeTextEl = textboxEl;
10954             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10955             if(opt.prompt){
10956                 if(opt.multiline){
10957                     textboxEl.hide();
10958                     textareaEl.show();
10959                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10960                         opt.multiline : this.defaultTextHeight);
10961                     activeTextEl = textareaEl;
10962                 }else{
10963                     textboxEl.show();
10964                     textareaEl.hide();
10965                 }
10966             }else{
10967                 textboxEl.hide();
10968                 textareaEl.hide();
10969             }
10970             progressEl.setDisplayed(opt.progress === true);
10971             this.updateProgress(0);
10972             activeTextEl.dom.value = opt.value || "";
10973             if(opt.prompt){
10974                 dlg.setDefaultButton(activeTextEl);
10975             }else{
10976                 var bs = opt.buttons;
10977                 var db = null;
10978                 if(bs && bs.ok){
10979                     db = buttons["ok"];
10980                 }else if(bs && bs.yes){
10981                     db = buttons["yes"];
10982                 }
10983                 dlg.setDefaultButton(db);
10984             }
10985             bwidth = updateButtons(opt.buttons);
10986             this.updateText(opt.msg);
10987             if(opt.cls){
10988                 d.el.addClass(opt.cls);
10989             }
10990             d.proxyDrag = opt.proxyDrag === true;
10991             d.modal = opt.modal !== false;
10992             d.mask = opt.modal !== false ? mask : false;
10993             if(!d.isVisible()){
10994                 // force it to the end of the z-index stack so it gets a cursor in FF
10995                 document.body.appendChild(dlg.el.dom);
10996                 d.animateTarget = null;
10997                 d.show(options.animEl);
10998             }
10999             return this;
11000         },
11001
11002         /**
11003          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
11004          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11005          * and closing the message box when the process is complete.
11006          * @param {String} title The title bar text
11007          * @param {String} msg The message box body text
11008          * @return {Roo.MessageBox} This message box
11009          */
11010         progress : function(title, msg){
11011             this.show({
11012                 title : title,
11013                 msg : msg,
11014                 buttons: false,
11015                 progress:true,
11016                 closable:false,
11017                 minWidth: this.minProgressWidth,
11018                 modal : true
11019             });
11020             return this;
11021         },
11022
11023         /**
11024          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11025          * If a callback function is passed it will be called after the user clicks the button, and the
11026          * id of the button that was clicked will be passed as the only parameter to the callback
11027          * (could also be the top-right close button).
11028          * @param {String} title The title bar text
11029          * @param {String} msg The message box body text
11030          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11031          * @param {Object} scope (optional) The scope of the callback function
11032          * @return {Roo.MessageBox} This message box
11033          */
11034         alert : function(title, msg, fn, scope){
11035             this.show({
11036                 title : title,
11037                 msg : msg,
11038                 buttons: this.OK,
11039                 fn: fn,
11040                 scope : scope,
11041                 modal : true
11042             });
11043             return this;
11044         },
11045
11046         /**
11047          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11048          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11049          * You are responsible for closing the message box when the process is complete.
11050          * @param {String} msg The message box body text
11051          * @param {String} title (optional) The title bar text
11052          * @return {Roo.MessageBox} This message box
11053          */
11054         wait : function(msg, title){
11055             this.show({
11056                 title : title,
11057                 msg : msg,
11058                 buttons: false,
11059                 closable:false,
11060                 progress:true,
11061                 modal:true,
11062                 width:300,
11063                 wait:true
11064             });
11065             waitTimer = Roo.TaskMgr.start({
11066                 run: function(i){
11067                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11068                 },
11069                 interval: 1000
11070             });
11071             return this;
11072         },
11073
11074         /**
11075          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11076          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11077          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11078          * @param {String} title The title bar text
11079          * @param {String} msg The message box body text
11080          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11081          * @param {Object} scope (optional) The scope of the callback function
11082          * @return {Roo.MessageBox} This message box
11083          */
11084         confirm : function(title, msg, fn, scope){
11085             this.show({
11086                 title : title,
11087                 msg : msg,
11088                 buttons: this.YESNO,
11089                 fn: fn,
11090                 scope : scope,
11091                 modal : true
11092             });
11093             return this;
11094         },
11095
11096         /**
11097          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11098          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11099          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11100          * (could also be the top-right close button) and the text that was entered will be passed as the two
11101          * parameters to the callback.
11102          * @param {String} title The title bar text
11103          * @param {String} msg The message box body text
11104          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11105          * @param {Object} scope (optional) The scope of the callback function
11106          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11107          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11108          * @return {Roo.MessageBox} This message box
11109          */
11110         prompt : function(title, msg, fn, scope, multiline){
11111             this.show({
11112                 title : title,
11113                 msg : msg,
11114                 buttons: this.OKCANCEL,
11115                 fn: fn,
11116                 minWidth:250,
11117                 scope : scope,
11118                 prompt:true,
11119                 multiline: multiline,
11120                 modal : true
11121             });
11122             return this;
11123         },
11124
11125         /**
11126          * Button config that displays a single OK button
11127          * @type Object
11128          */
11129         OK : {ok:true},
11130         /**
11131          * Button config that displays Yes and No buttons
11132          * @type Object
11133          */
11134         YESNO : {yes:true, no:true},
11135         /**
11136          * Button config that displays OK and Cancel buttons
11137          * @type Object
11138          */
11139         OKCANCEL : {ok:true, cancel:true},
11140         /**
11141          * Button config that displays Yes, No and Cancel buttons
11142          * @type Object
11143          */
11144         YESNOCANCEL : {yes:true, no:true, cancel:true},
11145
11146         /**
11147          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11148          * @type Number
11149          */
11150         defaultTextHeight : 75,
11151         /**
11152          * The maximum width in pixels of the message box (defaults to 600)
11153          * @type Number
11154          */
11155         maxWidth : 600,
11156         /**
11157          * The minimum width in pixels of the message box (defaults to 100)
11158          * @type Number
11159          */
11160         minWidth : 100,
11161         /**
11162          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11163          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11164          * @type Number
11165          */
11166         minProgressWidth : 250,
11167         /**
11168          * An object containing the default button text strings that can be overriden for localized language support.
11169          * Supported properties are: ok, cancel, yes and no.
11170          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11171          * @type Object
11172          */
11173         buttonText : {
11174             ok : "OK",
11175             cancel : "Cancel",
11176             yes : "Yes",
11177             no : "No"
11178         }
11179     };
11180 }();
11181
11182 /**
11183  * Shorthand for {@link Roo.MessageBox}
11184  */
11185 Roo.Msg = Roo.MessageBox;/*
11186  * Based on:
11187  * Ext JS Library 1.1.1
11188  * Copyright(c) 2006-2007, Ext JS, LLC.
11189  *
11190  * Originally Released Under LGPL - original licence link has changed is not relivant.
11191  *
11192  * Fork - LGPL
11193  * <script type="text/javascript">
11194  */
11195 /**
11196  * @class Roo.QuickTips
11197  * Provides attractive and customizable tooltips for any element.
11198  * @singleton
11199  */
11200 Roo.QuickTips = function(){
11201     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11202     var ce, bd, xy, dd;
11203     var visible = false, disabled = true, inited = false;
11204     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11205     
11206     var onOver = function(e){
11207         if(disabled){
11208             return;
11209         }
11210         var t = e.getTarget();
11211         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11212             return;
11213         }
11214         if(ce && t == ce.el){
11215             clearTimeout(hideProc);
11216             return;
11217         }
11218         if(t && tagEls[t.id]){
11219             tagEls[t.id].el = t;
11220             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11221             return;
11222         }
11223         var ttp, et = Roo.fly(t);
11224         var ns = cfg.namespace;
11225         if(tm.interceptTitles && t.title){
11226             ttp = t.title;
11227             t.qtip = ttp;
11228             t.removeAttribute("title");
11229             e.preventDefault();
11230         }else{
11231             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11232         }
11233         if(ttp){
11234             showProc = show.defer(tm.showDelay, tm, [{
11235                 el: t, 
11236                 text: ttp.replace(/\\n/g,'<br/>'),
11237                 width: et.getAttributeNS(ns, cfg.width),
11238                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11239                 title: et.getAttributeNS(ns, cfg.title),
11240                     cls: et.getAttributeNS(ns, cfg.cls)
11241             }]);
11242         }
11243     };
11244     
11245     var onOut = function(e){
11246         clearTimeout(showProc);
11247         var t = e.getTarget();
11248         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11249             hideProc = setTimeout(hide, tm.hideDelay);
11250         }
11251     };
11252     
11253     var onMove = function(e){
11254         if(disabled){
11255             return;
11256         }
11257         xy = e.getXY();
11258         xy[1] += 18;
11259         if(tm.trackMouse && ce){
11260             el.setXY(xy);
11261         }
11262     };
11263     
11264     var onDown = function(e){
11265         clearTimeout(showProc);
11266         clearTimeout(hideProc);
11267         if(!e.within(el)){
11268             if(tm.hideOnClick){
11269                 hide();
11270                 tm.disable();
11271                 tm.enable.defer(100, tm);
11272             }
11273         }
11274     };
11275     
11276     var getPad = function(){
11277         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11278     };
11279
11280     var show = function(o){
11281         if(disabled){
11282             return;
11283         }
11284         clearTimeout(dismissProc);
11285         ce = o;
11286         if(removeCls){ // in case manually hidden
11287             el.removeClass(removeCls);
11288             removeCls = null;
11289         }
11290         if(ce.cls){
11291             el.addClass(ce.cls);
11292             removeCls = ce.cls;
11293         }
11294         if(ce.title){
11295             tipTitle.update(ce.title);
11296             tipTitle.show();
11297         }else{
11298             tipTitle.update('');
11299             tipTitle.hide();
11300         }
11301         el.dom.style.width  = tm.maxWidth+'px';
11302         //tipBody.dom.style.width = '';
11303         tipBodyText.update(o.text);
11304         var p = getPad(), w = ce.width;
11305         if(!w){
11306             var td = tipBodyText.dom;
11307             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11308             if(aw > tm.maxWidth){
11309                 w = tm.maxWidth;
11310             }else if(aw < tm.minWidth){
11311                 w = tm.minWidth;
11312             }else{
11313                 w = aw;
11314             }
11315         }
11316         //tipBody.setWidth(w);
11317         el.setWidth(parseInt(w, 10) + p);
11318         if(ce.autoHide === false){
11319             close.setDisplayed(true);
11320             if(dd){
11321                 dd.unlock();
11322             }
11323         }else{
11324             close.setDisplayed(false);
11325             if(dd){
11326                 dd.lock();
11327             }
11328         }
11329         if(xy){
11330             el.avoidY = xy[1]-18;
11331             el.setXY(xy);
11332         }
11333         if(tm.animate){
11334             el.setOpacity(.1);
11335             el.setStyle("visibility", "visible");
11336             el.fadeIn({callback: afterShow});
11337         }else{
11338             afterShow();
11339         }
11340     };
11341     
11342     var afterShow = function(){
11343         if(ce){
11344             el.show();
11345             esc.enable();
11346             if(tm.autoDismiss && ce.autoHide !== false){
11347                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11348             }
11349         }
11350     };
11351     
11352     var hide = function(noanim){
11353         clearTimeout(dismissProc);
11354         clearTimeout(hideProc);
11355         ce = null;
11356         if(el.isVisible()){
11357             esc.disable();
11358             if(noanim !== true && tm.animate){
11359                 el.fadeOut({callback: afterHide});
11360             }else{
11361                 afterHide();
11362             } 
11363         }
11364     };
11365     
11366     var afterHide = function(){
11367         el.hide();
11368         if(removeCls){
11369             el.removeClass(removeCls);
11370             removeCls = null;
11371         }
11372     };
11373     
11374     return {
11375         /**
11376         * @cfg {Number} minWidth
11377         * The minimum width of the quick tip (defaults to 40)
11378         */
11379        minWidth : 40,
11380         /**
11381         * @cfg {Number} maxWidth
11382         * The maximum width of the quick tip (defaults to 300)
11383         */
11384        maxWidth : 300,
11385         /**
11386         * @cfg {Boolean} interceptTitles
11387         * True to automatically use the element's DOM title value if available (defaults to false)
11388         */
11389        interceptTitles : false,
11390         /**
11391         * @cfg {Boolean} trackMouse
11392         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11393         */
11394        trackMouse : false,
11395         /**
11396         * @cfg {Boolean} hideOnClick
11397         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11398         */
11399        hideOnClick : true,
11400         /**
11401         * @cfg {Number} showDelay
11402         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11403         */
11404        showDelay : 500,
11405         /**
11406         * @cfg {Number} hideDelay
11407         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11408         */
11409        hideDelay : 200,
11410         /**
11411         * @cfg {Boolean} autoHide
11412         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11413         * Used in conjunction with hideDelay.
11414         */
11415        autoHide : true,
11416         /**
11417         * @cfg {Boolean}
11418         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11419         * (defaults to true).  Used in conjunction with autoDismissDelay.
11420         */
11421        autoDismiss : true,
11422         /**
11423         * @cfg {Number}
11424         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11425         */
11426        autoDismissDelay : 5000,
11427        /**
11428         * @cfg {Boolean} animate
11429         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11430         */
11431        animate : false,
11432
11433        /**
11434         * @cfg {String} title
11435         * Title text to display (defaults to '').  This can be any valid HTML markup.
11436         */
11437         title: '',
11438        /**
11439         * @cfg {String} text
11440         * Body text to display (defaults to '').  This can be any valid HTML markup.
11441         */
11442         text : '',
11443        /**
11444         * @cfg {String} cls
11445         * A CSS class to apply to the base quick tip element (defaults to '').
11446         */
11447         cls : '',
11448        /**
11449         * @cfg {Number} width
11450         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11451         * minWidth or maxWidth.
11452         */
11453         width : null,
11454
11455     /**
11456      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11457      * or display QuickTips in a page.
11458      */
11459        init : function(){
11460           tm = Roo.QuickTips;
11461           cfg = tm.tagConfig;
11462           if(!inited){
11463               if(!Roo.isReady){ // allow calling of init() before onReady
11464                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11465                   return;
11466               }
11467               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11468               el.fxDefaults = {stopFx: true};
11469               // maximum custom styling
11470               //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>');
11471               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>');              
11472               tipTitle = el.child('h3');
11473               tipTitle.enableDisplayMode("block");
11474               tipBody = el.child('div.x-tip-bd');
11475               tipBodyText = el.child('div.x-tip-bd-inner');
11476               //bdLeft = el.child('div.x-tip-bd-left');
11477               //bdRight = el.child('div.x-tip-bd-right');
11478               close = el.child('div.x-tip-close');
11479               close.enableDisplayMode("block");
11480               close.on("click", hide);
11481               var d = Roo.get(document);
11482               d.on("mousedown", onDown);
11483               d.on("mouseover", onOver);
11484               d.on("mouseout", onOut);
11485               d.on("mousemove", onMove);
11486               esc = d.addKeyListener(27, hide);
11487               esc.disable();
11488               if(Roo.dd.DD){
11489                   dd = el.initDD("default", null, {
11490                       onDrag : function(){
11491                           el.sync();  
11492                       }
11493                   });
11494                   dd.setHandleElId(tipTitle.id);
11495                   dd.lock();
11496               }
11497               inited = true;
11498           }
11499           this.enable(); 
11500        },
11501
11502     /**
11503      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11504      * are supported:
11505      * <pre>
11506 Property    Type                   Description
11507 ----------  ---------------------  ------------------------------------------------------------------------
11508 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11509      * </ul>
11510      * @param {Object} config The config object
11511      */
11512        register : function(config){
11513            var cs = config instanceof Array ? config : arguments;
11514            for(var i = 0, len = cs.length; i < len; i++) {
11515                var c = cs[i];
11516                var target = c.target;
11517                if(target){
11518                    if(target instanceof Array){
11519                        for(var j = 0, jlen = target.length; j < jlen; j++){
11520                            tagEls[target[j]] = c;
11521                        }
11522                    }else{
11523                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11524                    }
11525                }
11526            }
11527        },
11528
11529     /**
11530      * Removes this quick tip from its element and destroys it.
11531      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11532      */
11533        unregister : function(el){
11534            delete tagEls[Roo.id(el)];
11535        },
11536
11537     /**
11538      * Enable this quick tip.
11539      */
11540        enable : function(){
11541            if(inited && disabled){
11542                locks.pop();
11543                if(locks.length < 1){
11544                    disabled = false;
11545                }
11546            }
11547        },
11548
11549     /**
11550      * Disable this quick tip.
11551      */
11552        disable : function(){
11553           disabled = true;
11554           clearTimeout(showProc);
11555           clearTimeout(hideProc);
11556           clearTimeout(dismissProc);
11557           if(ce){
11558               hide(true);
11559           }
11560           locks.push(1);
11561        },
11562
11563     /**
11564      * Returns true if the quick tip is enabled, else false.
11565      */
11566        isEnabled : function(){
11567             return !disabled;
11568        },
11569
11570         // private
11571        tagConfig : {
11572            namespace : "roo", // was ext?? this may break..
11573            alt_namespace : "ext",
11574            attribute : "qtip",
11575            width : "width",
11576            target : "target",
11577            title : "qtitle",
11578            hide : "hide",
11579            cls : "qclass"
11580        }
11581    };
11582 }();
11583
11584 // backwards compat
11585 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11586  * Based on:
11587  * Ext JS Library 1.1.1
11588  * Copyright(c) 2006-2007, Ext JS, LLC.
11589  *
11590  * Originally Released Under LGPL - original licence link has changed is not relivant.
11591  *
11592  * Fork - LGPL
11593  * <script type="text/javascript">
11594  */
11595  
11596
11597 /**
11598  * @class Roo.tree.TreePanel
11599  * @extends Roo.data.Tree
11600
11601  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11602  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11603  * @cfg {Boolean} enableDD true to enable drag and drop
11604  * @cfg {Boolean} enableDrag true to enable just drag
11605  * @cfg {Boolean} enableDrop true to enable just drop
11606  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11607  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11608  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11609  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11610  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11611  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11612  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11613  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11614  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11615  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11616  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11617  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11618  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11619  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11620  * @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>
11621  * @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>
11622  * 
11623  * @constructor
11624  * @param {String/HTMLElement/Element} el The container element
11625  * @param {Object} config
11626  */
11627 Roo.tree.TreePanel = function(el, config){
11628     var root = false;
11629     var loader = false;
11630     if (config.root) {
11631         root = config.root;
11632         delete config.root;
11633     }
11634     if (config.loader) {
11635         loader = config.loader;
11636         delete config.loader;
11637     }
11638     
11639     Roo.apply(this, config);
11640     Roo.tree.TreePanel.superclass.constructor.call(this);
11641     this.el = Roo.get(el);
11642     this.el.addClass('x-tree');
11643     //console.log(root);
11644     if (root) {
11645         this.setRootNode( Roo.factory(root, Roo.tree));
11646     }
11647     if (loader) {
11648         this.loader = Roo.factory(loader, Roo.tree);
11649     }
11650    /**
11651     * Read-only. The id of the container element becomes this TreePanel's id.
11652     */
11653     this.id = this.el.id;
11654     this.addEvents({
11655         /**
11656         * @event beforeload
11657         * Fires before a node is loaded, return false to cancel
11658         * @param {Node} node The node being loaded
11659         */
11660         "beforeload" : true,
11661         /**
11662         * @event load
11663         * Fires when a node is loaded
11664         * @param {Node} node The node that was loaded
11665         */
11666         "load" : true,
11667         /**
11668         * @event textchange
11669         * Fires when the text for a node is changed
11670         * @param {Node} node The node
11671         * @param {String} text The new text
11672         * @param {String} oldText The old text
11673         */
11674         "textchange" : true,
11675         /**
11676         * @event beforeexpand
11677         * Fires before a node is expanded, return false to cancel.
11678         * @param {Node} node The node
11679         * @param {Boolean} deep
11680         * @param {Boolean} anim
11681         */
11682         "beforeexpand" : true,
11683         /**
11684         * @event beforecollapse
11685         * Fires before a node is collapsed, return false to cancel.
11686         * @param {Node} node The node
11687         * @param {Boolean} deep
11688         * @param {Boolean} anim
11689         */
11690         "beforecollapse" : true,
11691         /**
11692         * @event expand
11693         * Fires when a node is expanded
11694         * @param {Node} node The node
11695         */
11696         "expand" : true,
11697         /**
11698         * @event disabledchange
11699         * Fires when the disabled status of a node changes
11700         * @param {Node} node The node
11701         * @param {Boolean} disabled
11702         */
11703         "disabledchange" : true,
11704         /**
11705         * @event collapse
11706         * Fires when a node is collapsed
11707         * @param {Node} node The node
11708         */
11709         "collapse" : true,
11710         /**
11711         * @event beforeclick
11712         * Fires before click processing on a node. Return false to cancel the default action.
11713         * @param {Node} node The node
11714         * @param {Roo.EventObject} e The event object
11715         */
11716         "beforeclick":true,
11717         /**
11718         * @event checkchange
11719         * Fires when a node with a checkbox's checked property changes
11720         * @param {Node} this This node
11721         * @param {Boolean} checked
11722         */
11723         "checkchange":true,
11724         /**
11725         * @event click
11726         * Fires when a node is clicked
11727         * @param {Node} node The node
11728         * @param {Roo.EventObject} e The event object
11729         */
11730         "click":true,
11731         /**
11732         * @event dblclick
11733         * Fires when a node is double clicked
11734         * @param {Node} node The node
11735         * @param {Roo.EventObject} e The event object
11736         */
11737         "dblclick":true,
11738         /**
11739         * @event contextmenu
11740         * Fires when a node is right clicked
11741         * @param {Node} node The node
11742         * @param {Roo.EventObject} e The event object
11743         */
11744         "contextmenu":true,
11745         /**
11746         * @event beforechildrenrendered
11747         * Fires right before the child nodes for a node are rendered
11748         * @param {Node} node The node
11749         */
11750         "beforechildrenrendered":true,
11751         /**
11752         * @event startdrag
11753         * Fires when a node starts being dragged
11754         * @param {Roo.tree.TreePanel} this
11755         * @param {Roo.tree.TreeNode} node
11756         * @param {event} e The raw browser event
11757         */ 
11758        "startdrag" : true,
11759        /**
11760         * @event enddrag
11761         * Fires when a drag operation is complete
11762         * @param {Roo.tree.TreePanel} this
11763         * @param {Roo.tree.TreeNode} node
11764         * @param {event} e The raw browser event
11765         */
11766        "enddrag" : true,
11767        /**
11768         * @event dragdrop
11769         * Fires when a dragged node is dropped on a valid DD target
11770         * @param {Roo.tree.TreePanel} this
11771         * @param {Roo.tree.TreeNode} node
11772         * @param {DD} dd The dd it was dropped on
11773         * @param {event} e The raw browser event
11774         */
11775        "dragdrop" : true,
11776        /**
11777         * @event beforenodedrop
11778         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11779         * passed to handlers has the following properties:<br />
11780         * <ul style="padding:5px;padding-left:16px;">
11781         * <li>tree - The TreePanel</li>
11782         * <li>target - The node being targeted for the drop</li>
11783         * <li>data - The drag data from the drag source</li>
11784         * <li>point - The point of the drop - append, above or below</li>
11785         * <li>source - The drag source</li>
11786         * <li>rawEvent - Raw mouse event</li>
11787         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11788         * to be inserted by setting them on this object.</li>
11789         * <li>cancel - Set this to true to cancel the drop.</li>
11790         * </ul>
11791         * @param {Object} dropEvent
11792         */
11793        "beforenodedrop" : true,
11794        /**
11795         * @event nodedrop
11796         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11797         * passed to handlers has the following properties:<br />
11798         * <ul style="padding:5px;padding-left:16px;">
11799         * <li>tree - The TreePanel</li>
11800         * <li>target - The node being targeted for the drop</li>
11801         * <li>data - The drag data from the drag source</li>
11802         * <li>point - The point of the drop - append, above or below</li>
11803         * <li>source - The drag source</li>
11804         * <li>rawEvent - Raw mouse event</li>
11805         * <li>dropNode - Dropped node(s).</li>
11806         * </ul>
11807         * @param {Object} dropEvent
11808         */
11809        "nodedrop" : true,
11810         /**
11811         * @event nodedragover
11812         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11813         * passed to handlers has the following properties:<br />
11814         * <ul style="padding:5px;padding-left:16px;">
11815         * <li>tree - The TreePanel</li>
11816         * <li>target - The node being targeted for the drop</li>
11817         * <li>data - The drag data from the drag source</li>
11818         * <li>point - The point of the drop - append, above or below</li>
11819         * <li>source - The drag source</li>
11820         * <li>rawEvent - Raw mouse event</li>
11821         * <li>dropNode - Drop node(s) provided by the source.</li>
11822         * <li>cancel - Set this to true to signal drop not allowed.</li>
11823         * </ul>
11824         * @param {Object} dragOverEvent
11825         */
11826        "nodedragover" : true
11827         
11828     });
11829     if(this.singleExpand){
11830        this.on("beforeexpand", this.restrictExpand, this);
11831     }
11832     if (this.editor) {
11833         this.editor.tree = this;
11834         this.editor = Roo.factory(this.editor, Roo.tree);
11835     }
11836     
11837     if (this.selModel) {
11838         this.selModel = Roo.factory(this.selModel, Roo.tree);
11839     }
11840    
11841 };
11842 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11843     rootVisible : true,
11844     animate: Roo.enableFx,
11845     lines : true,
11846     enableDD : false,
11847     hlDrop : Roo.enableFx,
11848   
11849     renderer: false,
11850     
11851     rendererTip: false,
11852     // private
11853     restrictExpand : function(node){
11854         var p = node.parentNode;
11855         if(p){
11856             if(p.expandedChild && p.expandedChild.parentNode == p){
11857                 p.expandedChild.collapse();
11858             }
11859             p.expandedChild = node;
11860         }
11861     },
11862
11863     // private override
11864     setRootNode : function(node){
11865         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11866         if(!this.rootVisible){
11867             node.ui = new Roo.tree.RootTreeNodeUI(node);
11868         }
11869         return node;
11870     },
11871
11872     /**
11873      * Returns the container element for this TreePanel
11874      */
11875     getEl : function(){
11876         return this.el;
11877     },
11878
11879     /**
11880      * Returns the default TreeLoader for this TreePanel
11881      */
11882     getLoader : function(){
11883         return this.loader;
11884     },
11885
11886     /**
11887      * Expand all nodes
11888      */
11889     expandAll : function(){
11890         this.root.expand(true);
11891     },
11892
11893     /**
11894      * Collapse all nodes
11895      */
11896     collapseAll : function(){
11897         this.root.collapse(true);
11898     },
11899
11900     /**
11901      * Returns the selection model used by this TreePanel
11902      */
11903     getSelectionModel : function(){
11904         if(!this.selModel){
11905             this.selModel = new Roo.tree.DefaultSelectionModel();
11906         }
11907         return this.selModel;
11908     },
11909
11910     /**
11911      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11912      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11913      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11914      * @return {Array}
11915      */
11916     getChecked : function(a, startNode){
11917         startNode = startNode || this.root;
11918         var r = [];
11919         var f = function(){
11920             if(this.attributes.checked){
11921                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11922             }
11923         }
11924         startNode.cascade(f);
11925         return r;
11926     },
11927
11928     /**
11929      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11930      * @param {String} path
11931      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11932      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11933      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11934      */
11935     expandPath : function(path, attr, callback){
11936         attr = attr || "id";
11937         var keys = path.split(this.pathSeparator);
11938         var curNode = this.root;
11939         if(curNode.attributes[attr] != keys[1]){ // invalid root
11940             if(callback){
11941                 callback(false, null);
11942             }
11943             return;
11944         }
11945         var index = 1;
11946         var f = function(){
11947             if(++index == keys.length){
11948                 if(callback){
11949                     callback(true, curNode);
11950                 }
11951                 return;
11952             }
11953             var c = curNode.findChild(attr, keys[index]);
11954             if(!c){
11955                 if(callback){
11956                     callback(false, curNode);
11957                 }
11958                 return;
11959             }
11960             curNode = c;
11961             c.expand(false, false, f);
11962         };
11963         curNode.expand(false, false, f);
11964     },
11965
11966     /**
11967      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11968      * @param {String} path
11969      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11970      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11971      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11972      */
11973     selectPath : function(path, attr, callback){
11974         attr = attr || "id";
11975         var keys = path.split(this.pathSeparator);
11976         var v = keys.pop();
11977         if(keys.length > 0){
11978             var f = function(success, node){
11979                 if(success && node){
11980                     var n = node.findChild(attr, v);
11981                     if(n){
11982                         n.select();
11983                         if(callback){
11984                             callback(true, n);
11985                         }
11986                     }else if(callback){
11987                         callback(false, n);
11988                     }
11989                 }else{
11990                     if(callback){
11991                         callback(false, n);
11992                     }
11993                 }
11994             };
11995             this.expandPath(keys.join(this.pathSeparator), attr, f);
11996         }else{
11997             this.root.select();
11998             if(callback){
11999                 callback(true, this.root);
12000             }
12001         }
12002     },
12003
12004     getTreeEl : function(){
12005         return this.el;
12006     },
12007
12008     /**
12009      * Trigger rendering of this TreePanel
12010      */
12011     render : function(){
12012         if (this.innerCt) {
12013             return this; // stop it rendering more than once!!
12014         }
12015         
12016         this.innerCt = this.el.createChild({tag:"ul",
12017                cls:"x-tree-root-ct " +
12018                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12019
12020         if(this.containerScroll){
12021             Roo.dd.ScrollManager.register(this.el);
12022         }
12023         if((this.enableDD || this.enableDrop) && !this.dropZone){
12024            /**
12025             * The dropZone used by this tree if drop is enabled
12026             * @type Roo.tree.TreeDropZone
12027             */
12028              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12029                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12030            });
12031         }
12032         if((this.enableDD || this.enableDrag) && !this.dragZone){
12033            /**
12034             * The dragZone used by this tree if drag is enabled
12035             * @type Roo.tree.TreeDragZone
12036             */
12037             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12038                ddGroup: this.ddGroup || "TreeDD",
12039                scroll: this.ddScroll
12040            });
12041         }
12042         this.getSelectionModel().init(this);
12043         if (!this.root) {
12044             Roo.log("ROOT not set in tree");
12045             return this;
12046         }
12047         this.root.render();
12048         if(!this.rootVisible){
12049             this.root.renderChildren();
12050         }
12051         return this;
12052     }
12053 });/*
12054  * Based on:
12055  * Ext JS Library 1.1.1
12056  * Copyright(c) 2006-2007, Ext JS, LLC.
12057  *
12058  * Originally Released Under LGPL - original licence link has changed is not relivant.
12059  *
12060  * Fork - LGPL
12061  * <script type="text/javascript">
12062  */
12063  
12064
12065 /**
12066  * @class Roo.tree.DefaultSelectionModel
12067  * @extends Roo.util.Observable
12068  * The default single selection for a TreePanel.
12069  * @param {Object} cfg Configuration
12070  */
12071 Roo.tree.DefaultSelectionModel = function(cfg){
12072    this.selNode = null;
12073    
12074    
12075    
12076    this.addEvents({
12077        /**
12078         * @event selectionchange
12079         * Fires when the selected node changes
12080         * @param {DefaultSelectionModel} this
12081         * @param {TreeNode} node the new selection
12082         */
12083        "selectionchange" : true,
12084
12085        /**
12086         * @event beforeselect
12087         * Fires before the selected node changes, return false to cancel the change
12088         * @param {DefaultSelectionModel} this
12089         * @param {TreeNode} node the new selection
12090         * @param {TreeNode} node the old selection
12091         */
12092        "beforeselect" : true
12093    });
12094    
12095     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12096 };
12097
12098 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12099     init : function(tree){
12100         this.tree = tree;
12101         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12102         tree.on("click", this.onNodeClick, this);
12103     },
12104     
12105     onNodeClick : function(node, e){
12106         if (e.ctrlKey && this.selNode == node)  {
12107             this.unselect(node);
12108             return;
12109         }
12110         this.select(node);
12111     },
12112     
12113     /**
12114      * Select a node.
12115      * @param {TreeNode} node The node to select
12116      * @return {TreeNode} The selected node
12117      */
12118     select : function(node){
12119         var last = this.selNode;
12120         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12121             if(last){
12122                 last.ui.onSelectedChange(false);
12123             }
12124             this.selNode = node;
12125             node.ui.onSelectedChange(true);
12126             this.fireEvent("selectionchange", this, node, last);
12127         }
12128         return node;
12129     },
12130     
12131     /**
12132      * Deselect a node.
12133      * @param {TreeNode} node The node to unselect
12134      */
12135     unselect : function(node){
12136         if(this.selNode == node){
12137             this.clearSelections();
12138         }    
12139     },
12140     
12141     /**
12142      * Clear all selections
12143      */
12144     clearSelections : function(){
12145         var n = this.selNode;
12146         if(n){
12147             n.ui.onSelectedChange(false);
12148             this.selNode = null;
12149             this.fireEvent("selectionchange", this, null);
12150         }
12151         return n;
12152     },
12153     
12154     /**
12155      * Get the selected node
12156      * @return {TreeNode} The selected node
12157      */
12158     getSelectedNode : function(){
12159         return this.selNode;    
12160     },
12161     
12162     /**
12163      * Returns true if the node is selected
12164      * @param {TreeNode} node The node to check
12165      * @return {Boolean}
12166      */
12167     isSelected : function(node){
12168         return this.selNode == node;  
12169     },
12170
12171     /**
12172      * Selects the node above the selected node in the tree, intelligently walking the nodes
12173      * @return TreeNode The new selection
12174      */
12175     selectPrevious : function(){
12176         var s = this.selNode || this.lastSelNode;
12177         if(!s){
12178             return null;
12179         }
12180         var ps = s.previousSibling;
12181         if(ps){
12182             if(!ps.isExpanded() || ps.childNodes.length < 1){
12183                 return this.select(ps);
12184             } else{
12185                 var lc = ps.lastChild;
12186                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12187                     lc = lc.lastChild;
12188                 }
12189                 return this.select(lc);
12190             }
12191         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12192             return this.select(s.parentNode);
12193         }
12194         return null;
12195     },
12196
12197     /**
12198      * Selects the node above the selected node in the tree, intelligently walking the nodes
12199      * @return TreeNode The new selection
12200      */
12201     selectNext : function(){
12202         var s = this.selNode || this.lastSelNode;
12203         if(!s){
12204             return null;
12205         }
12206         if(s.firstChild && s.isExpanded()){
12207              return this.select(s.firstChild);
12208          }else if(s.nextSibling){
12209              return this.select(s.nextSibling);
12210          }else if(s.parentNode){
12211             var newS = null;
12212             s.parentNode.bubble(function(){
12213                 if(this.nextSibling){
12214                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12215                     return false;
12216                 }
12217             });
12218             return newS;
12219          }
12220         return null;
12221     },
12222
12223     onKeyDown : function(e){
12224         var s = this.selNode || this.lastSelNode;
12225         // undesirable, but required
12226         var sm = this;
12227         if(!s){
12228             return;
12229         }
12230         var k = e.getKey();
12231         switch(k){
12232              case e.DOWN:
12233                  e.stopEvent();
12234                  this.selectNext();
12235              break;
12236              case e.UP:
12237                  e.stopEvent();
12238                  this.selectPrevious();
12239              break;
12240              case e.RIGHT:
12241                  e.preventDefault();
12242                  if(s.hasChildNodes()){
12243                      if(!s.isExpanded()){
12244                          s.expand();
12245                      }else if(s.firstChild){
12246                          this.select(s.firstChild, e);
12247                      }
12248                  }
12249              break;
12250              case e.LEFT:
12251                  e.preventDefault();
12252                  if(s.hasChildNodes() && s.isExpanded()){
12253                      s.collapse();
12254                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12255                      this.select(s.parentNode, e);
12256                  }
12257              break;
12258         };
12259     }
12260 });
12261
12262 /**
12263  * @class Roo.tree.MultiSelectionModel
12264  * @extends Roo.util.Observable
12265  * Multi selection for a TreePanel.
12266  * @param {Object} cfg Configuration
12267  */
12268 Roo.tree.MultiSelectionModel = function(){
12269    this.selNodes = [];
12270    this.selMap = {};
12271    this.addEvents({
12272        /**
12273         * @event selectionchange
12274         * Fires when the selected nodes change
12275         * @param {MultiSelectionModel} this
12276         * @param {Array} nodes Array of the selected nodes
12277         */
12278        "selectionchange" : true
12279    });
12280    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12281    
12282 };
12283
12284 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12285     init : function(tree){
12286         this.tree = tree;
12287         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12288         tree.on("click", this.onNodeClick, this);
12289     },
12290     
12291     onNodeClick : function(node, e){
12292         this.select(node, e, e.ctrlKey);
12293     },
12294     
12295     /**
12296      * Select a node.
12297      * @param {TreeNode} node The node to select
12298      * @param {EventObject} e (optional) An event associated with the selection
12299      * @param {Boolean} keepExisting True to retain existing selections
12300      * @return {TreeNode} The selected node
12301      */
12302     select : function(node, e, keepExisting){
12303         if(keepExisting !== true){
12304             this.clearSelections(true);
12305         }
12306         if(this.isSelected(node)){
12307             this.lastSelNode = node;
12308             return node;
12309         }
12310         this.selNodes.push(node);
12311         this.selMap[node.id] = node;
12312         this.lastSelNode = node;
12313         node.ui.onSelectedChange(true);
12314         this.fireEvent("selectionchange", this, this.selNodes);
12315         return node;
12316     },
12317     
12318     /**
12319      * Deselect a node.
12320      * @param {TreeNode} node The node to unselect
12321      */
12322     unselect : function(node){
12323         if(this.selMap[node.id]){
12324             node.ui.onSelectedChange(false);
12325             var sn = this.selNodes;
12326             var index = -1;
12327             if(sn.indexOf){
12328                 index = sn.indexOf(node);
12329             }else{
12330                 for(var i = 0, len = sn.length; i < len; i++){
12331                     if(sn[i] == node){
12332                         index = i;
12333                         break;
12334                     }
12335                 }
12336             }
12337             if(index != -1){
12338                 this.selNodes.splice(index, 1);
12339             }
12340             delete this.selMap[node.id];
12341             this.fireEvent("selectionchange", this, this.selNodes);
12342         }
12343     },
12344     
12345     /**
12346      * Clear all selections
12347      */
12348     clearSelections : function(suppressEvent){
12349         var sn = this.selNodes;
12350         if(sn.length > 0){
12351             for(var i = 0, len = sn.length; i < len; i++){
12352                 sn[i].ui.onSelectedChange(false);
12353             }
12354             this.selNodes = [];
12355             this.selMap = {};
12356             if(suppressEvent !== true){
12357                 this.fireEvent("selectionchange", this, this.selNodes);
12358             }
12359         }
12360     },
12361     
12362     /**
12363      * Returns true if the node is selected
12364      * @param {TreeNode} node The node to check
12365      * @return {Boolean}
12366      */
12367     isSelected : function(node){
12368         return this.selMap[node.id] ? true : false;  
12369     },
12370     
12371     /**
12372      * Returns an array of the selected nodes
12373      * @return {Array}
12374      */
12375     getSelectedNodes : function(){
12376         return this.selNodes;    
12377     },
12378
12379     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12380
12381     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12382
12383     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12384 });/*
12385  * Based on:
12386  * Ext JS Library 1.1.1
12387  * Copyright(c) 2006-2007, Ext JS, LLC.
12388  *
12389  * Originally Released Under LGPL - original licence link has changed is not relivant.
12390  *
12391  * Fork - LGPL
12392  * <script type="text/javascript">
12393  */
12394  
12395 /**
12396  * @class Roo.tree.TreeNode
12397  * @extends Roo.data.Node
12398  * @cfg {String} text The text for this node
12399  * @cfg {Boolean} expanded true to start the node expanded
12400  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12401  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12402  * @cfg {Boolean} disabled true to start the node disabled
12403  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12404  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12405  * @cfg {String} cls A css class to be added to the node
12406  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12407  * @cfg {String} href URL of the link used for the node (defaults to #)
12408  * @cfg {String} hrefTarget target frame for the link
12409  * @cfg {String} qtip An Ext QuickTip for the node
12410  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12411  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12412  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12413  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12414  * (defaults to undefined with no checkbox rendered)
12415  * @constructor
12416  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12417  */
12418 Roo.tree.TreeNode = function(attributes){
12419     attributes = attributes || {};
12420     if(typeof attributes == "string"){
12421         attributes = {text: attributes};
12422     }
12423     this.childrenRendered = false;
12424     this.rendered = false;
12425     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12426     this.expanded = attributes.expanded === true;
12427     this.isTarget = attributes.isTarget !== false;
12428     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12429     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12430
12431     /**
12432      * Read-only. The text for this node. To change it use setText().
12433      * @type String
12434      */
12435     this.text = attributes.text;
12436     /**
12437      * True if this node is disabled.
12438      * @type Boolean
12439      */
12440     this.disabled = attributes.disabled === true;
12441
12442     this.addEvents({
12443         /**
12444         * @event textchange
12445         * Fires when the text for this node is changed
12446         * @param {Node} this This node
12447         * @param {String} text The new text
12448         * @param {String} oldText The old text
12449         */
12450         "textchange" : true,
12451         /**
12452         * @event beforeexpand
12453         * Fires before this node is expanded, return false to cancel.
12454         * @param {Node} this This node
12455         * @param {Boolean} deep
12456         * @param {Boolean} anim
12457         */
12458         "beforeexpand" : true,
12459         /**
12460         * @event beforecollapse
12461         * Fires before this node is collapsed, return false to cancel.
12462         * @param {Node} this This node
12463         * @param {Boolean} deep
12464         * @param {Boolean} anim
12465         */
12466         "beforecollapse" : true,
12467         /**
12468         * @event expand
12469         * Fires when this node is expanded
12470         * @param {Node} this This node
12471         */
12472         "expand" : true,
12473         /**
12474         * @event disabledchange
12475         * Fires when the disabled status of this node changes
12476         * @param {Node} this This node
12477         * @param {Boolean} disabled
12478         */
12479         "disabledchange" : true,
12480         /**
12481         * @event collapse
12482         * Fires when this node is collapsed
12483         * @param {Node} this This node
12484         */
12485         "collapse" : true,
12486         /**
12487         * @event beforeclick
12488         * Fires before click processing. Return false to cancel the default action.
12489         * @param {Node} this This node
12490         * @param {Roo.EventObject} e The event object
12491         */
12492         "beforeclick":true,
12493         /**
12494         * @event checkchange
12495         * Fires when a node with a checkbox's checked property changes
12496         * @param {Node} this This node
12497         * @param {Boolean} checked
12498         */
12499         "checkchange":true,
12500         /**
12501         * @event click
12502         * Fires when this node is clicked
12503         * @param {Node} this This node
12504         * @param {Roo.EventObject} e The event object
12505         */
12506         "click":true,
12507         /**
12508         * @event dblclick
12509         * Fires when this node is double clicked
12510         * @param {Node} this This node
12511         * @param {Roo.EventObject} e The event object
12512         */
12513         "dblclick":true,
12514         /**
12515         * @event contextmenu
12516         * Fires when this node is right clicked
12517         * @param {Node} this This node
12518         * @param {Roo.EventObject} e The event object
12519         */
12520         "contextmenu":true,
12521         /**
12522         * @event beforechildrenrendered
12523         * Fires right before the child nodes for this node are rendered
12524         * @param {Node} this This node
12525         */
12526         "beforechildrenrendered":true
12527     });
12528
12529     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12530
12531     /**
12532      * Read-only. The UI for this node
12533      * @type TreeNodeUI
12534      */
12535     this.ui = new uiClass(this);
12536     
12537     // finally support items[]
12538     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12539         return;
12540     }
12541     
12542     
12543     Roo.each(this.attributes.items, function(c) {
12544         this.appendChild(Roo.factory(c,Roo.Tree));
12545     }, this);
12546     delete this.attributes.items;
12547     
12548     
12549     
12550 };
12551 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12552     preventHScroll: true,
12553     /**
12554      * Returns true if this node is expanded
12555      * @return {Boolean}
12556      */
12557     isExpanded : function(){
12558         return this.expanded;
12559     },
12560
12561     /**
12562      * Returns the UI object for this node
12563      * @return {TreeNodeUI}
12564      */
12565     getUI : function(){
12566         return this.ui;
12567     },
12568
12569     // private override
12570     setFirstChild : function(node){
12571         var of = this.firstChild;
12572         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12573         if(this.childrenRendered && of && node != of){
12574             of.renderIndent(true, true);
12575         }
12576         if(this.rendered){
12577             this.renderIndent(true, true);
12578         }
12579     },
12580
12581     // private override
12582     setLastChild : function(node){
12583         var ol = this.lastChild;
12584         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12585         if(this.childrenRendered && ol && node != ol){
12586             ol.renderIndent(true, true);
12587         }
12588         if(this.rendered){
12589             this.renderIndent(true, true);
12590         }
12591     },
12592
12593     // these methods are overridden to provide lazy rendering support
12594     // private override
12595     appendChild : function()
12596     {
12597         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12598         if(node && this.childrenRendered){
12599             node.render();
12600         }
12601         this.ui.updateExpandIcon();
12602         return node;
12603     },
12604
12605     // private override
12606     removeChild : function(node){
12607         this.ownerTree.getSelectionModel().unselect(node);
12608         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12609         // if it's been rendered remove dom node
12610         if(this.childrenRendered){
12611             node.ui.remove();
12612         }
12613         if(this.childNodes.length < 1){
12614             this.collapse(false, false);
12615         }else{
12616             this.ui.updateExpandIcon();
12617         }
12618         if(!this.firstChild) {
12619             this.childrenRendered = false;
12620         }
12621         return node;
12622     },
12623
12624     // private override
12625     insertBefore : function(node, refNode){
12626         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12627         if(newNode && refNode && this.childrenRendered){
12628             node.render();
12629         }
12630         this.ui.updateExpandIcon();
12631         return newNode;
12632     },
12633
12634     /**
12635      * Sets the text for this node
12636      * @param {String} text
12637      */
12638     setText : function(text){
12639         var oldText = this.text;
12640         this.text = text;
12641         this.attributes.text = text;
12642         if(this.rendered){ // event without subscribing
12643             this.ui.onTextChange(this, text, oldText);
12644         }
12645         this.fireEvent("textchange", this, text, oldText);
12646     },
12647
12648     /**
12649      * Triggers selection of this node
12650      */
12651     select : function(){
12652         this.getOwnerTree().getSelectionModel().select(this);
12653     },
12654
12655     /**
12656      * Triggers deselection of this node
12657      */
12658     unselect : function(){
12659         this.getOwnerTree().getSelectionModel().unselect(this);
12660     },
12661
12662     /**
12663      * Returns true if this node is selected
12664      * @return {Boolean}
12665      */
12666     isSelected : function(){
12667         return this.getOwnerTree().getSelectionModel().isSelected(this);
12668     },
12669
12670     /**
12671      * Expand this node.
12672      * @param {Boolean} deep (optional) True to expand all children as well
12673      * @param {Boolean} anim (optional) false to cancel the default animation
12674      * @param {Function} callback (optional) A callback to be called when
12675      * expanding this node completes (does not wait for deep expand to complete).
12676      * Called with 1 parameter, this node.
12677      */
12678     expand : function(deep, anim, callback){
12679         if(!this.expanded){
12680             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12681                 return;
12682             }
12683             if(!this.childrenRendered){
12684                 this.renderChildren();
12685             }
12686             this.expanded = true;
12687             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
12688                 this.ui.animExpand(function(){
12689                     this.fireEvent("expand", this);
12690                     if(typeof callback == "function"){
12691                         callback(this);
12692                     }
12693                     if(deep === true){
12694                         this.expandChildNodes(true);
12695                     }
12696                 }.createDelegate(this));
12697                 return;
12698             }else{
12699                 this.ui.expand();
12700                 this.fireEvent("expand", this);
12701                 if(typeof callback == "function"){
12702                     callback(this);
12703                 }
12704             }
12705         }else{
12706            if(typeof callback == "function"){
12707                callback(this);
12708            }
12709         }
12710         if(deep === true){
12711             this.expandChildNodes(true);
12712         }
12713     },
12714
12715     isHiddenRoot : function(){
12716         return this.isRoot && !this.getOwnerTree().rootVisible;
12717     },
12718
12719     /**
12720      * Collapse this node.
12721      * @param {Boolean} deep (optional) True to collapse all children as well
12722      * @param {Boolean} anim (optional) false to cancel the default animation
12723      */
12724     collapse : function(deep, anim){
12725         if(this.expanded && !this.isHiddenRoot()){
12726             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12727                 return;
12728             }
12729             this.expanded = false;
12730             if((this.getOwnerTree().animate && anim !== false) || anim){
12731                 this.ui.animCollapse(function(){
12732                     this.fireEvent("collapse", this);
12733                     if(deep === true){
12734                         this.collapseChildNodes(true);
12735                     }
12736                 }.createDelegate(this));
12737                 return;
12738             }else{
12739                 this.ui.collapse();
12740                 this.fireEvent("collapse", this);
12741             }
12742         }
12743         if(deep === true){
12744             var cs = this.childNodes;
12745             for(var i = 0, len = cs.length; i < len; i++) {
12746                 cs[i].collapse(true, false);
12747             }
12748         }
12749     },
12750
12751     // private
12752     delayedExpand : function(delay){
12753         if(!this.expandProcId){
12754             this.expandProcId = this.expand.defer(delay, this);
12755         }
12756     },
12757
12758     // private
12759     cancelExpand : function(){
12760         if(this.expandProcId){
12761             clearTimeout(this.expandProcId);
12762         }
12763         this.expandProcId = false;
12764     },
12765
12766     /**
12767      * Toggles expanded/collapsed state of the node
12768      */
12769     toggle : function(){
12770         if(this.expanded){
12771             this.collapse();
12772         }else{
12773             this.expand();
12774         }
12775     },
12776
12777     /**
12778      * Ensures all parent nodes are expanded
12779      */
12780     ensureVisible : function(callback){
12781         var tree = this.getOwnerTree();
12782         tree.expandPath(this.parentNode.getPath(), false, function(){
12783             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12784             Roo.callback(callback);
12785         }.createDelegate(this));
12786     },
12787
12788     /**
12789      * Expand all child nodes
12790      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12791      */
12792     expandChildNodes : function(deep){
12793         var cs = this.childNodes;
12794         for(var i = 0, len = cs.length; i < len; i++) {
12795                 cs[i].expand(deep);
12796         }
12797     },
12798
12799     /**
12800      * Collapse all child nodes
12801      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12802      */
12803     collapseChildNodes : function(deep){
12804         var cs = this.childNodes;
12805         for(var i = 0, len = cs.length; i < len; i++) {
12806                 cs[i].collapse(deep);
12807         }
12808     },
12809
12810     /**
12811      * Disables this node
12812      */
12813     disable : function(){
12814         this.disabled = true;
12815         this.unselect();
12816         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12817             this.ui.onDisableChange(this, true);
12818         }
12819         this.fireEvent("disabledchange", this, true);
12820     },
12821
12822     /**
12823      * Enables this node
12824      */
12825     enable : function(){
12826         this.disabled = false;
12827         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12828             this.ui.onDisableChange(this, false);
12829         }
12830         this.fireEvent("disabledchange", this, false);
12831     },
12832
12833     // private
12834     renderChildren : function(suppressEvent){
12835         if(suppressEvent !== false){
12836             this.fireEvent("beforechildrenrendered", this);
12837         }
12838         var cs = this.childNodes;
12839         for(var i = 0, len = cs.length; i < len; i++){
12840             cs[i].render(true);
12841         }
12842         this.childrenRendered = true;
12843     },
12844
12845     // private
12846     sort : function(fn, scope){
12847         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12848         if(this.childrenRendered){
12849             var cs = this.childNodes;
12850             for(var i = 0, len = cs.length; i < len; i++){
12851                 cs[i].render(true);
12852             }
12853         }
12854     },
12855
12856     // private
12857     render : function(bulkRender){
12858         this.ui.render(bulkRender);
12859         if(!this.rendered){
12860             this.rendered = true;
12861             if(this.expanded){
12862                 this.expanded = false;
12863                 this.expand(false, false);
12864             }
12865         }
12866     },
12867
12868     // private
12869     renderIndent : function(deep, refresh){
12870         if(refresh){
12871             this.ui.childIndent = null;
12872         }
12873         this.ui.renderIndent();
12874         if(deep === true && this.childrenRendered){
12875             var cs = this.childNodes;
12876             for(var i = 0, len = cs.length; i < len; i++){
12877                 cs[i].renderIndent(true, refresh);
12878             }
12879         }
12880     }
12881 });/*
12882  * Based on:
12883  * Ext JS Library 1.1.1
12884  * Copyright(c) 2006-2007, Ext JS, LLC.
12885  *
12886  * Originally Released Under LGPL - original licence link has changed is not relivant.
12887  *
12888  * Fork - LGPL
12889  * <script type="text/javascript">
12890  */
12891  
12892 /**
12893  * @class Roo.tree.AsyncTreeNode
12894  * @extends Roo.tree.TreeNode
12895  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12896  * @constructor
12897  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12898  */
12899  Roo.tree.AsyncTreeNode = function(config){
12900     this.loaded = false;
12901     this.loading = false;
12902     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12903     /**
12904     * @event beforeload
12905     * Fires before this node is loaded, return false to cancel
12906     * @param {Node} this This node
12907     */
12908     this.addEvents({'beforeload':true, 'load': true});
12909     /**
12910     * @event load
12911     * Fires when this node is loaded
12912     * @param {Node} this This node
12913     */
12914     /**
12915      * The loader used by this node (defaults to using the tree's defined loader)
12916      * @type TreeLoader
12917      * @property loader
12918      */
12919 };
12920 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12921     expand : function(deep, anim, callback){
12922         if(this.loading){ // if an async load is already running, waiting til it's done
12923             var timer;
12924             var f = function(){
12925                 if(!this.loading){ // done loading
12926                     clearInterval(timer);
12927                     this.expand(deep, anim, callback);
12928                 }
12929             }.createDelegate(this);
12930             timer = setInterval(f, 200);
12931             return;
12932         }
12933         if(!this.loaded){
12934             if(this.fireEvent("beforeload", this) === false){
12935                 return;
12936             }
12937             this.loading = true;
12938             this.ui.beforeLoad(this);
12939             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12940             if(loader){
12941                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12942                 return;
12943             }
12944         }
12945         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12946     },
12947     
12948     /**
12949      * Returns true if this node is currently loading
12950      * @return {Boolean}
12951      */
12952     isLoading : function(){
12953         return this.loading;  
12954     },
12955     
12956     loadComplete : function(deep, anim, callback){
12957         this.loading = false;
12958         this.loaded = true;
12959         this.ui.afterLoad(this);
12960         this.fireEvent("load", this);
12961         this.expand(deep, anim, callback);
12962     },
12963     
12964     /**
12965      * Returns true if this node has been loaded
12966      * @return {Boolean}
12967      */
12968     isLoaded : function(){
12969         return this.loaded;
12970     },
12971     
12972     hasChildNodes : function(){
12973         if(!this.isLeaf() && !this.loaded){
12974             return true;
12975         }else{
12976             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12977         }
12978     },
12979
12980     /**
12981      * Trigger a reload for this node
12982      * @param {Function} callback
12983      */
12984     reload : function(callback){
12985         this.collapse(false, false);
12986         while(this.firstChild){
12987             this.removeChild(this.firstChild);
12988         }
12989         this.childrenRendered = false;
12990         this.loaded = false;
12991         if(this.isHiddenRoot()){
12992             this.expanded = false;
12993         }
12994         this.expand(false, false, callback);
12995     }
12996 });/*
12997  * Based on:
12998  * Ext JS Library 1.1.1
12999  * Copyright(c) 2006-2007, Ext JS, LLC.
13000  *
13001  * Originally Released Under LGPL - original licence link has changed is not relivant.
13002  *
13003  * Fork - LGPL
13004  * <script type="text/javascript">
13005  */
13006  
13007 /**
13008  * @class Roo.tree.TreeNodeUI
13009  * @constructor
13010  * @param {Object} node The node to render
13011  * The TreeNode UI implementation is separate from the
13012  * tree implementation. Unless you are customizing the tree UI,
13013  * you should never have to use this directly.
13014  */
13015 Roo.tree.TreeNodeUI = function(node){
13016     this.node = node;
13017     this.rendered = false;
13018     this.animating = false;
13019     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13020 };
13021
13022 Roo.tree.TreeNodeUI.prototype = {
13023     removeChild : function(node){
13024         if(this.rendered){
13025             this.ctNode.removeChild(node.ui.getEl());
13026         }
13027     },
13028
13029     beforeLoad : function(){
13030          this.addClass("x-tree-node-loading");
13031     },
13032
13033     afterLoad : function(){
13034          this.removeClass("x-tree-node-loading");
13035     },
13036
13037     onTextChange : function(node, text, oldText){
13038         if(this.rendered){
13039             this.textNode.innerHTML = text;
13040         }
13041     },
13042
13043     onDisableChange : function(node, state){
13044         this.disabled = state;
13045         if(state){
13046             this.addClass("x-tree-node-disabled");
13047         }else{
13048             this.removeClass("x-tree-node-disabled");
13049         }
13050     },
13051
13052     onSelectedChange : function(state){
13053         if(state){
13054             this.focus();
13055             this.addClass("x-tree-selected");
13056         }else{
13057             //this.blur();
13058             this.removeClass("x-tree-selected");
13059         }
13060     },
13061
13062     onMove : function(tree, node, oldParent, newParent, index, refNode){
13063         this.childIndent = null;
13064         if(this.rendered){
13065             var targetNode = newParent.ui.getContainer();
13066             if(!targetNode){//target not rendered
13067                 this.holder = document.createElement("div");
13068                 this.holder.appendChild(this.wrap);
13069                 return;
13070             }
13071             var insertBefore = refNode ? refNode.ui.getEl() : null;
13072             if(insertBefore){
13073                 targetNode.insertBefore(this.wrap, insertBefore);
13074             }else{
13075                 targetNode.appendChild(this.wrap);
13076             }
13077             this.node.renderIndent(true);
13078         }
13079     },
13080
13081     addClass : function(cls){
13082         if(this.elNode){
13083             Roo.fly(this.elNode).addClass(cls);
13084         }
13085     },
13086
13087     removeClass : function(cls){
13088         if(this.elNode){
13089             Roo.fly(this.elNode).removeClass(cls);
13090         }
13091     },
13092
13093     remove : function(){
13094         if(this.rendered){
13095             this.holder = document.createElement("div");
13096             this.holder.appendChild(this.wrap);
13097         }
13098     },
13099
13100     fireEvent : function(){
13101         return this.node.fireEvent.apply(this.node, arguments);
13102     },
13103
13104     initEvents : function(){
13105         this.node.on("move", this.onMove, this);
13106         var E = Roo.EventManager;
13107         var a = this.anchor;
13108
13109         var el = Roo.fly(a, '_treeui');
13110
13111         if(Roo.isOpera){ // opera render bug ignores the CSS
13112             el.setStyle("text-decoration", "none");
13113         }
13114
13115         el.on("click", this.onClick, this);
13116         el.on("dblclick", this.onDblClick, this);
13117
13118         if(this.checkbox){
13119             Roo.EventManager.on(this.checkbox,
13120                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13121         }
13122
13123         el.on("contextmenu", this.onContextMenu, this);
13124
13125         var icon = Roo.fly(this.iconNode);
13126         icon.on("click", this.onClick, this);
13127         icon.on("dblclick", this.onDblClick, this);
13128         icon.on("contextmenu", this.onContextMenu, this);
13129         E.on(this.ecNode, "click", this.ecClick, this, true);
13130
13131         if(this.node.disabled){
13132             this.addClass("x-tree-node-disabled");
13133         }
13134         if(this.node.hidden){
13135             this.addClass("x-tree-node-disabled");
13136         }
13137         var ot = this.node.getOwnerTree();
13138         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
13139         if(dd && (!this.node.isRoot || ot.rootVisible)){
13140             Roo.dd.Registry.register(this.elNode, {
13141                 node: this.node,
13142                 handles: this.getDDHandles(),
13143                 isHandle: false
13144             });
13145         }
13146     },
13147
13148     getDDHandles : function(){
13149         return [this.iconNode, this.textNode];
13150     },
13151
13152     hide : function(){
13153         if(this.rendered){
13154             this.wrap.style.display = "none";
13155         }
13156     },
13157
13158     show : function(){
13159         if(this.rendered){
13160             this.wrap.style.display = "";
13161         }
13162     },
13163
13164     onContextMenu : function(e){
13165         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13166             e.preventDefault();
13167             this.focus();
13168             this.fireEvent("contextmenu", this.node, e);
13169         }
13170     },
13171
13172     onClick : function(e){
13173         if(this.dropping){
13174             e.stopEvent();
13175             return;
13176         }
13177         if(this.fireEvent("beforeclick", this.node, e) !== false){
13178             if(!this.disabled && this.node.attributes.href){
13179                 this.fireEvent("click", this.node, e);
13180                 return;
13181             }
13182             e.preventDefault();
13183             if(this.disabled){
13184                 return;
13185             }
13186
13187             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13188                 this.node.toggle();
13189             }
13190
13191             this.fireEvent("click", this.node, e);
13192         }else{
13193             e.stopEvent();
13194         }
13195     },
13196
13197     onDblClick : function(e){
13198         e.preventDefault();
13199         if(this.disabled){
13200             return;
13201         }
13202         if(this.checkbox){
13203             this.toggleCheck();
13204         }
13205         if(!this.animating && this.node.hasChildNodes()){
13206             this.node.toggle();
13207         }
13208         this.fireEvent("dblclick", this.node, e);
13209     },
13210
13211     onCheckChange : function(){
13212         var checked = this.checkbox.checked;
13213         this.node.attributes.checked = checked;
13214         this.fireEvent('checkchange', this.node, checked);
13215     },
13216
13217     ecClick : function(e){
13218         if(!this.animating && this.node.hasChildNodes()){
13219             this.node.toggle();
13220         }
13221     },
13222
13223     startDrop : function(){
13224         this.dropping = true;
13225     },
13226
13227     // delayed drop so the click event doesn't get fired on a drop
13228     endDrop : function(){
13229        setTimeout(function(){
13230            this.dropping = false;
13231        }.createDelegate(this), 50);
13232     },
13233
13234     expand : function(){
13235         this.updateExpandIcon();
13236         this.ctNode.style.display = "";
13237     },
13238
13239     focus : function(){
13240         if(!this.node.preventHScroll){
13241             try{this.anchor.focus();
13242             }catch(e){}
13243         }else if(!Roo.isIE){
13244             try{
13245                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13246                 var l = noscroll.scrollLeft;
13247                 this.anchor.focus();
13248                 noscroll.scrollLeft = l;
13249             }catch(e){}
13250         }
13251     },
13252
13253     toggleCheck : function(value){
13254         var cb = this.checkbox;
13255         if(cb){
13256             cb.checked = (value === undefined ? !cb.checked : value);
13257         }
13258     },
13259
13260     blur : function(){
13261         try{
13262             this.anchor.blur();
13263         }catch(e){}
13264     },
13265
13266     animExpand : function(callback){
13267         var ct = Roo.get(this.ctNode);
13268         ct.stopFx();
13269         if(!this.node.hasChildNodes()){
13270             this.updateExpandIcon();
13271             this.ctNode.style.display = "";
13272             Roo.callback(callback);
13273             return;
13274         }
13275         this.animating = true;
13276         this.updateExpandIcon();
13277
13278         ct.slideIn('t', {
13279            callback : function(){
13280                this.animating = false;
13281                Roo.callback(callback);
13282             },
13283             scope: this,
13284             duration: this.node.ownerTree.duration || .25
13285         });
13286     },
13287
13288     highlight : function(){
13289         var tree = this.node.getOwnerTree();
13290         Roo.fly(this.wrap).highlight(
13291             tree.hlColor || "C3DAF9",
13292             {endColor: tree.hlBaseColor}
13293         );
13294     },
13295
13296     collapse : function(){
13297         this.updateExpandIcon();
13298         this.ctNode.style.display = "none";
13299     },
13300
13301     animCollapse : function(callback){
13302         var ct = Roo.get(this.ctNode);
13303         ct.enableDisplayMode('block');
13304         ct.stopFx();
13305
13306         this.animating = true;
13307         this.updateExpandIcon();
13308
13309         ct.slideOut('t', {
13310             callback : function(){
13311                this.animating = false;
13312                Roo.callback(callback);
13313             },
13314             scope: this,
13315             duration: this.node.ownerTree.duration || .25
13316         });
13317     },
13318
13319     getContainer : function(){
13320         return this.ctNode;
13321     },
13322
13323     getEl : function(){
13324         return this.wrap;
13325     },
13326
13327     appendDDGhost : function(ghostNode){
13328         ghostNode.appendChild(this.elNode.cloneNode(true));
13329     },
13330
13331     getDDRepairXY : function(){
13332         return Roo.lib.Dom.getXY(this.iconNode);
13333     },
13334
13335     onRender : function(){
13336         this.render();
13337     },
13338
13339     render : function(bulkRender){
13340         var n = this.node, a = n.attributes;
13341         var targetNode = n.parentNode ?
13342               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13343
13344         if(!this.rendered){
13345             this.rendered = true;
13346
13347             this.renderElements(n, a, targetNode, bulkRender);
13348
13349             if(a.qtip){
13350                if(this.textNode.setAttributeNS){
13351                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13352                    if(a.qtipTitle){
13353                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13354                    }
13355                }else{
13356                    this.textNode.setAttribute("ext:qtip", a.qtip);
13357                    if(a.qtipTitle){
13358                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13359                    }
13360                }
13361             }else if(a.qtipCfg){
13362                 a.qtipCfg.target = Roo.id(this.textNode);
13363                 Roo.QuickTips.register(a.qtipCfg);
13364             }
13365             this.initEvents();
13366             if(!this.node.expanded){
13367                 this.updateExpandIcon();
13368             }
13369         }else{
13370             if(bulkRender === true) {
13371                 targetNode.appendChild(this.wrap);
13372             }
13373         }
13374     },
13375
13376     renderElements : function(n, a, targetNode, bulkRender)
13377     {
13378         // add some indent caching, this helps performance when rendering a large tree
13379         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13380         var t = n.getOwnerTree();
13381         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13382         if (typeof(n.attributes.html) != 'undefined') {
13383             txt = n.attributes.html;
13384         }
13385         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
13386         var cb = typeof a.checked == 'boolean';
13387         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13388         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13389             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13390             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13391             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13392             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13393             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13394              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13395                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13396             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13397             "</li>"];
13398
13399         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13400             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13401                                 n.nextSibling.ui.getEl(), buf.join(""));
13402         }else{
13403             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13404         }
13405
13406         this.elNode = this.wrap.childNodes[0];
13407         this.ctNode = this.wrap.childNodes[1];
13408         var cs = this.elNode.childNodes;
13409         this.indentNode = cs[0];
13410         this.ecNode = cs[1];
13411         this.iconNode = cs[2];
13412         var index = 3;
13413         if(cb){
13414             this.checkbox = cs[3];
13415             index++;
13416         }
13417         this.anchor = cs[index];
13418         this.textNode = cs[index].firstChild;
13419     },
13420
13421     getAnchor : function(){
13422         return this.anchor;
13423     },
13424
13425     getTextEl : function(){
13426         return this.textNode;
13427     },
13428
13429     getIconEl : function(){
13430         return this.iconNode;
13431     },
13432
13433     isChecked : function(){
13434         return this.checkbox ? this.checkbox.checked : false;
13435     },
13436
13437     updateExpandIcon : function(){
13438         if(this.rendered){
13439             var n = this.node, c1, c2;
13440             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13441             var hasChild = n.hasChildNodes();
13442             if(hasChild){
13443                 if(n.expanded){
13444                     cls += "-minus";
13445                     c1 = "x-tree-node-collapsed";
13446                     c2 = "x-tree-node-expanded";
13447                 }else{
13448                     cls += "-plus";
13449                     c1 = "x-tree-node-expanded";
13450                     c2 = "x-tree-node-collapsed";
13451                 }
13452                 if(this.wasLeaf){
13453                     this.removeClass("x-tree-node-leaf");
13454                     this.wasLeaf = false;
13455                 }
13456                 if(this.c1 != c1 || this.c2 != c2){
13457                     Roo.fly(this.elNode).replaceClass(c1, c2);
13458                     this.c1 = c1; this.c2 = c2;
13459                 }
13460             }else{
13461                 // this changes non-leafs into leafs if they have no children.
13462                 // it's not very rational behaviour..
13463                 
13464                 if(!this.wasLeaf && this.node.leaf){
13465                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13466                     delete this.c1;
13467                     delete this.c2;
13468                     this.wasLeaf = true;
13469                 }
13470             }
13471             var ecc = "x-tree-ec-icon "+cls;
13472             if(this.ecc != ecc){
13473                 this.ecNode.className = ecc;
13474                 this.ecc = ecc;
13475             }
13476         }
13477     },
13478
13479     getChildIndent : function(){
13480         if(!this.childIndent){
13481             var buf = [];
13482             var p = this.node;
13483             while(p){
13484                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13485                     if(!p.isLast()) {
13486                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13487                     } else {
13488                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13489                     }
13490                 }
13491                 p = p.parentNode;
13492             }
13493             this.childIndent = buf.join("");
13494         }
13495         return this.childIndent;
13496     },
13497
13498     renderIndent : function(){
13499         if(this.rendered){
13500             var indent = "";
13501             var p = this.node.parentNode;
13502             if(p){
13503                 indent = p.ui.getChildIndent();
13504             }
13505             if(this.indentMarkup != indent){ // don't rerender if not required
13506                 this.indentNode.innerHTML = indent;
13507                 this.indentMarkup = indent;
13508             }
13509             this.updateExpandIcon();
13510         }
13511     }
13512 };
13513
13514 Roo.tree.RootTreeNodeUI = function(){
13515     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13516 };
13517 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13518     render : function(){
13519         if(!this.rendered){
13520             var targetNode = this.node.ownerTree.innerCt.dom;
13521             this.node.expanded = true;
13522             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13523             this.wrap = this.ctNode = targetNode.firstChild;
13524         }
13525     },
13526     collapse : function(){
13527     },
13528     expand : function(){
13529     }
13530 });/*
13531  * Based on:
13532  * Ext JS Library 1.1.1
13533  * Copyright(c) 2006-2007, Ext JS, LLC.
13534  *
13535  * Originally Released Under LGPL - original licence link has changed is not relivant.
13536  *
13537  * Fork - LGPL
13538  * <script type="text/javascript">
13539  */
13540 /**
13541  * @class Roo.tree.TreeLoader
13542  * @extends Roo.util.Observable
13543  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13544  * nodes from a specified URL. The response must be a javascript Array definition
13545  * who's elements are node definition objects. eg:
13546  * <pre><code>
13547 {  success : true,
13548    data :      [
13549    
13550     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13551     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13552     ]
13553 }
13554
13555
13556 </code></pre>
13557  * <br><br>
13558  * The old style respose with just an array is still supported, but not recommended.
13559  * <br><br>
13560  *
13561  * A server request is sent, and child nodes are loaded only when a node is expanded.
13562  * The loading node's id is passed to the server under the parameter name "node" to
13563  * enable the server to produce the correct child nodes.
13564  * <br><br>
13565  * To pass extra parameters, an event handler may be attached to the "beforeload"
13566  * event, and the parameters specified in the TreeLoader's baseParams property:
13567  * <pre><code>
13568     myTreeLoader.on("beforeload", function(treeLoader, node) {
13569         this.baseParams.category = node.attributes.category;
13570     }, this);
13571     
13572 </code></pre>
13573  *
13574  * This would pass an HTTP parameter called "category" to the server containing
13575  * the value of the Node's "category" attribute.
13576  * @constructor
13577  * Creates a new Treeloader.
13578  * @param {Object} config A config object containing config properties.
13579  */
13580 Roo.tree.TreeLoader = function(config){
13581     this.baseParams = {};
13582     this.requestMethod = "POST";
13583     Roo.apply(this, config);
13584
13585     this.addEvents({
13586     
13587         /**
13588          * @event beforeload
13589          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13590          * @param {Object} This TreeLoader object.
13591          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13592          * @param {Object} callback The callback function specified in the {@link #load} call.
13593          */
13594         beforeload : true,
13595         /**
13596          * @event load
13597          * Fires when the node has been successfuly loaded.
13598          * @param {Object} This TreeLoader object.
13599          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13600          * @param {Object} response The response object containing the data from the server.
13601          */
13602         load : true,
13603         /**
13604          * @event loadexception
13605          * Fires if the network request failed.
13606          * @param {Object} This TreeLoader object.
13607          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13608          * @param {Object} response The response object containing the data from the server.
13609          */
13610         loadexception : true,
13611         /**
13612          * @event create
13613          * Fires before a node is created, enabling you to return custom Node types 
13614          * @param {Object} This TreeLoader object.
13615          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13616          */
13617         create : true
13618     });
13619
13620     Roo.tree.TreeLoader.superclass.constructor.call(this);
13621 };
13622
13623 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13624     /**
13625     * @cfg {String} dataUrl The URL from which to request a Json string which
13626     * specifies an array of node definition object representing the child nodes
13627     * to be loaded.
13628     */
13629     /**
13630     * @cfg {String} requestMethod either GET or POST
13631     * defaults to POST (due to BC)
13632     * to be loaded.
13633     */
13634     /**
13635     * @cfg {Object} baseParams (optional) An object containing properties which
13636     * specify HTTP parameters to be passed to each request for child nodes.
13637     */
13638     /**
13639     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13640     * created by this loader. If the attributes sent by the server have an attribute in this object,
13641     * they take priority.
13642     */
13643     /**
13644     * @cfg {Object} uiProviders (optional) An object containing properties which
13645     * 
13646     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13647     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13648     * <i>uiProvider</i> attribute of a returned child node is a string rather
13649     * than a reference to a TreeNodeUI implementation, this that string value
13650     * is used as a property name in the uiProviders object. You can define the provider named
13651     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13652     */
13653     uiProviders : {},
13654
13655     /**
13656     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13657     * child nodes before loading.
13658     */
13659     clearOnLoad : true,
13660
13661     /**
13662     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13663     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13664     * Grid query { data : [ .....] }
13665     */
13666     
13667     root : false,
13668      /**
13669     * @cfg {String} queryParam (optional) 
13670     * Name of the query as it will be passed on the querystring (defaults to 'node')
13671     * eg. the request will be ?node=[id]
13672     */
13673     
13674     
13675     queryParam: false,
13676     
13677     /**
13678      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13679      * This is called automatically when a node is expanded, but may be used to reload
13680      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13681      * @param {Roo.tree.TreeNode} node
13682      * @param {Function} callback
13683      */
13684     load : function(node, callback){
13685         if(this.clearOnLoad){
13686             while(node.firstChild){
13687                 node.removeChild(node.firstChild);
13688             }
13689         }
13690         if(node.attributes.children){ // preloaded json children
13691             var cs = node.attributes.children;
13692             for(var i = 0, len = cs.length; i < len; i++){
13693                 node.appendChild(this.createNode(cs[i]));
13694             }
13695             if(typeof callback == "function"){
13696                 callback();
13697             }
13698         }else if(this.dataUrl){
13699             this.requestData(node, callback);
13700         }
13701     },
13702
13703     getParams: function(node){
13704         var buf = [], bp = this.baseParams;
13705         for(var key in bp){
13706             if(typeof bp[key] != "function"){
13707                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13708             }
13709         }
13710         var n = this.queryParam === false ? 'node' : this.queryParam;
13711         buf.push(n + "=", encodeURIComponent(node.id));
13712         return buf.join("");
13713     },
13714
13715     requestData : function(node, callback){
13716         if(this.fireEvent("beforeload", this, node, callback) !== false){
13717             this.transId = Roo.Ajax.request({
13718                 method:this.requestMethod,
13719                 url: this.dataUrl||this.url,
13720                 success: this.handleResponse,
13721                 failure: this.handleFailure,
13722                 scope: this,
13723                 argument: {callback: callback, node: node},
13724                 params: this.getParams(node)
13725             });
13726         }else{
13727             // if the load is cancelled, make sure we notify
13728             // the node that we are done
13729             if(typeof callback == "function"){
13730                 callback();
13731             }
13732         }
13733     },
13734
13735     isLoading : function(){
13736         return this.transId ? true : false;
13737     },
13738
13739     abort : function(){
13740         if(this.isLoading()){
13741             Roo.Ajax.abort(this.transId);
13742         }
13743     },
13744
13745     // private
13746     createNode : function(attr)
13747     {
13748         // apply baseAttrs, nice idea Corey!
13749         if(this.baseAttrs){
13750             Roo.applyIf(attr, this.baseAttrs);
13751         }
13752         if(this.applyLoader !== false){
13753             attr.loader = this;
13754         }
13755         // uiProvider = depreciated..
13756         
13757         if(typeof(attr.uiProvider) == 'string'){
13758            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13759                 /**  eval:var:attr */ eval(attr.uiProvider);
13760         }
13761         if(typeof(this.uiProviders['default']) != 'undefined') {
13762             attr.uiProvider = this.uiProviders['default'];
13763         }
13764         
13765         this.fireEvent('create', this, attr);
13766         
13767         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13768         return(attr.leaf ?
13769                         new Roo.tree.TreeNode(attr) :
13770                         new Roo.tree.AsyncTreeNode(attr));
13771     },
13772
13773     processResponse : function(response, node, callback)
13774     {
13775         var json = response.responseText;
13776         try {
13777             
13778             var o = Roo.decode(json);
13779             
13780             if (this.root === false && typeof(o.success) != undefined) {
13781                 this.root = 'data'; // the default behaviour for list like data..
13782                 }
13783                 
13784             if (this.root !== false &&  !o.success) {
13785                 // it's a failure condition.
13786                 var a = response.argument;
13787                 this.fireEvent("loadexception", this, a.node, response);
13788                 Roo.log("Load failed - should have a handler really");
13789                 return;
13790             }
13791             
13792             
13793             
13794             if (this.root !== false) {
13795                  o = o[this.root];
13796             }
13797             
13798             for(var i = 0, len = o.length; i < len; i++){
13799                 var n = this.createNode(o[i]);
13800                 if(n){
13801                     node.appendChild(n);
13802                 }
13803             }
13804             if(typeof callback == "function"){
13805                 callback(this, node);
13806             }
13807         }catch(e){
13808             this.handleFailure(response);
13809         }
13810     },
13811
13812     handleResponse : function(response){
13813         this.transId = false;
13814         var a = response.argument;
13815         this.processResponse(response, a.node, a.callback);
13816         this.fireEvent("load", this, a.node, response);
13817     },
13818
13819     handleFailure : function(response)
13820     {
13821         // should handle failure better..
13822         this.transId = false;
13823         var a = response.argument;
13824         this.fireEvent("loadexception", this, a.node, response);
13825         if(typeof a.callback == "function"){
13826             a.callback(this, a.node);
13827         }
13828     }
13829 });/*
13830  * Based on:
13831  * Ext JS Library 1.1.1
13832  * Copyright(c) 2006-2007, Ext JS, LLC.
13833  *
13834  * Originally Released Under LGPL - original licence link has changed is not relivant.
13835  *
13836  * Fork - LGPL
13837  * <script type="text/javascript">
13838  */
13839
13840 /**
13841 * @class Roo.tree.TreeFilter
13842 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13843 * @param {TreePanel} tree
13844 * @param {Object} config (optional)
13845  */
13846 Roo.tree.TreeFilter = function(tree, config){
13847     this.tree = tree;
13848     this.filtered = {};
13849     Roo.apply(this, config);
13850 };
13851
13852 Roo.tree.TreeFilter.prototype = {
13853     clearBlank:false,
13854     reverse:false,
13855     autoClear:false,
13856     remove:false,
13857
13858      /**
13859      * Filter the data by a specific attribute.
13860      * @param {String/RegExp} value Either string that the attribute value
13861      * should start with or a RegExp to test against the attribute
13862      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13863      * @param {TreeNode} startNode (optional) The node to start the filter at.
13864      */
13865     filter : function(value, attr, startNode){
13866         attr = attr || "text";
13867         var f;
13868         if(typeof value == "string"){
13869             var vlen = value.length;
13870             // auto clear empty filter
13871             if(vlen == 0 && this.clearBlank){
13872                 this.clear();
13873                 return;
13874             }
13875             value = value.toLowerCase();
13876             f = function(n){
13877                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13878             };
13879         }else if(value.exec){ // regex?
13880             f = function(n){
13881                 return value.test(n.attributes[attr]);
13882             };
13883         }else{
13884             throw 'Illegal filter type, must be string or regex';
13885         }
13886         this.filterBy(f, null, startNode);
13887         },
13888
13889     /**
13890      * Filter by a function. The passed function will be called with each
13891      * node in the tree (or from the startNode). If the function returns true, the node is kept
13892      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13893      * @param {Function} fn The filter function
13894      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13895      */
13896     filterBy : function(fn, scope, startNode){
13897         startNode = startNode || this.tree.root;
13898         if(this.autoClear){
13899             this.clear();
13900         }
13901         var af = this.filtered, rv = this.reverse;
13902         var f = function(n){
13903             if(n == startNode){
13904                 return true;
13905             }
13906             if(af[n.id]){
13907                 return false;
13908             }
13909             var m = fn.call(scope || n, n);
13910             if(!m || rv){
13911                 af[n.id] = n;
13912                 n.ui.hide();
13913                 return false;
13914             }
13915             return true;
13916         };
13917         startNode.cascade(f);
13918         if(this.remove){
13919            for(var id in af){
13920                if(typeof id != "function"){
13921                    var n = af[id];
13922                    if(n && n.parentNode){
13923                        n.parentNode.removeChild(n);
13924                    }
13925                }
13926            }
13927         }
13928     },
13929
13930     /**
13931      * Clears the current filter. Note: with the "remove" option
13932      * set a filter cannot be cleared.
13933      */
13934     clear : function(){
13935         var t = this.tree;
13936         var af = this.filtered;
13937         for(var id in af){
13938             if(typeof id != "function"){
13939                 var n = af[id];
13940                 if(n){
13941                     n.ui.show();
13942                 }
13943             }
13944         }
13945         this.filtered = {};
13946     }
13947 };
13948 /*
13949  * Based on:
13950  * Ext JS Library 1.1.1
13951  * Copyright(c) 2006-2007, Ext JS, LLC.
13952  *
13953  * Originally Released Under LGPL - original licence link has changed is not relivant.
13954  *
13955  * Fork - LGPL
13956  * <script type="text/javascript">
13957  */
13958  
13959
13960 /**
13961  * @class Roo.tree.TreeSorter
13962  * Provides sorting of nodes in a TreePanel
13963  * 
13964  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13965  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13966  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13967  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13968  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13969  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13970  * @constructor
13971  * @param {TreePanel} tree
13972  * @param {Object} config
13973  */
13974 Roo.tree.TreeSorter = function(tree, config){
13975     Roo.apply(this, config);
13976     tree.on("beforechildrenrendered", this.doSort, this);
13977     tree.on("append", this.updateSort, this);
13978     tree.on("insert", this.updateSort, this);
13979     
13980     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13981     var p = this.property || "text";
13982     var sortType = this.sortType;
13983     var fs = this.folderSort;
13984     var cs = this.caseSensitive === true;
13985     var leafAttr = this.leafAttr || 'leaf';
13986
13987     this.sortFn = function(n1, n2){
13988         if(fs){
13989             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13990                 return 1;
13991             }
13992             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13993                 return -1;
13994             }
13995         }
13996         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13997         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13998         if(v1 < v2){
13999                         return dsc ? +1 : -1;
14000                 }else if(v1 > v2){
14001                         return dsc ? -1 : +1;
14002         }else{
14003                 return 0;
14004         }
14005     };
14006 };
14007
14008 Roo.tree.TreeSorter.prototype = {
14009     doSort : function(node){
14010         node.sort(this.sortFn);
14011     },
14012     
14013     compareNodes : function(n1, n2){
14014         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14015     },
14016     
14017     updateSort : function(tree, node){
14018         if(node.childrenRendered){
14019             this.doSort.defer(1, this, [node]);
14020         }
14021     }
14022 };/*
14023  * Based on:
14024  * Ext JS Library 1.1.1
14025  * Copyright(c) 2006-2007, Ext JS, LLC.
14026  *
14027  * Originally Released Under LGPL - original licence link has changed is not relivant.
14028  *
14029  * Fork - LGPL
14030  * <script type="text/javascript">
14031  */
14032
14033 if(Roo.dd.DropZone){
14034     
14035 Roo.tree.TreeDropZone = function(tree, config){
14036     this.allowParentInsert = false;
14037     this.allowContainerDrop = false;
14038     this.appendOnly = false;
14039     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14040     this.tree = tree;
14041     this.lastInsertClass = "x-tree-no-status";
14042     this.dragOverData = {};
14043 };
14044
14045 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14046     ddGroup : "TreeDD",
14047     scroll:  true,
14048     
14049     expandDelay : 1000,
14050     
14051     expandNode : function(node){
14052         if(node.hasChildNodes() && !node.isExpanded()){
14053             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14054         }
14055     },
14056     
14057     queueExpand : function(node){
14058         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14059     },
14060     
14061     cancelExpand : function(){
14062         if(this.expandProcId){
14063             clearTimeout(this.expandProcId);
14064             this.expandProcId = false;
14065         }
14066     },
14067     
14068     isValidDropPoint : function(n, pt, dd, e, data){
14069         if(!n || !data){ return false; }
14070         var targetNode = n.node;
14071         var dropNode = data.node;
14072         // default drop rules
14073         if(!(targetNode && targetNode.isTarget && pt)){
14074             return false;
14075         }
14076         if(pt == "append" && targetNode.allowChildren === false){
14077             return false;
14078         }
14079         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14080             return false;
14081         }
14082         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14083             return false;
14084         }
14085         // reuse the object
14086         var overEvent = this.dragOverData;
14087         overEvent.tree = this.tree;
14088         overEvent.target = targetNode;
14089         overEvent.data = data;
14090         overEvent.point = pt;
14091         overEvent.source = dd;
14092         overEvent.rawEvent = e;
14093         overEvent.dropNode = dropNode;
14094         overEvent.cancel = false;  
14095         var result = this.tree.fireEvent("nodedragover", overEvent);
14096         return overEvent.cancel === false && result !== false;
14097     },
14098     
14099     getDropPoint : function(e, n, dd)
14100     {
14101         var tn = n.node;
14102         if(tn.isRoot){
14103             return tn.allowChildren !== false ? "append" : false; // always append for root
14104         }
14105         var dragEl = n.ddel;
14106         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14107         var y = Roo.lib.Event.getPageY(e);
14108         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14109         
14110         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14111         var noAppend = tn.allowChildren === false;
14112         if(this.appendOnly || tn.parentNode.allowChildren === false){
14113             return noAppend ? false : "append";
14114         }
14115         var noBelow = false;
14116         if(!this.allowParentInsert){
14117             noBelow = tn.hasChildNodes() && tn.isExpanded();
14118         }
14119         var q = (b - t) / (noAppend ? 2 : 3);
14120         if(y >= t && y < (t + q)){
14121             return "above";
14122         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14123             return "below";
14124         }else{
14125             return "append";
14126         }
14127     },
14128     
14129     onNodeEnter : function(n, dd, e, data)
14130     {
14131         this.cancelExpand();
14132     },
14133     
14134     onNodeOver : function(n, dd, e, data)
14135     {
14136        
14137         var pt = this.getDropPoint(e, n, dd);
14138         var node = n.node;
14139         
14140         // auto node expand check
14141         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14142             this.queueExpand(node);
14143         }else if(pt != "append"){
14144             this.cancelExpand();
14145         }
14146         
14147         // set the insert point style on the target node
14148         var returnCls = this.dropNotAllowed;
14149         if(this.isValidDropPoint(n, pt, dd, e, data)){
14150            if(pt){
14151                var el = n.ddel;
14152                var cls;
14153                if(pt == "above"){
14154                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14155                    cls = "x-tree-drag-insert-above";
14156                }else if(pt == "below"){
14157                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14158                    cls = "x-tree-drag-insert-below";
14159                }else{
14160                    returnCls = "x-tree-drop-ok-append";
14161                    cls = "x-tree-drag-append";
14162                }
14163                if(this.lastInsertClass != cls){
14164                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14165                    this.lastInsertClass = cls;
14166                }
14167            }
14168        }
14169        return returnCls;
14170     },
14171     
14172     onNodeOut : function(n, dd, e, data){
14173         
14174         this.cancelExpand();
14175         this.removeDropIndicators(n);
14176     },
14177     
14178     onNodeDrop : function(n, dd, e, data){
14179         var point = this.getDropPoint(e, n, dd);
14180         var targetNode = n.node;
14181         targetNode.ui.startDrop();
14182         if(!this.isValidDropPoint(n, point, dd, e, data)){
14183             targetNode.ui.endDrop();
14184             return false;
14185         }
14186         // first try to find the drop node
14187         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14188         var dropEvent = {
14189             tree : this.tree,
14190             target: targetNode,
14191             data: data,
14192             point: point,
14193             source: dd,
14194             rawEvent: e,
14195             dropNode: dropNode,
14196             cancel: !dropNode   
14197         };
14198         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14199         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14200             targetNode.ui.endDrop();
14201             return false;
14202         }
14203         // allow target changing
14204         targetNode = dropEvent.target;
14205         if(point == "append" && !targetNode.isExpanded()){
14206             targetNode.expand(false, null, function(){
14207                 this.completeDrop(dropEvent);
14208             }.createDelegate(this));
14209         }else{
14210             this.completeDrop(dropEvent);
14211         }
14212         return true;
14213     },
14214     
14215     completeDrop : function(de){
14216         var ns = de.dropNode, p = de.point, t = de.target;
14217         if(!(ns instanceof Array)){
14218             ns = [ns];
14219         }
14220         var n;
14221         for(var i = 0, len = ns.length; i < len; i++){
14222             n = ns[i];
14223             if(p == "above"){
14224                 t.parentNode.insertBefore(n, t);
14225             }else if(p == "below"){
14226                 t.parentNode.insertBefore(n, t.nextSibling);
14227             }else{
14228                 t.appendChild(n);
14229             }
14230         }
14231         n.ui.focus();
14232         if(this.tree.hlDrop){
14233             n.ui.highlight();
14234         }
14235         t.ui.endDrop();
14236         this.tree.fireEvent("nodedrop", de);
14237     },
14238     
14239     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14240         if(this.tree.hlDrop){
14241             dropNode.ui.focus();
14242             dropNode.ui.highlight();
14243         }
14244         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14245     },
14246     
14247     getTree : function(){
14248         return this.tree;
14249     },
14250     
14251     removeDropIndicators : function(n){
14252         if(n && n.ddel){
14253             var el = n.ddel;
14254             Roo.fly(el).removeClass([
14255                     "x-tree-drag-insert-above",
14256                     "x-tree-drag-insert-below",
14257                     "x-tree-drag-append"]);
14258             this.lastInsertClass = "_noclass";
14259         }
14260     },
14261     
14262     beforeDragDrop : function(target, e, id){
14263         this.cancelExpand();
14264         return true;
14265     },
14266     
14267     afterRepair : function(data){
14268         if(data && Roo.enableFx){
14269             data.node.ui.highlight();
14270         }
14271         this.hideProxy();
14272     } 
14273     
14274 });
14275
14276 }
14277 /*
14278  * Based on:
14279  * Ext JS Library 1.1.1
14280  * Copyright(c) 2006-2007, Ext JS, LLC.
14281  *
14282  * Originally Released Under LGPL - original licence link has changed is not relivant.
14283  *
14284  * Fork - LGPL
14285  * <script type="text/javascript">
14286  */
14287  
14288
14289 if(Roo.dd.DragZone){
14290 Roo.tree.TreeDragZone = function(tree, config){
14291     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14292     this.tree = tree;
14293 };
14294
14295 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14296     ddGroup : "TreeDD",
14297    
14298     onBeforeDrag : function(data, e){
14299         var n = data.node;
14300         return n && n.draggable && !n.disabled;
14301     },
14302      
14303     
14304     onInitDrag : function(e){
14305         var data = this.dragData;
14306         this.tree.getSelectionModel().select(data.node);
14307         this.proxy.update("");
14308         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14309         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14310     },
14311     
14312     getRepairXY : function(e, data){
14313         return data.node.ui.getDDRepairXY();
14314     },
14315     
14316     onEndDrag : function(data, e){
14317         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14318         
14319         
14320     },
14321     
14322     onValidDrop : function(dd, e, id){
14323         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14324         this.hideProxy();
14325     },
14326     
14327     beforeInvalidDrop : function(e, id){
14328         // this scrolls the original position back into view
14329         var sm = this.tree.getSelectionModel();
14330         sm.clearSelections();
14331         sm.select(this.dragData.node);
14332     }
14333 });
14334 }/*
14335  * Based on:
14336  * Ext JS Library 1.1.1
14337  * Copyright(c) 2006-2007, Ext JS, LLC.
14338  *
14339  * Originally Released Under LGPL - original licence link has changed is not relivant.
14340  *
14341  * Fork - LGPL
14342  * <script type="text/javascript">
14343  */
14344 /**
14345  * @class Roo.tree.TreeEditor
14346  * @extends Roo.Editor
14347  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14348  * as the editor field.
14349  * @constructor
14350  * @param {Object} config (used to be the tree panel.)
14351  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14352  * 
14353  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14354  * @cfg {Roo.form.TextField|Object} field The field configuration
14355  *
14356  * 
14357  */
14358 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14359     var tree = config;
14360     var field;
14361     if (oldconfig) { // old style..
14362         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14363     } else {
14364         // new style..
14365         tree = config.tree;
14366         config.field = config.field  || {};
14367         config.field.xtype = 'TextField';
14368         field = Roo.factory(config.field, Roo.form);
14369     }
14370     config = config || {};
14371     
14372     
14373     this.addEvents({
14374         /**
14375          * @event beforenodeedit
14376          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14377          * false from the handler of this event.
14378          * @param {Editor} this
14379          * @param {Roo.tree.Node} node 
14380          */
14381         "beforenodeedit" : true
14382     });
14383     
14384     //Roo.log(config);
14385     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14386
14387     this.tree = tree;
14388
14389     tree.on('beforeclick', this.beforeNodeClick, this);
14390     tree.getTreeEl().on('mousedown', this.hide, this);
14391     this.on('complete', this.updateNode, this);
14392     this.on('beforestartedit', this.fitToTree, this);
14393     this.on('startedit', this.bindScroll, this, {delay:10});
14394     this.on('specialkey', this.onSpecialKey, this);
14395 };
14396
14397 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14398     /**
14399      * @cfg {String} alignment
14400      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14401      */
14402     alignment: "l-l",
14403     // inherit
14404     autoSize: false,
14405     /**
14406      * @cfg {Boolean} hideEl
14407      * True to hide the bound element while the editor is displayed (defaults to false)
14408      */
14409     hideEl : false,
14410     /**
14411      * @cfg {String} cls
14412      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14413      */
14414     cls: "x-small-editor x-tree-editor",
14415     /**
14416      * @cfg {Boolean} shim
14417      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14418      */
14419     shim:false,
14420     // inherit
14421     shadow:"frame",
14422     /**
14423      * @cfg {Number} maxWidth
14424      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14425      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14426      * scroll and client offsets into account prior to each edit.
14427      */
14428     maxWidth: 250,
14429
14430     editDelay : 350,
14431
14432     // private
14433     fitToTree : function(ed, el){
14434         var td = this.tree.getTreeEl().dom, nd = el.dom;
14435         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14436             td.scrollLeft = nd.offsetLeft;
14437         }
14438         var w = Math.min(
14439                 this.maxWidth,
14440                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14441         this.setSize(w, '');
14442         
14443         return this.fireEvent('beforenodeedit', this, this.editNode);
14444         
14445     },
14446
14447     // private
14448     triggerEdit : function(node){
14449         this.completeEdit();
14450         this.editNode = node;
14451         this.startEdit(node.ui.textNode, node.text);
14452     },
14453
14454     // private
14455     bindScroll : function(){
14456         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14457     },
14458
14459     // private
14460     beforeNodeClick : function(node, e){
14461         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14462         this.lastClick = new Date();
14463         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14464             e.stopEvent();
14465             this.triggerEdit(node);
14466             return false;
14467         }
14468         return true;
14469     },
14470
14471     // private
14472     updateNode : function(ed, value){
14473         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14474         this.editNode.setText(value);
14475     },
14476
14477     // private
14478     onHide : function(){
14479         Roo.tree.TreeEditor.superclass.onHide.call(this);
14480         if(this.editNode){
14481             this.editNode.ui.focus();
14482         }
14483     },
14484
14485     // private
14486     onSpecialKey : function(field, e){
14487         var k = e.getKey();
14488         if(k == e.ESC){
14489             e.stopEvent();
14490             this.cancelEdit();
14491         }else if(k == e.ENTER && !e.hasModifier()){
14492             e.stopEvent();
14493             this.completeEdit();
14494         }
14495     }
14496 });//<Script type="text/javascript">
14497 /*
14498  * Based on:
14499  * Ext JS Library 1.1.1
14500  * Copyright(c) 2006-2007, Ext JS, LLC.
14501  *
14502  * Originally Released Under LGPL - original licence link has changed is not relivant.
14503  *
14504  * Fork - LGPL
14505  * <script type="text/javascript">
14506  */
14507  
14508 /**
14509  * Not documented??? - probably should be...
14510  */
14511
14512 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14513     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14514     
14515     renderElements : function(n, a, targetNode, bulkRender){
14516         //consel.log("renderElements?");
14517         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14518
14519         var t = n.getOwnerTree();
14520         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14521         
14522         var cols = t.columns;
14523         var bw = t.borderWidth;
14524         var c = cols[0];
14525         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14526          var cb = typeof a.checked == "boolean";
14527         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14528         var colcls = 'x-t-' + tid + '-c0';
14529         var buf = [
14530             '<li class="x-tree-node">',
14531             
14532                 
14533                 '<div class="x-tree-node-el ', a.cls,'">',
14534                     // extran...
14535                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14536                 
14537                 
14538                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14539                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14540                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14541                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14542                            (a.iconCls ? ' '+a.iconCls : ''),
14543                            '" unselectable="on" />',
14544                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14545                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14546                              
14547                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14548                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14549                             '<span unselectable="on" qtip="' + tx + '">',
14550                              tx,
14551                              '</span></a>' ,
14552                     '</div>',
14553                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14554                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14555                  ];
14556         for(var i = 1, len = cols.length; i < len; i++){
14557             c = cols[i];
14558             colcls = 'x-t-' + tid + '-c' +i;
14559             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14560             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14561                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14562                       "</div>");
14563          }
14564          
14565          buf.push(
14566             '</a>',
14567             '<div class="x-clear"></div></div>',
14568             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14569             "</li>");
14570         
14571         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14572             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14573                                 n.nextSibling.ui.getEl(), buf.join(""));
14574         }else{
14575             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14576         }
14577         var el = this.wrap.firstChild;
14578         this.elRow = el;
14579         this.elNode = el.firstChild;
14580         this.ranchor = el.childNodes[1];
14581         this.ctNode = this.wrap.childNodes[1];
14582         var cs = el.firstChild.childNodes;
14583         this.indentNode = cs[0];
14584         this.ecNode = cs[1];
14585         this.iconNode = cs[2];
14586         var index = 3;
14587         if(cb){
14588             this.checkbox = cs[3];
14589             index++;
14590         }
14591         this.anchor = cs[index];
14592         
14593         this.textNode = cs[index].firstChild;
14594         
14595         //el.on("click", this.onClick, this);
14596         //el.on("dblclick", this.onDblClick, this);
14597         
14598         
14599        // console.log(this);
14600     },
14601     initEvents : function(){
14602         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14603         
14604             
14605         var a = this.ranchor;
14606
14607         var el = Roo.get(a);
14608
14609         if(Roo.isOpera){ // opera render bug ignores the CSS
14610             el.setStyle("text-decoration", "none");
14611         }
14612
14613         el.on("click", this.onClick, this);
14614         el.on("dblclick", this.onDblClick, this);
14615         el.on("contextmenu", this.onContextMenu, this);
14616         
14617     },
14618     
14619     /*onSelectedChange : function(state){
14620         if(state){
14621             this.focus();
14622             this.addClass("x-tree-selected");
14623         }else{
14624             //this.blur();
14625             this.removeClass("x-tree-selected");
14626         }
14627     },*/
14628     addClass : function(cls){
14629         if(this.elRow){
14630             Roo.fly(this.elRow).addClass(cls);
14631         }
14632         
14633     },
14634     
14635     
14636     removeClass : function(cls){
14637         if(this.elRow){
14638             Roo.fly(this.elRow).removeClass(cls);
14639         }
14640     }
14641
14642     
14643     
14644 });//<Script type="text/javascript">
14645
14646 /*
14647  * Based on:
14648  * Ext JS Library 1.1.1
14649  * Copyright(c) 2006-2007, Ext JS, LLC.
14650  *
14651  * Originally Released Under LGPL - original licence link has changed is not relivant.
14652  *
14653  * Fork - LGPL
14654  * <script type="text/javascript">
14655  */
14656  
14657
14658 /**
14659  * @class Roo.tree.ColumnTree
14660  * @extends Roo.data.TreePanel
14661  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14662  * @cfg {int} borderWidth  compined right/left border allowance
14663  * @constructor
14664  * @param {String/HTMLElement/Element} el The container element
14665  * @param {Object} config
14666  */
14667 Roo.tree.ColumnTree =  function(el, config)
14668 {
14669    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14670    this.addEvents({
14671         /**
14672         * @event resize
14673         * Fire this event on a container when it resizes
14674         * @param {int} w Width
14675         * @param {int} h Height
14676         */
14677        "resize" : true
14678     });
14679     this.on('resize', this.onResize, this);
14680 };
14681
14682 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14683     //lines:false,
14684     
14685     
14686     borderWidth: Roo.isBorderBox ? 0 : 2, 
14687     headEls : false,
14688     
14689     render : function(){
14690         // add the header.....
14691        
14692         Roo.tree.ColumnTree.superclass.render.apply(this);
14693         
14694         this.el.addClass('x-column-tree');
14695         
14696         this.headers = this.el.createChild(
14697             {cls:'x-tree-headers'},this.innerCt.dom);
14698    
14699         var cols = this.columns, c;
14700         var totalWidth = 0;
14701         this.headEls = [];
14702         var  len = cols.length;
14703         for(var i = 0; i < len; i++){
14704              c = cols[i];
14705              totalWidth += c.width;
14706             this.headEls.push(this.headers.createChild({
14707                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14708                  cn: {
14709                      cls:'x-tree-hd-text',
14710                      html: c.header
14711                  },
14712                  style:'width:'+(c.width-this.borderWidth)+'px;'
14713              }));
14714         }
14715         this.headers.createChild({cls:'x-clear'});
14716         // prevent floats from wrapping when clipped
14717         this.headers.setWidth(totalWidth);
14718         //this.innerCt.setWidth(totalWidth);
14719         this.innerCt.setStyle({ overflow: 'auto' });
14720         this.onResize(this.width, this.height);
14721              
14722         
14723     },
14724     onResize : function(w,h)
14725     {
14726         this.height = h;
14727         this.width = w;
14728         // resize cols..
14729         this.innerCt.setWidth(this.width);
14730         this.innerCt.setHeight(this.height-20);
14731         
14732         // headers...
14733         var cols = this.columns, c;
14734         var totalWidth = 0;
14735         var expEl = false;
14736         var len = cols.length;
14737         for(var i = 0; i < len; i++){
14738             c = cols[i];
14739             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14740                 // it's the expander..
14741                 expEl  = this.headEls[i];
14742                 continue;
14743             }
14744             totalWidth += c.width;
14745             
14746         }
14747         if (expEl) {
14748             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14749         }
14750         this.headers.setWidth(w-20);
14751
14752         
14753         
14754         
14755     }
14756 });
14757 /*
14758  * Based on:
14759  * Ext JS Library 1.1.1
14760  * Copyright(c) 2006-2007, Ext JS, LLC.
14761  *
14762  * Originally Released Under LGPL - original licence link has changed is not relivant.
14763  *
14764  * Fork - LGPL
14765  * <script type="text/javascript">
14766  */
14767  
14768 /**
14769  * @class Roo.menu.Menu
14770  * @extends Roo.util.Observable
14771  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14772  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14773  * @constructor
14774  * Creates a new Menu
14775  * @param {Object} config Configuration options
14776  */
14777 Roo.menu.Menu = function(config){
14778     Roo.apply(this, config);
14779     this.id = this.id || Roo.id();
14780     this.addEvents({
14781         /**
14782          * @event beforeshow
14783          * Fires before this menu is displayed
14784          * @param {Roo.menu.Menu} this
14785          */
14786         beforeshow : true,
14787         /**
14788          * @event beforehide
14789          * Fires before this menu is hidden
14790          * @param {Roo.menu.Menu} this
14791          */
14792         beforehide : true,
14793         /**
14794          * @event show
14795          * Fires after this menu is displayed
14796          * @param {Roo.menu.Menu} this
14797          */
14798         show : true,
14799         /**
14800          * @event hide
14801          * Fires after this menu is hidden
14802          * @param {Roo.menu.Menu} this
14803          */
14804         hide : true,
14805         /**
14806          * @event click
14807          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14808          * @param {Roo.menu.Menu} this
14809          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14810          * @param {Roo.EventObject} e
14811          */
14812         click : true,
14813         /**
14814          * @event mouseover
14815          * Fires when the mouse is hovering over this menu
14816          * @param {Roo.menu.Menu} this
14817          * @param {Roo.EventObject} e
14818          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14819          */
14820         mouseover : true,
14821         /**
14822          * @event mouseout
14823          * Fires when the mouse exits this menu
14824          * @param {Roo.menu.Menu} this
14825          * @param {Roo.EventObject} e
14826          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14827          */
14828         mouseout : true,
14829         /**
14830          * @event itemclick
14831          * Fires when a menu item contained in this menu is clicked
14832          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14833          * @param {Roo.EventObject} e
14834          */
14835         itemclick: true
14836     });
14837     if (this.registerMenu) {
14838         Roo.menu.MenuMgr.register(this);
14839     }
14840     
14841     var mis = this.items;
14842     this.items = new Roo.util.MixedCollection();
14843     if(mis){
14844         this.add.apply(this, mis);
14845     }
14846 };
14847
14848 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14849     /**
14850      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14851      */
14852     minWidth : 120,
14853     /**
14854      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14855      * for bottom-right shadow (defaults to "sides")
14856      */
14857     shadow : "sides",
14858     /**
14859      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14860      * this menu (defaults to "tl-tr?")
14861      */
14862     subMenuAlign : "tl-tr?",
14863     /**
14864      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14865      * relative to its element of origin (defaults to "tl-bl?")
14866      */
14867     defaultAlign : "tl-bl?",
14868     /**
14869      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14870      */
14871     allowOtherMenus : false,
14872     /**
14873      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14874      */
14875     registerMenu : true,
14876
14877     hidden:true,
14878
14879     // private
14880     render : function(){
14881         if(this.el){
14882             return;
14883         }
14884         var el = this.el = new Roo.Layer({
14885             cls: "x-menu",
14886             shadow:this.shadow,
14887             constrain: false,
14888             parentEl: this.parentEl || document.body,
14889             zindex:15000
14890         });
14891
14892         this.keyNav = new Roo.menu.MenuNav(this);
14893
14894         if(this.plain){
14895             el.addClass("x-menu-plain");
14896         }
14897         if(this.cls){
14898             el.addClass(this.cls);
14899         }
14900         // generic focus element
14901         this.focusEl = el.createChild({
14902             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14903         });
14904         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14905         //disabling touch- as it's causing issues ..
14906         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14907         ul.on('click'   , this.onClick, this);
14908         
14909         
14910         ul.on("mouseover", this.onMouseOver, this);
14911         ul.on("mouseout", this.onMouseOut, this);
14912         this.items.each(function(item){
14913             if (item.hidden) {
14914                 return;
14915             }
14916             
14917             var li = document.createElement("li");
14918             li.className = "x-menu-list-item";
14919             ul.dom.appendChild(li);
14920             item.render(li, this);
14921         }, this);
14922         this.ul = ul;
14923         this.autoWidth();
14924     },
14925
14926     // private
14927     autoWidth : function(){
14928         var el = this.el, ul = this.ul;
14929         if(!el){
14930             return;
14931         }
14932         var w = this.width;
14933         if(w){
14934             el.setWidth(w);
14935         }else if(Roo.isIE){
14936             el.setWidth(this.minWidth);
14937             var t = el.dom.offsetWidth; // force recalc
14938             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14939         }
14940     },
14941
14942     // private
14943     delayAutoWidth : function(){
14944         if(this.rendered){
14945             if(!this.awTask){
14946                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14947             }
14948             this.awTask.delay(20);
14949         }
14950     },
14951
14952     // private
14953     findTargetItem : function(e){
14954         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14955         if(t && t.menuItemId){
14956             return this.items.get(t.menuItemId);
14957         }
14958     },
14959
14960     // private
14961     onClick : function(e){
14962         Roo.log("menu.onClick");
14963         var t = this.findTargetItem(e);
14964         if(!t){
14965             return;
14966         }
14967         Roo.log(e);
14968         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14969             if(t == this.activeItem && t.shouldDeactivate(e)){
14970                 this.activeItem.deactivate();
14971                 delete this.activeItem;
14972                 return;
14973             }
14974             if(t.canActivate){
14975                 this.setActiveItem(t, true);
14976             }
14977             return;
14978             
14979             
14980         }
14981         
14982         t.onClick(e);
14983         this.fireEvent("click", this, t, e);
14984     },
14985
14986     // private
14987     setActiveItem : function(item, autoExpand){
14988         if(item != this.activeItem){
14989             if(this.activeItem){
14990                 this.activeItem.deactivate();
14991             }
14992             this.activeItem = item;
14993             item.activate(autoExpand);
14994         }else if(autoExpand){
14995             item.expandMenu();
14996         }
14997     },
14998
14999     // private
15000     tryActivate : function(start, step){
15001         var items = this.items;
15002         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15003             var item = items.get(i);
15004             if(!item.disabled && item.canActivate){
15005                 this.setActiveItem(item, false);
15006                 return item;
15007             }
15008         }
15009         return false;
15010     },
15011
15012     // private
15013     onMouseOver : function(e){
15014         var t;
15015         if(t = this.findTargetItem(e)){
15016             if(t.canActivate && !t.disabled){
15017                 this.setActiveItem(t, true);
15018             }
15019         }
15020         this.fireEvent("mouseover", this, e, t);
15021     },
15022
15023     // private
15024     onMouseOut : function(e){
15025         var t;
15026         if(t = this.findTargetItem(e)){
15027             if(t == this.activeItem && t.shouldDeactivate(e)){
15028                 this.activeItem.deactivate();
15029                 delete this.activeItem;
15030             }
15031         }
15032         this.fireEvent("mouseout", this, e, t);
15033     },
15034
15035     /**
15036      * Read-only.  Returns true if the menu is currently displayed, else false.
15037      * @type Boolean
15038      */
15039     isVisible : function(){
15040         return this.el && !this.hidden;
15041     },
15042
15043     /**
15044      * Displays this menu relative to another element
15045      * @param {String/HTMLElement/Roo.Element} element The element to align to
15046      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15047      * the element (defaults to this.defaultAlign)
15048      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15049      */
15050     show : function(el, pos, parentMenu){
15051         this.parentMenu = parentMenu;
15052         if(!this.el){
15053             this.render();
15054         }
15055         this.fireEvent("beforeshow", this);
15056         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15057     },
15058
15059     /**
15060      * Displays this menu at a specific xy position
15061      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15062      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15063      */
15064     showAt : function(xy, parentMenu, /* private: */_e){
15065         this.parentMenu = parentMenu;
15066         if(!this.el){
15067             this.render();
15068         }
15069         if(_e !== false){
15070             this.fireEvent("beforeshow", this);
15071             xy = this.el.adjustForConstraints(xy);
15072         }
15073         this.el.setXY(xy);
15074         this.el.show();
15075         this.hidden = false;
15076         this.focus();
15077         this.fireEvent("show", this);
15078     },
15079
15080     focus : function(){
15081         if(!this.hidden){
15082             this.doFocus.defer(50, this);
15083         }
15084     },
15085
15086     doFocus : function(){
15087         if(!this.hidden){
15088             this.focusEl.focus();
15089         }
15090     },
15091
15092     /**
15093      * Hides this menu and optionally all parent menus
15094      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15095      */
15096     hide : function(deep){
15097         if(this.el && this.isVisible()){
15098             this.fireEvent("beforehide", this);
15099             if(this.activeItem){
15100                 this.activeItem.deactivate();
15101                 this.activeItem = null;
15102             }
15103             this.el.hide();
15104             this.hidden = true;
15105             this.fireEvent("hide", this);
15106         }
15107         if(deep === true && this.parentMenu){
15108             this.parentMenu.hide(true);
15109         }
15110     },
15111
15112     /**
15113      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15114      * Any of the following are valid:
15115      * <ul>
15116      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15117      * <li>An HTMLElement object which will be converted to a menu item</li>
15118      * <li>A menu item config object that will be created as a new menu item</li>
15119      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15120      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15121      * </ul>
15122      * Usage:
15123      * <pre><code>
15124 // Create the menu
15125 var menu = new Roo.menu.Menu();
15126
15127 // Create a menu item to add by reference
15128 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15129
15130 // Add a bunch of items at once using different methods.
15131 // Only the last item added will be returned.
15132 var item = menu.add(
15133     menuItem,                // add existing item by ref
15134     'Dynamic Item',          // new TextItem
15135     '-',                     // new separator
15136     { text: 'Config Item' }  // new item by config
15137 );
15138 </code></pre>
15139      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15140      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15141      */
15142     add : function(){
15143         var a = arguments, l = a.length, item;
15144         for(var i = 0; i < l; i++){
15145             var el = a[i];
15146             if ((typeof(el) == "object") && el.xtype && el.xns) {
15147                 el = Roo.factory(el, Roo.menu);
15148             }
15149             
15150             if(el.render){ // some kind of Item
15151                 item = this.addItem(el);
15152             }else if(typeof el == "string"){ // string
15153                 if(el == "separator" || el == "-"){
15154                     item = this.addSeparator();
15155                 }else{
15156                     item = this.addText(el);
15157                 }
15158             }else if(el.tagName || el.el){ // element
15159                 item = this.addElement(el);
15160             }else if(typeof el == "object"){ // must be menu item config?
15161                 item = this.addMenuItem(el);
15162             }
15163         }
15164         return item;
15165     },
15166
15167     /**
15168      * Returns this menu's underlying {@link Roo.Element} object
15169      * @return {Roo.Element} The element
15170      */
15171     getEl : function(){
15172         if(!this.el){
15173             this.render();
15174         }
15175         return this.el;
15176     },
15177
15178     /**
15179      * Adds a separator bar to the menu
15180      * @return {Roo.menu.Item} The menu item that was added
15181      */
15182     addSeparator : function(){
15183         return this.addItem(new Roo.menu.Separator());
15184     },
15185
15186     /**
15187      * Adds an {@link Roo.Element} object to the menu
15188      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15189      * @return {Roo.menu.Item} The menu item that was added
15190      */
15191     addElement : function(el){
15192         return this.addItem(new Roo.menu.BaseItem(el));
15193     },
15194
15195     /**
15196      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15197      * @param {Roo.menu.Item} item The menu item to add
15198      * @return {Roo.menu.Item} The menu item that was added
15199      */
15200     addItem : function(item){
15201         this.items.add(item);
15202         if(this.ul){
15203             var li = document.createElement("li");
15204             li.className = "x-menu-list-item";
15205             this.ul.dom.appendChild(li);
15206             item.render(li, this);
15207             this.delayAutoWidth();
15208         }
15209         return item;
15210     },
15211
15212     /**
15213      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15214      * @param {Object} config A MenuItem config object
15215      * @return {Roo.menu.Item} The menu item that was added
15216      */
15217     addMenuItem : function(config){
15218         if(!(config instanceof Roo.menu.Item)){
15219             if(typeof config.checked == "boolean"){ // must be check menu item config?
15220                 config = new Roo.menu.CheckItem(config);
15221             }else{
15222                 config = new Roo.menu.Item(config);
15223             }
15224         }
15225         return this.addItem(config);
15226     },
15227
15228     /**
15229      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15230      * @param {String} text The text to display in the menu item
15231      * @return {Roo.menu.Item} The menu item that was added
15232      */
15233     addText : function(text){
15234         return this.addItem(new Roo.menu.TextItem({ text : text }));
15235     },
15236
15237     /**
15238      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15239      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15240      * @param {Roo.menu.Item} item The menu item to add
15241      * @return {Roo.menu.Item} The menu item that was added
15242      */
15243     insert : function(index, item){
15244         this.items.insert(index, item);
15245         if(this.ul){
15246             var li = document.createElement("li");
15247             li.className = "x-menu-list-item";
15248             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15249             item.render(li, this);
15250             this.delayAutoWidth();
15251         }
15252         return item;
15253     },
15254
15255     /**
15256      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15257      * @param {Roo.menu.Item} item The menu item to remove
15258      */
15259     remove : function(item){
15260         this.items.removeKey(item.id);
15261         item.destroy();
15262     },
15263
15264     /**
15265      * Removes and destroys all items in the menu
15266      */
15267     removeAll : function(){
15268         var f;
15269         while(f = this.items.first()){
15270             this.remove(f);
15271         }
15272     }
15273 });
15274
15275 // MenuNav is a private utility class used internally by the Menu
15276 Roo.menu.MenuNav = function(menu){
15277     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15278     this.scope = this.menu = menu;
15279 };
15280
15281 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15282     doRelay : function(e, h){
15283         var k = e.getKey();
15284         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15285             this.menu.tryActivate(0, 1);
15286             return false;
15287         }
15288         return h.call(this.scope || this, e, this.menu);
15289     },
15290
15291     up : function(e, m){
15292         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15293             m.tryActivate(m.items.length-1, -1);
15294         }
15295     },
15296
15297     down : function(e, m){
15298         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15299             m.tryActivate(0, 1);
15300         }
15301     },
15302
15303     right : function(e, m){
15304         if(m.activeItem){
15305             m.activeItem.expandMenu(true);
15306         }
15307     },
15308
15309     left : function(e, m){
15310         m.hide();
15311         if(m.parentMenu && m.parentMenu.activeItem){
15312             m.parentMenu.activeItem.activate();
15313         }
15314     },
15315
15316     enter : function(e, m){
15317         if(m.activeItem){
15318             e.stopPropagation();
15319             m.activeItem.onClick(e);
15320             m.fireEvent("click", this, m.activeItem);
15321             return true;
15322         }
15323     }
15324 });/*
15325  * Based on:
15326  * Ext JS Library 1.1.1
15327  * Copyright(c) 2006-2007, Ext JS, LLC.
15328  *
15329  * Originally Released Under LGPL - original licence link has changed is not relivant.
15330  *
15331  * Fork - LGPL
15332  * <script type="text/javascript">
15333  */
15334  
15335 /**
15336  * @class Roo.menu.MenuMgr
15337  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15338  * @singleton
15339  */
15340 Roo.menu.MenuMgr = function(){
15341    var menus, active, groups = {}, attached = false, lastShow = new Date();
15342
15343    // private - called when first menu is created
15344    function init(){
15345        menus = {};
15346        active = new Roo.util.MixedCollection();
15347        Roo.get(document).addKeyListener(27, function(){
15348            if(active.length > 0){
15349                hideAll();
15350            }
15351        });
15352    }
15353
15354    // private
15355    function hideAll(){
15356        if(active && active.length > 0){
15357            var c = active.clone();
15358            c.each(function(m){
15359                m.hide();
15360            });
15361        }
15362    }
15363
15364    // private
15365    function onHide(m){
15366        active.remove(m);
15367        if(active.length < 1){
15368            Roo.get(document).un("mousedown", onMouseDown);
15369            attached = false;
15370        }
15371    }
15372
15373    // private
15374    function onShow(m){
15375        var last = active.last();
15376        lastShow = new Date();
15377        active.add(m);
15378        if(!attached){
15379            Roo.get(document).on("mousedown", onMouseDown);
15380            attached = true;
15381        }
15382        if(m.parentMenu){
15383           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15384           m.parentMenu.activeChild = m;
15385        }else if(last && last.isVisible()){
15386           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15387        }
15388    }
15389
15390    // private
15391    function onBeforeHide(m){
15392        if(m.activeChild){
15393            m.activeChild.hide();
15394        }
15395        if(m.autoHideTimer){
15396            clearTimeout(m.autoHideTimer);
15397            delete m.autoHideTimer;
15398        }
15399    }
15400
15401    // private
15402    function onBeforeShow(m){
15403        var pm = m.parentMenu;
15404        if(!pm && !m.allowOtherMenus){
15405            hideAll();
15406        }else if(pm && pm.activeChild && active != m){
15407            pm.activeChild.hide();
15408        }
15409    }
15410
15411    // private
15412    function onMouseDown(e){
15413        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15414            hideAll();
15415        }
15416    }
15417
15418    // private
15419    function onBeforeCheck(mi, state){
15420        if(state){
15421            var g = groups[mi.group];
15422            for(var i = 0, l = g.length; i < l; i++){
15423                if(g[i] != mi){
15424                    g[i].setChecked(false);
15425                }
15426            }
15427        }
15428    }
15429
15430    return {
15431
15432        /**
15433         * Hides all menus that are currently visible
15434         */
15435        hideAll : function(){
15436             hideAll();  
15437        },
15438
15439        // private
15440        register : function(menu){
15441            if(!menus){
15442                init();
15443            }
15444            menus[menu.id] = menu;
15445            menu.on("beforehide", onBeforeHide);
15446            menu.on("hide", onHide);
15447            menu.on("beforeshow", onBeforeShow);
15448            menu.on("show", onShow);
15449            var g = menu.group;
15450            if(g && menu.events["checkchange"]){
15451                if(!groups[g]){
15452                    groups[g] = [];
15453                }
15454                groups[g].push(menu);
15455                menu.on("checkchange", onCheck);
15456            }
15457        },
15458
15459         /**
15460          * Returns a {@link Roo.menu.Menu} object
15461          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15462          * be used to generate and return a new Menu instance.
15463          */
15464        get : function(menu){
15465            if(typeof menu == "string"){ // menu id
15466                return menus[menu];
15467            }else if(menu.events){  // menu instance
15468                return menu;
15469            }else if(typeof menu.length == 'number'){ // array of menu items?
15470                return new Roo.menu.Menu({items:menu});
15471            }else{ // otherwise, must be a config
15472                return new Roo.menu.Menu(menu);
15473            }
15474        },
15475
15476        // private
15477        unregister : function(menu){
15478            delete menus[menu.id];
15479            menu.un("beforehide", onBeforeHide);
15480            menu.un("hide", onHide);
15481            menu.un("beforeshow", onBeforeShow);
15482            menu.un("show", onShow);
15483            var g = menu.group;
15484            if(g && menu.events["checkchange"]){
15485                groups[g].remove(menu);
15486                menu.un("checkchange", onCheck);
15487            }
15488        },
15489
15490        // private
15491        registerCheckable : function(menuItem){
15492            var g = menuItem.group;
15493            if(g){
15494                if(!groups[g]){
15495                    groups[g] = [];
15496                }
15497                groups[g].push(menuItem);
15498                menuItem.on("beforecheckchange", onBeforeCheck);
15499            }
15500        },
15501
15502        // private
15503        unregisterCheckable : function(menuItem){
15504            var g = menuItem.group;
15505            if(g){
15506                groups[g].remove(menuItem);
15507                menuItem.un("beforecheckchange", onBeforeCheck);
15508            }
15509        }
15510    };
15511 }();/*
15512  * Based on:
15513  * Ext JS Library 1.1.1
15514  * Copyright(c) 2006-2007, Ext JS, LLC.
15515  *
15516  * Originally Released Under LGPL - original licence link has changed is not relivant.
15517  *
15518  * Fork - LGPL
15519  * <script type="text/javascript">
15520  */
15521  
15522
15523 /**
15524  * @class Roo.menu.BaseItem
15525  * @extends Roo.Component
15526  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15527  * management and base configuration options shared by all menu components.
15528  * @constructor
15529  * Creates a new BaseItem
15530  * @param {Object} config Configuration options
15531  */
15532 Roo.menu.BaseItem = function(config){
15533     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15534
15535     this.addEvents({
15536         /**
15537          * @event click
15538          * Fires when this item is clicked
15539          * @param {Roo.menu.BaseItem} this
15540          * @param {Roo.EventObject} e
15541          */
15542         click: true,
15543         /**
15544          * @event activate
15545          * Fires when this item is activated
15546          * @param {Roo.menu.BaseItem} this
15547          */
15548         activate : true,
15549         /**
15550          * @event deactivate
15551          * Fires when this item is deactivated
15552          * @param {Roo.menu.BaseItem} this
15553          */
15554         deactivate : true
15555     });
15556
15557     if(this.handler){
15558         this.on("click", this.handler, this.scope, true);
15559     }
15560 };
15561
15562 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15563     /**
15564      * @cfg {Function} handler
15565      * A function that will handle the click event of this menu item (defaults to undefined)
15566      */
15567     /**
15568      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15569      */
15570     canActivate : false,
15571     
15572      /**
15573      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15574      */
15575     hidden: false,
15576     
15577     /**
15578      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15579      */
15580     activeClass : "x-menu-item-active",
15581     /**
15582      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15583      */
15584     hideOnClick : true,
15585     /**
15586      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15587      */
15588     hideDelay : 100,
15589
15590     // private
15591     ctype: "Roo.menu.BaseItem",
15592
15593     // private
15594     actionMode : "container",
15595
15596     // private
15597     render : function(container, parentMenu){
15598         this.parentMenu = parentMenu;
15599         Roo.menu.BaseItem.superclass.render.call(this, container);
15600         this.container.menuItemId = this.id;
15601     },
15602
15603     // private
15604     onRender : function(container, position){
15605         this.el = Roo.get(this.el);
15606         container.dom.appendChild(this.el.dom);
15607     },
15608
15609     // private
15610     onClick : function(e){
15611         if(!this.disabled && this.fireEvent("click", this, e) !== false
15612                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15613             this.handleClick(e);
15614         }else{
15615             e.stopEvent();
15616         }
15617     },
15618
15619     // private
15620     activate : function(){
15621         if(this.disabled){
15622             return false;
15623         }
15624         var li = this.container;
15625         li.addClass(this.activeClass);
15626         this.region = li.getRegion().adjust(2, 2, -2, -2);
15627         this.fireEvent("activate", this);
15628         return true;
15629     },
15630
15631     // private
15632     deactivate : function(){
15633         this.container.removeClass(this.activeClass);
15634         this.fireEvent("deactivate", this);
15635     },
15636
15637     // private
15638     shouldDeactivate : function(e){
15639         return !this.region || !this.region.contains(e.getPoint());
15640     },
15641
15642     // private
15643     handleClick : function(e){
15644         if(this.hideOnClick){
15645             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15646         }
15647     },
15648
15649     // private
15650     expandMenu : function(autoActivate){
15651         // do nothing
15652     },
15653
15654     // private
15655     hideMenu : function(){
15656         // do nothing
15657     }
15658 });/*
15659  * Based on:
15660  * Ext JS Library 1.1.1
15661  * Copyright(c) 2006-2007, Ext JS, LLC.
15662  *
15663  * Originally Released Under LGPL - original licence link has changed is not relivant.
15664  *
15665  * Fork - LGPL
15666  * <script type="text/javascript">
15667  */
15668  
15669 /**
15670  * @class Roo.menu.Adapter
15671  * @extends Roo.menu.BaseItem
15672  * 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.
15673  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15674  * @constructor
15675  * Creates a new Adapter
15676  * @param {Object} config Configuration options
15677  */
15678 Roo.menu.Adapter = function(component, config){
15679     Roo.menu.Adapter.superclass.constructor.call(this, config);
15680     this.component = component;
15681 };
15682 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15683     // private
15684     canActivate : true,
15685
15686     // private
15687     onRender : function(container, position){
15688         this.component.render(container);
15689         this.el = this.component.getEl();
15690     },
15691
15692     // private
15693     activate : function(){
15694         if(this.disabled){
15695             return false;
15696         }
15697         this.component.focus();
15698         this.fireEvent("activate", this);
15699         return true;
15700     },
15701
15702     // private
15703     deactivate : function(){
15704         this.fireEvent("deactivate", this);
15705     },
15706
15707     // private
15708     disable : function(){
15709         this.component.disable();
15710         Roo.menu.Adapter.superclass.disable.call(this);
15711     },
15712
15713     // private
15714     enable : function(){
15715         this.component.enable();
15716         Roo.menu.Adapter.superclass.enable.call(this);
15717     }
15718 });/*
15719  * Based on:
15720  * Ext JS Library 1.1.1
15721  * Copyright(c) 2006-2007, Ext JS, LLC.
15722  *
15723  * Originally Released Under LGPL - original licence link has changed is not relivant.
15724  *
15725  * Fork - LGPL
15726  * <script type="text/javascript">
15727  */
15728
15729 /**
15730  * @class Roo.menu.TextItem
15731  * @extends Roo.menu.BaseItem
15732  * Adds a static text string to a menu, usually used as either a heading or group separator.
15733  * Note: old style constructor with text is still supported.
15734  * 
15735  * @constructor
15736  * Creates a new TextItem
15737  * @param {Object} cfg Configuration
15738  */
15739 Roo.menu.TextItem = function(cfg){
15740     if (typeof(cfg) == 'string') {
15741         this.text = cfg;
15742     } else {
15743         Roo.apply(this,cfg);
15744     }
15745     
15746     Roo.menu.TextItem.superclass.constructor.call(this);
15747 };
15748
15749 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15750     /**
15751      * @cfg {Boolean} text Text to show on item.
15752      */
15753     text : '',
15754     
15755     /**
15756      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15757      */
15758     hideOnClick : false,
15759     /**
15760      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15761      */
15762     itemCls : "x-menu-text",
15763
15764     // private
15765     onRender : function(){
15766         var s = document.createElement("span");
15767         s.className = this.itemCls;
15768         s.innerHTML = this.text;
15769         this.el = s;
15770         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15771     }
15772 });/*
15773  * Based on:
15774  * Ext JS Library 1.1.1
15775  * Copyright(c) 2006-2007, Ext JS, LLC.
15776  *
15777  * Originally Released Under LGPL - original licence link has changed is not relivant.
15778  *
15779  * Fork - LGPL
15780  * <script type="text/javascript">
15781  */
15782
15783 /**
15784  * @class Roo.menu.Separator
15785  * @extends Roo.menu.BaseItem
15786  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15787  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15788  * @constructor
15789  * @param {Object} config Configuration options
15790  */
15791 Roo.menu.Separator = function(config){
15792     Roo.menu.Separator.superclass.constructor.call(this, config);
15793 };
15794
15795 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15796     /**
15797      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15798      */
15799     itemCls : "x-menu-sep",
15800     /**
15801      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15802      */
15803     hideOnClick : false,
15804
15805     // private
15806     onRender : function(li){
15807         var s = document.createElement("span");
15808         s.className = this.itemCls;
15809         s.innerHTML = "&#160;";
15810         this.el = s;
15811         li.addClass("x-menu-sep-li");
15812         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15813     }
15814 });/*
15815  * Based on:
15816  * Ext JS Library 1.1.1
15817  * Copyright(c) 2006-2007, Ext JS, LLC.
15818  *
15819  * Originally Released Under LGPL - original licence link has changed is not relivant.
15820  *
15821  * Fork - LGPL
15822  * <script type="text/javascript">
15823  */
15824 /**
15825  * @class Roo.menu.Item
15826  * @extends Roo.menu.BaseItem
15827  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15828  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15829  * activation and click handling.
15830  * @constructor
15831  * Creates a new Item
15832  * @param {Object} config Configuration options
15833  */
15834 Roo.menu.Item = function(config){
15835     Roo.menu.Item.superclass.constructor.call(this, config);
15836     if(this.menu){
15837         this.menu = Roo.menu.MenuMgr.get(this.menu);
15838     }
15839 };
15840 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15841     
15842     /**
15843      * @cfg {String} text
15844      * The text to show on the menu item.
15845      */
15846     text: '',
15847      /**
15848      * @cfg {String} HTML to render in menu
15849      * The text to show on the menu item (HTML version).
15850      */
15851     html: '',
15852     /**
15853      * @cfg {String} icon
15854      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15855      */
15856     icon: undefined,
15857     /**
15858      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15859      */
15860     itemCls : "x-menu-item",
15861     /**
15862      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15863      */
15864     canActivate : true,
15865     /**
15866      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15867      */
15868     showDelay: 200,
15869     // doc'd in BaseItem
15870     hideDelay: 200,
15871
15872     // private
15873     ctype: "Roo.menu.Item",
15874     
15875     // private
15876     onRender : function(container, position){
15877         var el = document.createElement("a");
15878         el.hideFocus = true;
15879         el.unselectable = "on";
15880         el.href = this.href || "#";
15881         if(this.hrefTarget){
15882             el.target = this.hrefTarget;
15883         }
15884         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15885         
15886         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15887         
15888         el.innerHTML = String.format(
15889                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15890                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15891         this.el = el;
15892         Roo.menu.Item.superclass.onRender.call(this, container, position);
15893     },
15894
15895     /**
15896      * Sets the text to display in this menu item
15897      * @param {String} text The text to display
15898      * @param {Boolean} isHTML true to indicate text is pure html.
15899      */
15900     setText : function(text, isHTML){
15901         if (isHTML) {
15902             this.html = text;
15903         } else {
15904             this.text = text;
15905             this.html = '';
15906         }
15907         if(this.rendered){
15908             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15909      
15910             this.el.update(String.format(
15911                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15912                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15913             this.parentMenu.autoWidth();
15914         }
15915     },
15916
15917     // private
15918     handleClick : function(e){
15919         if(!this.href){ // if no link defined, stop the event automatically
15920             e.stopEvent();
15921         }
15922         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15923     },
15924
15925     // private
15926     activate : function(autoExpand){
15927         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15928             this.focus();
15929             if(autoExpand){
15930                 this.expandMenu();
15931             }
15932         }
15933         return true;
15934     },
15935
15936     // private
15937     shouldDeactivate : function(e){
15938         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15939             if(this.menu && this.menu.isVisible()){
15940                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15941             }
15942             return true;
15943         }
15944         return false;
15945     },
15946
15947     // private
15948     deactivate : function(){
15949         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15950         this.hideMenu();
15951     },
15952
15953     // private
15954     expandMenu : function(autoActivate){
15955         if(!this.disabled && this.menu){
15956             clearTimeout(this.hideTimer);
15957             delete this.hideTimer;
15958             if(!this.menu.isVisible() && !this.showTimer){
15959                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15960             }else if (this.menu.isVisible() && autoActivate){
15961                 this.menu.tryActivate(0, 1);
15962             }
15963         }
15964     },
15965
15966     // private
15967     deferExpand : function(autoActivate){
15968         delete this.showTimer;
15969         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15970         if(autoActivate){
15971             this.menu.tryActivate(0, 1);
15972         }
15973     },
15974
15975     // private
15976     hideMenu : function(){
15977         clearTimeout(this.showTimer);
15978         delete this.showTimer;
15979         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15980             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15981         }
15982     },
15983
15984     // private
15985     deferHide : function(){
15986         delete this.hideTimer;
15987         this.menu.hide();
15988     }
15989 });/*
15990  * Based on:
15991  * Ext JS Library 1.1.1
15992  * Copyright(c) 2006-2007, Ext JS, LLC.
15993  *
15994  * Originally Released Under LGPL - original licence link has changed is not relivant.
15995  *
15996  * Fork - LGPL
15997  * <script type="text/javascript">
15998  */
15999  
16000 /**
16001  * @class Roo.menu.CheckItem
16002  * @extends Roo.menu.Item
16003  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16004  * @constructor
16005  * Creates a new CheckItem
16006  * @param {Object} config Configuration options
16007  */
16008 Roo.menu.CheckItem = function(config){
16009     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16010     this.addEvents({
16011         /**
16012          * @event beforecheckchange
16013          * Fires before the checked value is set, providing an opportunity to cancel if needed
16014          * @param {Roo.menu.CheckItem} this
16015          * @param {Boolean} checked The new checked value that will be set
16016          */
16017         "beforecheckchange" : true,
16018         /**
16019          * @event checkchange
16020          * Fires after the checked value has been set
16021          * @param {Roo.menu.CheckItem} this
16022          * @param {Boolean} checked The checked value that was set
16023          */
16024         "checkchange" : true
16025     });
16026     if(this.checkHandler){
16027         this.on('checkchange', this.checkHandler, this.scope);
16028     }
16029 };
16030 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16031     /**
16032      * @cfg {String} group
16033      * All check items with the same group name will automatically be grouped into a single-select
16034      * radio button group (defaults to '')
16035      */
16036     /**
16037      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16038      */
16039     itemCls : "x-menu-item x-menu-check-item",
16040     /**
16041      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16042      */
16043     groupClass : "x-menu-group-item",
16044
16045     /**
16046      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16047      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16048      * initialized with checked = true will be rendered as checked.
16049      */
16050     checked: false,
16051
16052     // private
16053     ctype: "Roo.menu.CheckItem",
16054
16055     // private
16056     onRender : function(c){
16057         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16058         if(this.group){
16059             this.el.addClass(this.groupClass);
16060         }
16061         Roo.menu.MenuMgr.registerCheckable(this);
16062         if(this.checked){
16063             this.checked = false;
16064             this.setChecked(true, true);
16065         }
16066     },
16067
16068     // private
16069     destroy : function(){
16070         if(this.rendered){
16071             Roo.menu.MenuMgr.unregisterCheckable(this);
16072         }
16073         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16074     },
16075
16076     /**
16077      * Set the checked state of this item
16078      * @param {Boolean} checked The new checked value
16079      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16080      */
16081     setChecked : function(state, suppressEvent){
16082         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16083             if(this.container){
16084                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16085             }
16086             this.checked = state;
16087             if(suppressEvent !== true){
16088                 this.fireEvent("checkchange", this, state);
16089             }
16090         }
16091     },
16092
16093     // private
16094     handleClick : function(e){
16095        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16096            this.setChecked(!this.checked);
16097        }
16098        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16099     }
16100 });/*
16101  * Based on:
16102  * Ext JS Library 1.1.1
16103  * Copyright(c) 2006-2007, Ext JS, LLC.
16104  *
16105  * Originally Released Under LGPL - original licence link has changed is not relivant.
16106  *
16107  * Fork - LGPL
16108  * <script type="text/javascript">
16109  */
16110  
16111 /**
16112  * @class Roo.menu.DateItem
16113  * @extends Roo.menu.Adapter
16114  * A menu item that wraps the {@link Roo.DatPicker} component.
16115  * @constructor
16116  * Creates a new DateItem
16117  * @param {Object} config Configuration options
16118  */
16119 Roo.menu.DateItem = function(config){
16120     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16121     /** The Roo.DatePicker object @type Roo.DatePicker */
16122     this.picker = this.component;
16123     this.addEvents({select: true});
16124     
16125     this.picker.on("render", function(picker){
16126         picker.getEl().swallowEvent("click");
16127         picker.container.addClass("x-menu-date-item");
16128     });
16129
16130     this.picker.on("select", this.onSelect, this);
16131 };
16132
16133 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16134     // private
16135     onSelect : function(picker, date){
16136         this.fireEvent("select", this, date, picker);
16137         Roo.menu.DateItem.superclass.handleClick.call(this);
16138     }
16139 });/*
16140  * Based on:
16141  * Ext JS Library 1.1.1
16142  * Copyright(c) 2006-2007, Ext JS, LLC.
16143  *
16144  * Originally Released Under LGPL - original licence link has changed is not relivant.
16145  *
16146  * Fork - LGPL
16147  * <script type="text/javascript">
16148  */
16149  
16150 /**
16151  * @class Roo.menu.ColorItem
16152  * @extends Roo.menu.Adapter
16153  * A menu item that wraps the {@link Roo.ColorPalette} component.
16154  * @constructor
16155  * Creates a new ColorItem
16156  * @param {Object} config Configuration options
16157  */
16158 Roo.menu.ColorItem = function(config){
16159     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16160     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16161     this.palette = this.component;
16162     this.relayEvents(this.palette, ["select"]);
16163     if(this.selectHandler){
16164         this.on('select', this.selectHandler, this.scope);
16165     }
16166 };
16167 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16168  * Based on:
16169  * Ext JS Library 1.1.1
16170  * Copyright(c) 2006-2007, Ext JS, LLC.
16171  *
16172  * Originally Released Under LGPL - original licence link has changed is not relivant.
16173  *
16174  * Fork - LGPL
16175  * <script type="text/javascript">
16176  */
16177  
16178
16179 /**
16180  * @class Roo.menu.DateMenu
16181  * @extends Roo.menu.Menu
16182  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16183  * @constructor
16184  * Creates a new DateMenu
16185  * @param {Object} config Configuration options
16186  */
16187 Roo.menu.DateMenu = function(config){
16188     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16189     this.plain = true;
16190     var di = new Roo.menu.DateItem(config);
16191     this.add(di);
16192     /**
16193      * The {@link Roo.DatePicker} instance for this DateMenu
16194      * @type DatePicker
16195      */
16196     this.picker = di.picker;
16197     /**
16198      * @event select
16199      * @param {DatePicker} picker
16200      * @param {Date} date
16201      */
16202     this.relayEvents(di, ["select"]);
16203     this.on('beforeshow', function(){
16204         if(this.picker){
16205             this.picker.hideMonthPicker(false);
16206         }
16207     }, this);
16208 };
16209 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16210     cls:'x-date-menu'
16211 });/*
16212  * Based on:
16213  * Ext JS Library 1.1.1
16214  * Copyright(c) 2006-2007, Ext JS, LLC.
16215  *
16216  * Originally Released Under LGPL - original licence link has changed is not relivant.
16217  *
16218  * Fork - LGPL
16219  * <script type="text/javascript">
16220  */
16221  
16222
16223 /**
16224  * @class Roo.menu.ColorMenu
16225  * @extends Roo.menu.Menu
16226  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16227  * @constructor
16228  * Creates a new ColorMenu
16229  * @param {Object} config Configuration options
16230  */
16231 Roo.menu.ColorMenu = function(config){
16232     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16233     this.plain = true;
16234     var ci = new Roo.menu.ColorItem(config);
16235     this.add(ci);
16236     /**
16237      * The {@link Roo.ColorPalette} instance for this ColorMenu
16238      * @type ColorPalette
16239      */
16240     this.palette = ci.palette;
16241     /**
16242      * @event select
16243      * @param {ColorPalette} palette
16244      * @param {String} color
16245      */
16246     this.relayEvents(ci, ["select"]);
16247 };
16248 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16249  * Based on:
16250  * Ext JS Library 1.1.1
16251  * Copyright(c) 2006-2007, Ext JS, LLC.
16252  *
16253  * Originally Released Under LGPL - original licence link has changed is not relivant.
16254  *
16255  * Fork - LGPL
16256  * <script type="text/javascript">
16257  */
16258  
16259 /**
16260  * @class Roo.form.TextItem
16261  * @extends Roo.BoxComponent
16262  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16263  * @constructor
16264  * Creates a new TextItem
16265  * @param {Object} config Configuration options
16266  */
16267 Roo.form.TextItem = function(config){
16268     Roo.form.TextItem.superclass.constructor.call(this, config);
16269 };
16270
16271 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
16272     
16273     /**
16274      * @cfg {String} tag the tag for this item (default div)
16275      */
16276     tag : 'div',
16277     /**
16278      * @cfg {String} html the content for this item
16279      */
16280     html : '',
16281     /**
16282      * @cfg {String} cls the class for this item
16283      */
16284     cls : '',
16285     
16286     getAutoCreate : function()
16287     {
16288         var cfg = {
16289             id: this.id,
16290             tag: this.tag,
16291             html: this.html
16292         };
16293         
16294         return cfg;
16295         
16296     }
16297     
16298 });/*
16299  * Based on:
16300  * Ext JS Library 1.1.1
16301  * Copyright(c) 2006-2007, Ext JS, LLC.
16302  *
16303  * Originally Released Under LGPL - original licence link has changed is not relivant.
16304  *
16305  * Fork - LGPL
16306  * <script type="text/javascript">
16307  */
16308  
16309 /**
16310  * @class Roo.form.Field
16311  * @extends Roo.BoxComponent
16312  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16313  * @constructor
16314  * Creates a new Field
16315  * @param {Object} config Configuration options
16316  */
16317 Roo.form.Field = function(config){
16318     Roo.form.Field.superclass.constructor.call(this, config);
16319 };
16320
16321 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16322     /**
16323      * @cfg {String} fieldLabel Label to use when rendering a form.
16324      */
16325        /**
16326      * @cfg {String} qtip Mouse over tip
16327      */
16328      
16329     /**
16330      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16331      */
16332     invalidClass : "x-form-invalid",
16333     /**
16334      * @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")
16335      */
16336     invalidText : "The value in this field is invalid",
16337     /**
16338      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16339      */
16340     focusClass : "x-form-focus",
16341     /**
16342      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16343       automatic validation (defaults to "keyup").
16344      */
16345     validationEvent : "keyup",
16346     /**
16347      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16348      */
16349     validateOnBlur : true,
16350     /**
16351      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16352      */
16353     validationDelay : 250,
16354     /**
16355      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16356      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16357      */
16358     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16359     /**
16360      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16361      */
16362     fieldClass : "x-form-field",
16363     /**
16364      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16365      *<pre>
16366 Value         Description
16367 -----------   ----------------------------------------------------------------------
16368 qtip          Display a quick tip when the user hovers over the field
16369 title         Display a default browser title attribute popup
16370 under         Add a block div beneath the field containing the error text
16371 side          Add an error icon to the right of the field with a popup on hover
16372 [element id]  Add the error text directly to the innerHTML of the specified element
16373 </pre>
16374      */
16375     msgTarget : 'qtip',
16376     /**
16377      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16378      */
16379     msgFx : 'normal',
16380
16381     /**
16382      * @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.
16383      */
16384     readOnly : false,
16385
16386     /**
16387      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16388      */
16389     disabled : false,
16390
16391     /**
16392      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16393      */
16394     inputType : undefined,
16395     
16396     /**
16397      * @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).
16398          */
16399         tabIndex : undefined,
16400         
16401     // private
16402     isFormField : true,
16403
16404     // private
16405     hasFocus : false,
16406     /**
16407      * @property {Roo.Element} fieldEl
16408      * Element Containing the rendered Field (with label etc.)
16409      */
16410     /**
16411      * @cfg {Mixed} value A value to initialize this field with.
16412      */
16413     value : undefined,
16414
16415     /**
16416      * @cfg {String} name The field's HTML name attribute.
16417      */
16418     /**
16419      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16420      */
16421     // private
16422     loadedValue : false,
16423      
16424      
16425         // private ??
16426         initComponent : function(){
16427         Roo.form.Field.superclass.initComponent.call(this);
16428         this.addEvents({
16429             /**
16430              * @event focus
16431              * Fires when this field receives input focus.
16432              * @param {Roo.form.Field} this
16433              */
16434             focus : true,
16435             /**
16436              * @event blur
16437              * Fires when this field loses input focus.
16438              * @param {Roo.form.Field} this
16439              */
16440             blur : true,
16441             /**
16442              * @event specialkey
16443              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16444              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16445              * @param {Roo.form.Field} this
16446              * @param {Roo.EventObject} e The event object
16447              */
16448             specialkey : true,
16449             /**
16450              * @event change
16451              * Fires just before the field blurs if the field value has changed.
16452              * @param {Roo.form.Field} this
16453              * @param {Mixed} newValue The new value
16454              * @param {Mixed} oldValue The original value
16455              */
16456             change : true,
16457             /**
16458              * @event invalid
16459              * Fires after the field has been marked as invalid.
16460              * @param {Roo.form.Field} this
16461              * @param {String} msg The validation message
16462              */
16463             invalid : true,
16464             /**
16465              * @event valid
16466              * Fires after the field has been validated with no errors.
16467              * @param {Roo.form.Field} this
16468              */
16469             valid : true,
16470              /**
16471              * @event keyup
16472              * Fires after the key up
16473              * @param {Roo.form.Field} this
16474              * @param {Roo.EventObject}  e The event Object
16475              */
16476             keyup : true
16477         });
16478     },
16479
16480     /**
16481      * Returns the name attribute of the field if available
16482      * @return {String} name The field name
16483      */
16484     getName: function(){
16485          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16486     },
16487
16488     // private
16489     onRender : function(ct, position){
16490         Roo.form.Field.superclass.onRender.call(this, ct, position);
16491         if(!this.el){
16492             var cfg = this.getAutoCreate();
16493             if(!cfg.name){
16494                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16495             }
16496             if (!cfg.name.length) {
16497                 delete cfg.name;
16498             }
16499             if(this.inputType){
16500                 cfg.type = this.inputType;
16501             }
16502             this.el = ct.createChild(cfg, position);
16503         }
16504         var type = this.el.dom.type;
16505         if(type){
16506             if(type == 'password'){
16507                 type = 'text';
16508             }
16509             this.el.addClass('x-form-'+type);
16510         }
16511         if(this.readOnly){
16512             this.el.dom.readOnly = true;
16513         }
16514         if(this.tabIndex !== undefined){
16515             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16516         }
16517
16518         this.el.addClass([this.fieldClass, this.cls]);
16519         this.initValue();
16520     },
16521
16522     /**
16523      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16524      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16525      * @return {Roo.form.Field} this
16526      */
16527     applyTo : function(target){
16528         this.allowDomMove = false;
16529         this.el = Roo.get(target);
16530         this.render(this.el.dom.parentNode);
16531         return this;
16532     },
16533
16534     // private
16535     initValue : function(){
16536         if(this.value !== undefined){
16537             this.setValue(this.value);
16538         }else if(this.el.dom.value.length > 0){
16539             this.setValue(this.el.dom.value);
16540         }
16541     },
16542
16543     /**
16544      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16545      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16546      */
16547     isDirty : function() {
16548         if(this.disabled) {
16549             return false;
16550         }
16551         return String(this.getValue()) !== String(this.originalValue);
16552     },
16553
16554     /**
16555      * stores the current value in loadedValue
16556      */
16557     resetHasChanged : function()
16558     {
16559         this.loadedValue = String(this.getValue());
16560     },
16561     /**
16562      * checks the current value against the 'loaded' value.
16563      * Note - will return false if 'resetHasChanged' has not been called first.
16564      */
16565     hasChanged : function()
16566     {
16567         if(this.disabled || this.readOnly) {
16568             return false;
16569         }
16570         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16571     },
16572     
16573     
16574     
16575     // private
16576     afterRender : function(){
16577         Roo.form.Field.superclass.afterRender.call(this);
16578         this.initEvents();
16579     },
16580
16581     // private
16582     fireKey : function(e){
16583         //Roo.log('field ' + e.getKey());
16584         if(e.isNavKeyPress()){
16585             this.fireEvent("specialkey", this, e);
16586         }
16587     },
16588
16589     /**
16590      * Resets the current field value to the originally loaded value and clears any validation messages
16591      */
16592     reset : function(){
16593         this.setValue(this.resetValue);
16594         this.originalValue = this.getValue();
16595         this.clearInvalid();
16596     },
16597
16598     // private
16599     initEvents : function(){
16600         // safari killled keypress - so keydown is now used..
16601         this.el.on("keydown" , this.fireKey,  this);
16602         this.el.on("focus", this.onFocus,  this);
16603         this.el.on("blur", this.onBlur,  this);
16604         this.el.relayEvent('keyup', this);
16605
16606         // reference to original value for reset
16607         this.originalValue = this.getValue();
16608         this.resetValue =  this.getValue();
16609     },
16610
16611     // private
16612     onFocus : function(){
16613         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16614             this.el.addClass(this.focusClass);
16615         }
16616         if(!this.hasFocus){
16617             this.hasFocus = true;
16618             this.startValue = this.getValue();
16619             this.fireEvent("focus", this);
16620         }
16621     },
16622
16623     beforeBlur : Roo.emptyFn,
16624
16625     // private
16626     onBlur : function(){
16627         this.beforeBlur();
16628         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16629             this.el.removeClass(this.focusClass);
16630         }
16631         this.hasFocus = false;
16632         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16633             this.validate();
16634         }
16635         var v = this.getValue();
16636         if(String(v) !== String(this.startValue)){
16637             this.fireEvent('change', this, v, this.startValue);
16638         }
16639         this.fireEvent("blur", this);
16640     },
16641
16642     /**
16643      * Returns whether or not the field value is currently valid
16644      * @param {Boolean} preventMark True to disable marking the field invalid
16645      * @return {Boolean} True if the value is valid, else false
16646      */
16647     isValid : function(preventMark){
16648         if(this.disabled){
16649             return true;
16650         }
16651         var restore = this.preventMark;
16652         this.preventMark = preventMark === true;
16653         var v = this.validateValue(this.processValue(this.getRawValue()));
16654         this.preventMark = restore;
16655         return v;
16656     },
16657
16658     /**
16659      * Validates the field value
16660      * @return {Boolean} True if the value is valid, else false
16661      */
16662     validate : function(){
16663         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16664             this.clearInvalid();
16665             return true;
16666         }
16667         return false;
16668     },
16669
16670     processValue : function(value){
16671         return value;
16672     },
16673
16674     // private
16675     // Subclasses should provide the validation implementation by overriding this
16676     validateValue : function(value){
16677         return true;
16678     },
16679
16680     /**
16681      * Mark this field as invalid
16682      * @param {String} msg The validation message
16683      */
16684     markInvalid : function(msg){
16685         if(!this.rendered || this.preventMark){ // not rendered
16686             return;
16687         }
16688         
16689         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16690         
16691         obj.el.addClass(this.invalidClass);
16692         msg = msg || this.invalidText;
16693         switch(this.msgTarget){
16694             case 'qtip':
16695                 obj.el.dom.qtip = msg;
16696                 obj.el.dom.qclass = 'x-form-invalid-tip';
16697                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16698                     Roo.QuickTips.enable();
16699                 }
16700                 break;
16701             case 'title':
16702                 this.el.dom.title = msg;
16703                 break;
16704             case 'under':
16705                 if(!this.errorEl){
16706                     var elp = this.el.findParent('.x-form-element', 5, true);
16707                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16708                     this.errorEl.setWidth(elp.getWidth(true)-20);
16709                 }
16710                 this.errorEl.update(msg);
16711                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16712                 break;
16713             case 'side':
16714                 if(!this.errorIcon){
16715                     var elp = this.el.findParent('.x-form-element', 5, true);
16716                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16717                 }
16718                 this.alignErrorIcon();
16719                 this.errorIcon.dom.qtip = msg;
16720                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16721                 this.errorIcon.show();
16722                 this.on('resize', this.alignErrorIcon, this);
16723                 break;
16724             default:
16725                 var t = Roo.getDom(this.msgTarget);
16726                 t.innerHTML = msg;
16727                 t.style.display = this.msgDisplay;
16728                 break;
16729         }
16730         this.fireEvent('invalid', this, msg);
16731     },
16732
16733     // private
16734     alignErrorIcon : function(){
16735         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16736     },
16737
16738     /**
16739      * Clear any invalid styles/messages for this field
16740      */
16741     clearInvalid : function(){
16742         if(!this.rendered || this.preventMark){ // not rendered
16743             return;
16744         }
16745         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16746         
16747         obj.el.removeClass(this.invalidClass);
16748         switch(this.msgTarget){
16749             case 'qtip':
16750                 obj.el.dom.qtip = '';
16751                 break;
16752             case 'title':
16753                 this.el.dom.title = '';
16754                 break;
16755             case 'under':
16756                 if(this.errorEl){
16757                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16758                 }
16759                 break;
16760             case 'side':
16761                 if(this.errorIcon){
16762                     this.errorIcon.dom.qtip = '';
16763                     this.errorIcon.hide();
16764                     this.un('resize', this.alignErrorIcon, this);
16765                 }
16766                 break;
16767             default:
16768                 var t = Roo.getDom(this.msgTarget);
16769                 t.innerHTML = '';
16770                 t.style.display = 'none';
16771                 break;
16772         }
16773         this.fireEvent('valid', this);
16774     },
16775
16776     /**
16777      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16778      * @return {Mixed} value The field value
16779      */
16780     getRawValue : function(){
16781         var v = this.el.getValue();
16782         
16783         return v;
16784     },
16785
16786     /**
16787      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16788      * @return {Mixed} value The field value
16789      */
16790     getValue : function(){
16791         var v = this.el.getValue();
16792          
16793         return v;
16794     },
16795
16796     /**
16797      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16798      * @param {Mixed} value The value to set
16799      */
16800     setRawValue : function(v){
16801         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16802     },
16803
16804     /**
16805      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16806      * @param {Mixed} value The value to set
16807      */
16808     setValue : function(v){
16809         this.value = v;
16810         if(this.rendered){
16811             this.el.dom.value = (v === null || v === undefined ? '' : v);
16812              this.validate();
16813         }
16814     },
16815
16816     adjustSize : function(w, h){
16817         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16818         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16819         return s;
16820     },
16821
16822     adjustWidth : function(tag, w){
16823         tag = tag.toLowerCase();
16824         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16825             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16826                 if(tag == 'input'){
16827                     return w + 2;
16828                 }
16829                 if(tag == 'textarea'){
16830                     return w-2;
16831                 }
16832             }else if(Roo.isOpera){
16833                 if(tag == 'input'){
16834                     return w + 2;
16835                 }
16836                 if(tag == 'textarea'){
16837                     return w-2;
16838                 }
16839             }
16840         }
16841         return w;
16842     }
16843 });
16844
16845
16846 // anything other than normal should be considered experimental
16847 Roo.form.Field.msgFx = {
16848     normal : {
16849         show: function(msgEl, f){
16850             msgEl.setDisplayed('block');
16851         },
16852
16853         hide : function(msgEl, f){
16854             msgEl.setDisplayed(false).update('');
16855         }
16856     },
16857
16858     slide : {
16859         show: function(msgEl, f){
16860             msgEl.slideIn('t', {stopFx:true});
16861         },
16862
16863         hide : function(msgEl, f){
16864             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16865         }
16866     },
16867
16868     slideRight : {
16869         show: function(msgEl, f){
16870             msgEl.fixDisplay();
16871             msgEl.alignTo(f.el, 'tl-tr');
16872             msgEl.slideIn('l', {stopFx:true});
16873         },
16874
16875         hide : function(msgEl, f){
16876             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16877         }
16878     }
16879 };/*
16880  * Based on:
16881  * Ext JS Library 1.1.1
16882  * Copyright(c) 2006-2007, Ext JS, LLC.
16883  *
16884  * Originally Released Under LGPL - original licence link has changed is not relivant.
16885  *
16886  * Fork - LGPL
16887  * <script type="text/javascript">
16888  */
16889  
16890
16891 /**
16892  * @class Roo.form.TextField
16893  * @extends Roo.form.Field
16894  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16895  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16896  * @constructor
16897  * Creates a new TextField
16898  * @param {Object} config Configuration options
16899  */
16900 Roo.form.TextField = function(config){
16901     Roo.form.TextField.superclass.constructor.call(this, config);
16902     this.addEvents({
16903         /**
16904          * @event autosize
16905          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16906          * according to the default logic, but this event provides a hook for the developer to apply additional
16907          * logic at runtime to resize the field if needed.
16908              * @param {Roo.form.Field} this This text field
16909              * @param {Number} width The new field width
16910              */
16911         autosize : true
16912     });
16913 };
16914
16915 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16916     /**
16917      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16918      */
16919     grow : false,
16920     /**
16921      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16922      */
16923     growMin : 30,
16924     /**
16925      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16926      */
16927     growMax : 800,
16928     /**
16929      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16930      */
16931     vtype : null,
16932     /**
16933      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16934      */
16935     maskRe : null,
16936     /**
16937      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16938      */
16939     disableKeyFilter : false,
16940     /**
16941      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16942      */
16943     allowBlank : true,
16944     /**
16945      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16946      */
16947     minLength : 0,
16948     /**
16949      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16950      */
16951     maxLength : Number.MAX_VALUE,
16952     /**
16953      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16954      */
16955     minLengthText : "The minimum length for this field is {0}",
16956     /**
16957      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16958      */
16959     maxLengthText : "The maximum length for this field is {0}",
16960     /**
16961      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16962      */
16963     selectOnFocus : false,
16964     /**
16965      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16966      */
16967     blankText : "This field is required",
16968     /**
16969      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16970      * If available, this function will be called only after the basic validators all return true, and will be passed the
16971      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16972      */
16973     validator : null,
16974     /**
16975      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16976      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16977      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16978      */
16979     regex : null,
16980     /**
16981      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16982      */
16983     regexText : "",
16984     /**
16985      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16986      */
16987     emptyText : null,
16988    
16989
16990     // private
16991     initEvents : function()
16992     {
16993         if (this.emptyText) {
16994             this.el.attr('placeholder', this.emptyText);
16995         }
16996         
16997         Roo.form.TextField.superclass.initEvents.call(this);
16998         if(this.validationEvent == 'keyup'){
16999             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
17000             this.el.on('keyup', this.filterValidation, this);
17001         }
17002         else if(this.validationEvent !== false){
17003             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
17004         }
17005         
17006         if(this.selectOnFocus){
17007             this.on("focus", this.preFocus, this);
17008             
17009         }
17010         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
17011             this.el.on("keypress", this.filterKeys, this);
17012         }
17013         if(this.grow){
17014             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
17015             this.el.on("click", this.autoSize,  this);
17016         }
17017         if(this.el.is('input[type=password]') && Roo.isSafari){
17018             this.el.on('keydown', this.SafariOnKeyDown, this);
17019         }
17020     },
17021
17022     processValue : function(value){
17023         if(this.stripCharsRe){
17024             var newValue = value.replace(this.stripCharsRe, '');
17025             if(newValue !== value){
17026                 this.setRawValue(newValue);
17027                 return newValue;
17028             }
17029         }
17030         return value;
17031     },
17032
17033     filterValidation : function(e){
17034         if(!e.isNavKeyPress()){
17035             this.validationTask.delay(this.validationDelay);
17036         }
17037     },
17038
17039     // private
17040     onKeyUp : function(e){
17041         if(!e.isNavKeyPress()){
17042             this.autoSize();
17043         }
17044     },
17045
17046     /**
17047      * Resets the current field value to the originally-loaded value and clears any validation messages.
17048      *  
17049      */
17050     reset : function(){
17051         Roo.form.TextField.superclass.reset.call(this);
17052        
17053     },
17054
17055     
17056     // private
17057     preFocus : function(){
17058         
17059         if(this.selectOnFocus){
17060             this.el.dom.select();
17061         }
17062     },
17063
17064     
17065     // private
17066     filterKeys : function(e){
17067         var k = e.getKey();
17068         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17069             return;
17070         }
17071         var c = e.getCharCode(), cc = String.fromCharCode(c);
17072         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17073             return;
17074         }
17075         if(!this.maskRe.test(cc)){
17076             e.stopEvent();
17077         }
17078     },
17079
17080     setValue : function(v){
17081         
17082         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17083         
17084         this.autoSize();
17085     },
17086
17087     /**
17088      * Validates a value according to the field's validation rules and marks the field as invalid
17089      * if the validation fails
17090      * @param {Mixed} value The value to validate
17091      * @return {Boolean} True if the value is valid, else false
17092      */
17093     validateValue : function(value){
17094         if(value.length < 1)  { // if it's blank
17095              if(this.allowBlank){
17096                 this.clearInvalid();
17097                 return true;
17098              }else{
17099                 this.markInvalid(this.blankText);
17100                 return false;
17101              }
17102         }
17103         if(value.length < this.minLength){
17104             this.markInvalid(String.format(this.minLengthText, this.minLength));
17105             return false;
17106         }
17107         if(value.length > this.maxLength){
17108             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17109             return false;
17110         }
17111         if(this.vtype){
17112             var vt = Roo.form.VTypes;
17113             if(!vt[this.vtype](value, this)){
17114                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17115                 return false;
17116             }
17117         }
17118         if(typeof this.validator == "function"){
17119             var msg = this.validator(value);
17120             if(msg !== true){
17121                 this.markInvalid(msg);
17122                 return false;
17123             }
17124         }
17125         if(this.regex && !this.regex.test(value)){
17126             this.markInvalid(this.regexText);
17127             return false;
17128         }
17129         return true;
17130     },
17131
17132     /**
17133      * Selects text in this field
17134      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17135      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17136      */
17137     selectText : function(start, end){
17138         var v = this.getRawValue();
17139         if(v.length > 0){
17140             start = start === undefined ? 0 : start;
17141             end = end === undefined ? v.length : end;
17142             var d = this.el.dom;
17143             if(d.setSelectionRange){
17144                 d.setSelectionRange(start, end);
17145             }else if(d.createTextRange){
17146                 var range = d.createTextRange();
17147                 range.moveStart("character", start);
17148                 range.moveEnd("character", v.length-end);
17149                 range.select();
17150             }
17151         }
17152     },
17153
17154     /**
17155      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17156      * This only takes effect if grow = true, and fires the autosize event.
17157      */
17158     autoSize : function(){
17159         if(!this.grow || !this.rendered){
17160             return;
17161         }
17162         if(!this.metrics){
17163             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17164         }
17165         var el = this.el;
17166         var v = el.dom.value;
17167         var d = document.createElement('div');
17168         d.appendChild(document.createTextNode(v));
17169         v = d.innerHTML;
17170         d = null;
17171         v += "&#160;";
17172         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17173         this.el.setWidth(w);
17174         this.fireEvent("autosize", this, w);
17175     },
17176     
17177     // private
17178     SafariOnKeyDown : function(event)
17179     {
17180         // this is a workaround for a password hang bug on chrome/ webkit.
17181         
17182         var isSelectAll = false;
17183         
17184         if(this.el.dom.selectionEnd > 0){
17185             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17186         }
17187         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17188             event.preventDefault();
17189             this.setValue('');
17190             return;
17191         }
17192         
17193         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17194             
17195             event.preventDefault();
17196             // this is very hacky as keydown always get's upper case.
17197             
17198             var cc = String.fromCharCode(event.getCharCode());
17199             
17200             
17201             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17202             
17203         }
17204         
17205         
17206     }
17207 });/*
17208  * Based on:
17209  * Ext JS Library 1.1.1
17210  * Copyright(c) 2006-2007, Ext JS, LLC.
17211  *
17212  * Originally Released Under LGPL - original licence link has changed is not relivant.
17213  *
17214  * Fork - LGPL
17215  * <script type="text/javascript">
17216  */
17217  
17218 /**
17219  * @class Roo.form.Hidden
17220  * @extends Roo.form.TextField
17221  * Simple Hidden element used on forms 
17222  * 
17223  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17224  * 
17225  * @constructor
17226  * Creates a new Hidden form element.
17227  * @param {Object} config Configuration options
17228  */
17229
17230
17231
17232 // easy hidden field...
17233 Roo.form.Hidden = function(config){
17234     Roo.form.Hidden.superclass.constructor.call(this, config);
17235 };
17236   
17237 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17238     fieldLabel:      '',
17239     inputType:      'hidden',
17240     width:          50,
17241     allowBlank:     true,
17242     labelSeparator: '',
17243     hidden:         true,
17244     itemCls :       'x-form-item-display-none'
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.TriggerField
17263  * @extends Roo.form.TextField
17264  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17265  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17266  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17267  * for which you can provide a custom implementation.  For example:
17268  * <pre><code>
17269 var trigger = new Roo.form.TriggerField();
17270 trigger.onTriggerClick = myTriggerFn;
17271 trigger.applyTo('my-field');
17272 </code></pre>
17273  *
17274  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17275  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17276  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17277  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17278  * @constructor
17279  * Create a new TriggerField.
17280  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17281  * to the base TextField)
17282  */
17283 Roo.form.TriggerField = function(config){
17284     this.mimicing = false;
17285     Roo.form.TriggerField.superclass.constructor.call(this, config);
17286 };
17287
17288 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17289     /**
17290      * @cfg {String} triggerClass A CSS class to apply to the trigger
17291      */
17292     /**
17293      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17294      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17295      */
17296     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17297     /**
17298      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17299      */
17300     hideTrigger:false,
17301
17302     /** @cfg {Boolean} grow @hide */
17303     /** @cfg {Number} growMin @hide */
17304     /** @cfg {Number} growMax @hide */
17305
17306     /**
17307      * @hide 
17308      * @method
17309      */
17310     autoSize: Roo.emptyFn,
17311     // private
17312     monitorTab : true,
17313     // private
17314     deferHeight : true,
17315
17316     
17317     actionMode : 'wrap',
17318     // private
17319     onResize : function(w, h){
17320         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17321         if(typeof w == 'number'){
17322             var x = w - this.trigger.getWidth();
17323             this.el.setWidth(this.adjustWidth('input', x));
17324             this.trigger.setStyle('left', x+'px');
17325         }
17326     },
17327
17328     // private
17329     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17330
17331     // private
17332     getResizeEl : function(){
17333         return this.wrap;
17334     },
17335
17336     // private
17337     getPositionEl : function(){
17338         return this.wrap;
17339     },
17340
17341     // private
17342     alignErrorIcon : function(){
17343         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17344     },
17345
17346     // private
17347     onRender : function(ct, position){
17348         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17349         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17350         this.trigger = this.wrap.createChild(this.triggerConfig ||
17351                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17352         if(this.hideTrigger){
17353             this.trigger.setDisplayed(false);
17354         }
17355         this.initTrigger();
17356         if(!this.width){
17357             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17358         }
17359     },
17360
17361     // private
17362     initTrigger : function(){
17363         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17364         this.trigger.addClassOnOver('x-form-trigger-over');
17365         this.trigger.addClassOnClick('x-form-trigger-click');
17366     },
17367
17368     // private
17369     onDestroy : function(){
17370         if(this.trigger){
17371             this.trigger.removeAllListeners();
17372             this.trigger.remove();
17373         }
17374         if(this.wrap){
17375             this.wrap.remove();
17376         }
17377         Roo.form.TriggerField.superclass.onDestroy.call(this);
17378     },
17379
17380     // private
17381     onFocus : function(){
17382         Roo.form.TriggerField.superclass.onFocus.call(this);
17383         if(!this.mimicing){
17384             this.wrap.addClass('x-trigger-wrap-focus');
17385             this.mimicing = true;
17386             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17387             if(this.monitorTab){
17388                 this.el.on("keydown", this.checkTab, this);
17389             }
17390         }
17391     },
17392
17393     // private
17394     checkTab : function(e){
17395         if(e.getKey() == e.TAB){
17396             this.triggerBlur();
17397         }
17398     },
17399
17400     // private
17401     onBlur : function(){
17402         // do nothing
17403     },
17404
17405     // private
17406     mimicBlur : function(e, t){
17407         if(!this.wrap.contains(t) && this.validateBlur()){
17408             this.triggerBlur();
17409         }
17410     },
17411
17412     // private
17413     triggerBlur : function(){
17414         this.mimicing = false;
17415         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17416         if(this.monitorTab){
17417             this.el.un("keydown", this.checkTab, this);
17418         }
17419         this.wrap.removeClass('x-trigger-wrap-focus');
17420         Roo.form.TriggerField.superclass.onBlur.call(this);
17421     },
17422
17423     // private
17424     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17425     validateBlur : function(e, t){
17426         return true;
17427     },
17428
17429     // private
17430     onDisable : function(){
17431         Roo.form.TriggerField.superclass.onDisable.call(this);
17432         if(this.wrap){
17433             this.wrap.addClass('x-item-disabled');
17434         }
17435     },
17436
17437     // private
17438     onEnable : function(){
17439         Roo.form.TriggerField.superclass.onEnable.call(this);
17440         if(this.wrap){
17441             this.wrap.removeClass('x-item-disabled');
17442         }
17443     },
17444
17445     // private
17446     onShow : function(){
17447         var ae = this.getActionEl();
17448         
17449         if(ae){
17450             ae.dom.style.display = '';
17451             ae.dom.style.visibility = 'visible';
17452         }
17453     },
17454
17455     // private
17456     
17457     onHide : function(){
17458         var ae = this.getActionEl();
17459         ae.dom.style.display = 'none';
17460     },
17461
17462     /**
17463      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17464      * by an implementing function.
17465      * @method
17466      * @param {EventObject} e
17467      */
17468     onTriggerClick : Roo.emptyFn
17469 });
17470
17471 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17472 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17473 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17474 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17475     initComponent : function(){
17476         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17477
17478         this.triggerConfig = {
17479             tag:'span', cls:'x-form-twin-triggers', cn:[
17480             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17481             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17482         ]};
17483     },
17484
17485     getTrigger : function(index){
17486         return this.triggers[index];
17487     },
17488
17489     initTrigger : function(){
17490         var ts = this.trigger.select('.x-form-trigger', true);
17491         this.wrap.setStyle('overflow', 'hidden');
17492         var triggerField = this;
17493         ts.each(function(t, all, index){
17494             t.hide = function(){
17495                 var w = triggerField.wrap.getWidth();
17496                 this.dom.style.display = 'none';
17497                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17498             };
17499             t.show = function(){
17500                 var w = triggerField.wrap.getWidth();
17501                 this.dom.style.display = '';
17502                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17503             };
17504             var triggerIndex = 'Trigger'+(index+1);
17505
17506             if(this['hide'+triggerIndex]){
17507                 t.dom.style.display = 'none';
17508             }
17509             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17510             t.addClassOnOver('x-form-trigger-over');
17511             t.addClassOnClick('x-form-trigger-click');
17512         }, this);
17513         this.triggers = ts.elements;
17514     },
17515
17516     onTrigger1Click : Roo.emptyFn,
17517     onTrigger2Click : Roo.emptyFn
17518 });/*
17519  * Based on:
17520  * Ext JS Library 1.1.1
17521  * Copyright(c) 2006-2007, Ext JS, LLC.
17522  *
17523  * Originally Released Under LGPL - original licence link has changed is not relivant.
17524  *
17525  * Fork - LGPL
17526  * <script type="text/javascript">
17527  */
17528  
17529 /**
17530  * @class Roo.form.TextArea
17531  * @extends Roo.form.TextField
17532  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17533  * support for auto-sizing.
17534  * @constructor
17535  * Creates a new TextArea
17536  * @param {Object} config Configuration options
17537  */
17538 Roo.form.TextArea = function(config){
17539     Roo.form.TextArea.superclass.constructor.call(this, config);
17540     // these are provided exchanges for backwards compat
17541     // minHeight/maxHeight were replaced by growMin/growMax to be
17542     // compatible with TextField growing config values
17543     if(this.minHeight !== undefined){
17544         this.growMin = this.minHeight;
17545     }
17546     if(this.maxHeight !== undefined){
17547         this.growMax = this.maxHeight;
17548     }
17549 };
17550
17551 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17552     /**
17553      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17554      */
17555     growMin : 60,
17556     /**
17557      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17558      */
17559     growMax: 1000,
17560     /**
17561      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17562      * in the field (equivalent to setting overflow: hidden, defaults to false)
17563      */
17564     preventScrollbars: false,
17565     /**
17566      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17567      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17568      */
17569
17570     // private
17571     onRender : function(ct, position){
17572         if(!this.el){
17573             this.defaultAutoCreate = {
17574                 tag: "textarea",
17575                 style:"width:300px;height:60px;",
17576                 autocomplete: "new-password"
17577             };
17578         }
17579         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17580         if(this.grow){
17581             this.textSizeEl = Roo.DomHelper.append(document.body, {
17582                 tag: "pre", cls: "x-form-grow-sizer"
17583             });
17584             if(this.preventScrollbars){
17585                 this.el.setStyle("overflow", "hidden");
17586             }
17587             this.el.setHeight(this.growMin);
17588         }
17589     },
17590
17591     onDestroy : function(){
17592         if(this.textSizeEl){
17593             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17594         }
17595         Roo.form.TextArea.superclass.onDestroy.call(this);
17596     },
17597
17598     // private
17599     onKeyUp : function(e){
17600         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17601             this.autoSize();
17602         }
17603     },
17604
17605     /**
17606      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17607      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17608      */
17609     autoSize : function(){
17610         if(!this.grow || !this.textSizeEl){
17611             return;
17612         }
17613         var el = this.el;
17614         var v = el.dom.value;
17615         var ts = this.textSizeEl;
17616
17617         ts.innerHTML = '';
17618         ts.appendChild(document.createTextNode(v));
17619         v = ts.innerHTML;
17620
17621         Roo.fly(ts).setWidth(this.el.getWidth());
17622         if(v.length < 1){
17623             v = "&#160;&#160;";
17624         }else{
17625             if(Roo.isIE){
17626                 v = v.replace(/\n/g, '<p>&#160;</p>');
17627             }
17628             v += "&#160;\n&#160;";
17629         }
17630         ts.innerHTML = v;
17631         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17632         if(h != this.lastHeight){
17633             this.lastHeight = h;
17634             this.el.setHeight(h);
17635             this.fireEvent("autosize", this, h);
17636         }
17637     }
17638 });/*
17639  * Based on:
17640  * Ext JS Library 1.1.1
17641  * Copyright(c) 2006-2007, Ext JS, LLC.
17642  *
17643  * Originally Released Under LGPL - original licence link has changed is not relivant.
17644  *
17645  * Fork - LGPL
17646  * <script type="text/javascript">
17647  */
17648  
17649
17650 /**
17651  * @class Roo.form.NumberField
17652  * @extends Roo.form.TextField
17653  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17654  * @constructor
17655  * Creates a new NumberField
17656  * @param {Object} config Configuration options
17657  */
17658 Roo.form.NumberField = function(config){
17659     Roo.form.NumberField.superclass.constructor.call(this, config);
17660 };
17661
17662 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17663     /**
17664      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17665      */
17666     fieldClass: "x-form-field x-form-num-field",
17667     /**
17668      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17669      */
17670     allowDecimals : true,
17671     /**
17672      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17673      */
17674     decimalSeparator : ".",
17675     /**
17676      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17677      */
17678     decimalPrecision : 2,
17679     /**
17680      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17681      */
17682     allowNegative : true,
17683     /**
17684      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17685      */
17686     minValue : Number.NEGATIVE_INFINITY,
17687     /**
17688      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17689      */
17690     maxValue : Number.MAX_VALUE,
17691     /**
17692      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17693      */
17694     minText : "The minimum value for this field is {0}",
17695     /**
17696      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17697      */
17698     maxText : "The maximum value for this field is {0}",
17699     /**
17700      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17701      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17702      */
17703     nanText : "{0} is not a valid number",
17704
17705     // private
17706     initEvents : function(){
17707         Roo.form.NumberField.superclass.initEvents.call(this);
17708         var allowed = "0123456789";
17709         if(this.allowDecimals){
17710             allowed += this.decimalSeparator;
17711         }
17712         if(this.allowNegative){
17713             allowed += "-";
17714         }
17715         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17716         var keyPress = function(e){
17717             var k = e.getKey();
17718             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17719                 return;
17720             }
17721             var c = e.getCharCode();
17722             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17723                 e.stopEvent();
17724             }
17725         };
17726         this.el.on("keypress", keyPress, this);
17727     },
17728
17729     // private
17730     validateValue : function(value){
17731         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17732             return false;
17733         }
17734         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17735              return true;
17736         }
17737         var num = this.parseValue(value);
17738         if(isNaN(num)){
17739             this.markInvalid(String.format(this.nanText, value));
17740             return false;
17741         }
17742         if(num < this.minValue){
17743             this.markInvalid(String.format(this.minText, this.minValue));
17744             return false;
17745         }
17746         if(num > this.maxValue){
17747             this.markInvalid(String.format(this.maxText, this.maxValue));
17748             return false;
17749         }
17750         return true;
17751     },
17752
17753     getValue : function(){
17754         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17755     },
17756
17757     // private
17758     parseValue : function(value){
17759         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17760         return isNaN(value) ? '' : value;
17761     },
17762
17763     // private
17764     fixPrecision : function(value){
17765         var nan = isNaN(value);
17766         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17767             return nan ? '' : value;
17768         }
17769         return parseFloat(value).toFixed(this.decimalPrecision);
17770     },
17771
17772     setValue : function(v){
17773         v = this.fixPrecision(v);
17774         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17775     },
17776
17777     // private
17778     decimalPrecisionFcn : function(v){
17779         return Math.floor(v);
17780     },
17781
17782     beforeBlur : function(){
17783         var v = this.parseValue(this.getRawValue());
17784         if(v){
17785             this.setValue(v);
17786         }
17787     }
17788 });/*
17789  * Based on:
17790  * Ext JS Library 1.1.1
17791  * Copyright(c) 2006-2007, Ext JS, LLC.
17792  *
17793  * Originally Released Under LGPL - original licence link has changed is not relivant.
17794  *
17795  * Fork - LGPL
17796  * <script type="text/javascript">
17797  */
17798  
17799 /**
17800  * @class Roo.form.DateField
17801  * @extends Roo.form.TriggerField
17802  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17803 * @constructor
17804 * Create a new DateField
17805 * @param {Object} config
17806  */
17807 Roo.form.DateField = function(config){
17808     Roo.form.DateField.superclass.constructor.call(this, config);
17809     
17810       this.addEvents({
17811          
17812         /**
17813          * @event select
17814          * Fires when a date is selected
17815              * @param {Roo.form.DateField} combo This combo box
17816              * @param {Date} date The date selected
17817              */
17818         'select' : true
17819          
17820     });
17821     
17822     
17823     if(typeof this.minValue == "string") {
17824         this.minValue = this.parseDate(this.minValue);
17825     }
17826     if(typeof this.maxValue == "string") {
17827         this.maxValue = this.parseDate(this.maxValue);
17828     }
17829     this.ddMatch = null;
17830     if(this.disabledDates){
17831         var dd = this.disabledDates;
17832         var re = "(?:";
17833         for(var i = 0; i < dd.length; i++){
17834             re += dd[i];
17835             if(i != dd.length-1) {
17836                 re += "|";
17837             }
17838         }
17839         this.ddMatch = new RegExp(re + ")");
17840     }
17841 };
17842
17843 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17844     /**
17845      * @cfg {String} format
17846      * The default date format string which can be overriden for localization support.  The format must be
17847      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17848      */
17849     format : "m/d/y",
17850     /**
17851      * @cfg {String} altFormats
17852      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17853      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17854      */
17855     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17856     /**
17857      * @cfg {Array} disabledDays
17858      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17859      */
17860     disabledDays : null,
17861     /**
17862      * @cfg {String} disabledDaysText
17863      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17864      */
17865     disabledDaysText : "Disabled",
17866     /**
17867      * @cfg {Array} disabledDates
17868      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17869      * expression so they are very powerful. Some examples:
17870      * <ul>
17871      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17872      * <li>["03/08", "09/16"] would disable those days for every year</li>
17873      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17874      * <li>["03/../2006"] would disable every day in March 2006</li>
17875      * <li>["^03"] would disable every day in every March</li>
17876      * </ul>
17877      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17878      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17879      */
17880     disabledDates : null,
17881     /**
17882      * @cfg {String} disabledDatesText
17883      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17884      */
17885     disabledDatesText : "Disabled",
17886     /**
17887      * @cfg {Date/String} minValue
17888      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17889      * valid format (defaults to null).
17890      */
17891     minValue : null,
17892     /**
17893      * @cfg {Date/String} maxValue
17894      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17895      * valid format (defaults to null).
17896      */
17897     maxValue : null,
17898     /**
17899      * @cfg {String} minText
17900      * The error text to display when the date in the cell is before minValue (defaults to
17901      * 'The date in this field must be after {minValue}').
17902      */
17903     minText : "The date in this field must be equal to or after {0}",
17904     /**
17905      * @cfg {String} maxText
17906      * The error text to display when the date in the cell is after maxValue (defaults to
17907      * 'The date in this field must be before {maxValue}').
17908      */
17909     maxText : "The date in this field must be equal to or before {0}",
17910     /**
17911      * @cfg {String} invalidText
17912      * The error text to display when the date in the field is invalid (defaults to
17913      * '{value} is not a valid date - it must be in the format {format}').
17914      */
17915     invalidText : "{0} is not a valid date - it must be in the format {1}",
17916     /**
17917      * @cfg {String} triggerClass
17918      * An additional CSS class used to style the trigger button.  The trigger will always get the
17919      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17920      * which displays a calendar icon).
17921      */
17922     triggerClass : 'x-form-date-trigger',
17923     
17924
17925     /**
17926      * @cfg {Boolean} useIso
17927      * if enabled, then the date field will use a hidden field to store the 
17928      * real value as iso formated date. default (false)
17929      */ 
17930     useIso : false,
17931     /**
17932      * @cfg {String/Object} autoCreate
17933      * A DomHelper element spec, or true for a default element spec (defaults to
17934      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17935      */ 
17936     // private
17937     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17938     
17939     // private
17940     hiddenField: false,
17941     
17942     onRender : function(ct, position)
17943     {
17944         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17945         if (this.useIso) {
17946             //this.el.dom.removeAttribute('name'); 
17947             Roo.log("Changing name?");
17948             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17949             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17950                     'before', true);
17951             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17952             // prevent input submission
17953             this.hiddenName = this.name;
17954         }
17955             
17956             
17957     },
17958     
17959     // private
17960     validateValue : function(value)
17961     {
17962         value = this.formatDate(value);
17963         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17964             Roo.log('super failed');
17965             return false;
17966         }
17967         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17968              return true;
17969         }
17970         var svalue = value;
17971         value = this.parseDate(value);
17972         if(!value){
17973             Roo.log('parse date failed' + svalue);
17974             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17975             return false;
17976         }
17977         var time = value.getTime();
17978         if(this.minValue && time < this.minValue.getTime()){
17979             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17980             return false;
17981         }
17982         if(this.maxValue && time > this.maxValue.getTime()){
17983             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17984             return false;
17985         }
17986         if(this.disabledDays){
17987             var day = value.getDay();
17988             for(var i = 0; i < this.disabledDays.length; i++) {
17989                 if(day === this.disabledDays[i]){
17990                     this.markInvalid(this.disabledDaysText);
17991                     return false;
17992                 }
17993             }
17994         }
17995         var fvalue = this.formatDate(value);
17996         if(this.ddMatch && this.ddMatch.test(fvalue)){
17997             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17998             return false;
17999         }
18000         return true;
18001     },
18002
18003     // private
18004     // Provides logic to override the default TriggerField.validateBlur which just returns true
18005     validateBlur : function(){
18006         return !this.menu || !this.menu.isVisible();
18007     },
18008     
18009     getName: function()
18010     {
18011         // returns hidden if it's set..
18012         if (!this.rendered) {return ''};
18013         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18014         
18015     },
18016
18017     /**
18018      * Returns the current date value of the date field.
18019      * @return {Date} The date value
18020      */
18021     getValue : function(){
18022         
18023         return  this.hiddenField ?
18024                 this.hiddenField.value :
18025                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
18026     },
18027
18028     /**
18029      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18030      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
18031      * (the default format used is "m/d/y").
18032      * <br />Usage:
18033      * <pre><code>
18034 //All of these calls set the same date value (May 4, 2006)
18035
18036 //Pass a date object:
18037 var dt = new Date('5/4/06');
18038 dateField.setValue(dt);
18039
18040 //Pass a date string (default format):
18041 dateField.setValue('5/4/06');
18042
18043 //Pass a date string (custom format):
18044 dateField.format = 'Y-m-d';
18045 dateField.setValue('2006-5-4');
18046 </code></pre>
18047      * @param {String/Date} date The date or valid date string
18048      */
18049     setValue : function(date){
18050         if (this.hiddenField) {
18051             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18052         }
18053         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18054         // make sure the value field is always stored as a date..
18055         this.value = this.parseDate(date);
18056         
18057         
18058     },
18059
18060     // private
18061     parseDate : function(value){
18062         if(!value || value instanceof Date){
18063             return value;
18064         }
18065         var v = Date.parseDate(value, this.format);
18066          if (!v && this.useIso) {
18067             v = Date.parseDate(value, 'Y-m-d');
18068         }
18069         if(!v && this.altFormats){
18070             if(!this.altFormatsArray){
18071                 this.altFormatsArray = this.altFormats.split("|");
18072             }
18073             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18074                 v = Date.parseDate(value, this.altFormatsArray[i]);
18075             }
18076         }
18077         return v;
18078     },
18079
18080     // private
18081     formatDate : function(date, fmt){
18082         return (!date || !(date instanceof Date)) ?
18083                date : date.dateFormat(fmt || this.format);
18084     },
18085
18086     // private
18087     menuListeners : {
18088         select: function(m, d){
18089             
18090             this.setValue(d);
18091             this.fireEvent('select', this, d);
18092         },
18093         show : function(){ // retain focus styling
18094             this.onFocus();
18095         },
18096         hide : function(){
18097             this.focus.defer(10, this);
18098             var ml = this.menuListeners;
18099             this.menu.un("select", ml.select,  this);
18100             this.menu.un("show", ml.show,  this);
18101             this.menu.un("hide", ml.hide,  this);
18102         }
18103     },
18104
18105     // private
18106     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18107     onTriggerClick : function(){
18108         if(this.disabled){
18109             return;
18110         }
18111         if(this.menu == null){
18112             this.menu = new Roo.menu.DateMenu();
18113         }
18114         Roo.apply(this.menu.picker,  {
18115             showClear: this.allowBlank,
18116             minDate : this.minValue,
18117             maxDate : this.maxValue,
18118             disabledDatesRE : this.ddMatch,
18119             disabledDatesText : this.disabledDatesText,
18120             disabledDays : this.disabledDays,
18121             disabledDaysText : this.disabledDaysText,
18122             format : this.useIso ? 'Y-m-d' : this.format,
18123             minText : String.format(this.minText, this.formatDate(this.minValue)),
18124             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18125         });
18126         this.menu.on(Roo.apply({}, this.menuListeners, {
18127             scope:this
18128         }));
18129         this.menu.picker.setValue(this.getValue() || new Date());
18130         this.menu.show(this.el, "tl-bl?");
18131     },
18132
18133     beforeBlur : function(){
18134         var v = this.parseDate(this.getRawValue());
18135         if(v){
18136             this.setValue(v);
18137         }
18138     },
18139
18140     /*@
18141      * overide
18142      * 
18143      */
18144     isDirty : function() {
18145         if(this.disabled) {
18146             return false;
18147         }
18148         
18149         if(typeof(this.startValue) === 'undefined'){
18150             return false;
18151         }
18152         
18153         return String(this.getValue()) !== String(this.startValue);
18154         
18155     }
18156 });/*
18157  * Based on:
18158  * Ext JS Library 1.1.1
18159  * Copyright(c) 2006-2007, Ext JS, LLC.
18160  *
18161  * Originally Released Under LGPL - original licence link has changed is not relivant.
18162  *
18163  * Fork - LGPL
18164  * <script type="text/javascript">
18165  */
18166  
18167 /**
18168  * @class Roo.form.MonthField
18169  * @extends Roo.form.TriggerField
18170  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18171 * @constructor
18172 * Create a new MonthField
18173 * @param {Object} config
18174  */
18175 Roo.form.MonthField = function(config){
18176     
18177     Roo.form.MonthField.superclass.constructor.call(this, config);
18178     
18179       this.addEvents({
18180          
18181         /**
18182          * @event select
18183          * Fires when a date is selected
18184              * @param {Roo.form.MonthFieeld} combo This combo box
18185              * @param {Date} date The date selected
18186              */
18187         'select' : true
18188          
18189     });
18190     
18191     
18192     if(typeof this.minValue == "string") {
18193         this.minValue = this.parseDate(this.minValue);
18194     }
18195     if(typeof this.maxValue == "string") {
18196         this.maxValue = this.parseDate(this.maxValue);
18197     }
18198     this.ddMatch = null;
18199     if(this.disabledDates){
18200         var dd = this.disabledDates;
18201         var re = "(?:";
18202         for(var i = 0; i < dd.length; i++){
18203             re += dd[i];
18204             if(i != dd.length-1) {
18205                 re += "|";
18206             }
18207         }
18208         this.ddMatch = new RegExp(re + ")");
18209     }
18210 };
18211
18212 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18213     /**
18214      * @cfg {String} format
18215      * The default date format string which can be overriden for localization support.  The format must be
18216      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18217      */
18218     format : "M Y",
18219     /**
18220      * @cfg {String} altFormats
18221      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18222      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18223      */
18224     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18225     /**
18226      * @cfg {Array} disabledDays
18227      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18228      */
18229     disabledDays : [0,1,2,3,4,5,6],
18230     /**
18231      * @cfg {String} disabledDaysText
18232      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18233      */
18234     disabledDaysText : "Disabled",
18235     /**
18236      * @cfg {Array} disabledDates
18237      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18238      * expression so they are very powerful. Some examples:
18239      * <ul>
18240      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18241      * <li>["03/08", "09/16"] would disable those days for every year</li>
18242      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18243      * <li>["03/../2006"] would disable every day in March 2006</li>
18244      * <li>["^03"] would disable every day in every March</li>
18245      * </ul>
18246      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18247      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18248      */
18249     disabledDates : null,
18250     /**
18251      * @cfg {String} disabledDatesText
18252      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18253      */
18254     disabledDatesText : "Disabled",
18255     /**
18256      * @cfg {Date/String} minValue
18257      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18258      * valid format (defaults to null).
18259      */
18260     minValue : null,
18261     /**
18262      * @cfg {Date/String} maxValue
18263      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18264      * valid format (defaults to null).
18265      */
18266     maxValue : null,
18267     /**
18268      * @cfg {String} minText
18269      * The error text to display when the date in the cell is before minValue (defaults to
18270      * 'The date in this field must be after {minValue}').
18271      */
18272     minText : "The date in this field must be equal to or after {0}",
18273     /**
18274      * @cfg {String} maxTextf
18275      * The error text to display when the date in the cell is after maxValue (defaults to
18276      * 'The date in this field must be before {maxValue}').
18277      */
18278     maxText : "The date in this field must be equal to or before {0}",
18279     /**
18280      * @cfg {String} invalidText
18281      * The error text to display when the date in the field is invalid (defaults to
18282      * '{value} is not a valid date - it must be in the format {format}').
18283      */
18284     invalidText : "{0} is not a valid date - it must be in the format {1}",
18285     /**
18286      * @cfg {String} triggerClass
18287      * An additional CSS class used to style the trigger button.  The trigger will always get the
18288      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18289      * which displays a calendar icon).
18290      */
18291     triggerClass : 'x-form-date-trigger',
18292     
18293
18294     /**
18295      * @cfg {Boolean} useIso
18296      * if enabled, then the date field will use a hidden field to store the 
18297      * real value as iso formated date. default (true)
18298      */ 
18299     useIso : true,
18300     /**
18301      * @cfg {String/Object} autoCreate
18302      * A DomHelper element spec, or true for a default element spec (defaults to
18303      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18304      */ 
18305     // private
18306     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18307     
18308     // private
18309     hiddenField: false,
18310     
18311     hideMonthPicker : false,
18312     
18313     onRender : function(ct, position)
18314     {
18315         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18316         if (this.useIso) {
18317             this.el.dom.removeAttribute('name'); 
18318             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18319                     'before', true);
18320             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18321             // prevent input submission
18322             this.hiddenName = this.name;
18323         }
18324             
18325             
18326     },
18327     
18328     // private
18329     validateValue : function(value)
18330     {
18331         value = this.formatDate(value);
18332         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18333             return false;
18334         }
18335         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18336              return true;
18337         }
18338         var svalue = value;
18339         value = this.parseDate(value);
18340         if(!value){
18341             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18342             return false;
18343         }
18344         var time = value.getTime();
18345         if(this.minValue && time < this.minValue.getTime()){
18346             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18347             return false;
18348         }
18349         if(this.maxValue && time > this.maxValue.getTime()){
18350             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18351             return false;
18352         }
18353         /*if(this.disabledDays){
18354             var day = value.getDay();
18355             for(var i = 0; i < this.disabledDays.length; i++) {
18356                 if(day === this.disabledDays[i]){
18357                     this.markInvalid(this.disabledDaysText);
18358                     return false;
18359                 }
18360             }
18361         }
18362         */
18363         var fvalue = this.formatDate(value);
18364         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18365             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18366             return false;
18367         }
18368         */
18369         return true;
18370     },
18371
18372     // private
18373     // Provides logic to override the default TriggerField.validateBlur which just returns true
18374     validateBlur : function(){
18375         return !this.menu || !this.menu.isVisible();
18376     },
18377
18378     /**
18379      * Returns the current date value of the date field.
18380      * @return {Date} The date value
18381      */
18382     getValue : function(){
18383         
18384         
18385         
18386         return  this.hiddenField ?
18387                 this.hiddenField.value :
18388                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18389     },
18390
18391     /**
18392      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18393      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18394      * (the default format used is "m/d/y").
18395      * <br />Usage:
18396      * <pre><code>
18397 //All of these calls set the same date value (May 4, 2006)
18398
18399 //Pass a date object:
18400 var dt = new Date('5/4/06');
18401 monthField.setValue(dt);
18402
18403 //Pass a date string (default format):
18404 monthField.setValue('5/4/06');
18405
18406 //Pass a date string (custom format):
18407 monthField.format = 'Y-m-d';
18408 monthField.setValue('2006-5-4');
18409 </code></pre>
18410      * @param {String/Date} date The date or valid date string
18411      */
18412     setValue : function(date){
18413         Roo.log('month setValue' + date);
18414         // can only be first of month..
18415         
18416         var val = this.parseDate(date);
18417         
18418         if (this.hiddenField) {
18419             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18420         }
18421         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18422         this.value = this.parseDate(date);
18423     },
18424
18425     // private
18426     parseDate : function(value){
18427         if(!value || value instanceof Date){
18428             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18429             return value;
18430         }
18431         var v = Date.parseDate(value, this.format);
18432         if (!v && this.useIso) {
18433             v = Date.parseDate(value, 'Y-m-d');
18434         }
18435         if (v) {
18436             // 
18437             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18438         }
18439         
18440         
18441         if(!v && this.altFormats){
18442             if(!this.altFormatsArray){
18443                 this.altFormatsArray = this.altFormats.split("|");
18444             }
18445             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18446                 v = Date.parseDate(value, this.altFormatsArray[i]);
18447             }
18448         }
18449         return v;
18450     },
18451
18452     // private
18453     formatDate : function(date, fmt){
18454         return (!date || !(date instanceof Date)) ?
18455                date : date.dateFormat(fmt || this.format);
18456     },
18457
18458     // private
18459     menuListeners : {
18460         select: function(m, d){
18461             this.setValue(d);
18462             this.fireEvent('select', this, d);
18463         },
18464         show : function(){ // retain focus styling
18465             this.onFocus();
18466         },
18467         hide : function(){
18468             this.focus.defer(10, this);
18469             var ml = this.menuListeners;
18470             this.menu.un("select", ml.select,  this);
18471             this.menu.un("show", ml.show,  this);
18472             this.menu.un("hide", ml.hide,  this);
18473         }
18474     },
18475     // private
18476     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18477     onTriggerClick : function(){
18478         if(this.disabled){
18479             return;
18480         }
18481         if(this.menu == null){
18482             this.menu = new Roo.menu.DateMenu();
18483            
18484         }
18485         
18486         Roo.apply(this.menu.picker,  {
18487             
18488             showClear: this.allowBlank,
18489             minDate : this.minValue,
18490             maxDate : this.maxValue,
18491             disabledDatesRE : this.ddMatch,
18492             disabledDatesText : this.disabledDatesText,
18493             
18494             format : this.useIso ? 'Y-m-d' : this.format,
18495             minText : String.format(this.minText, this.formatDate(this.minValue)),
18496             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18497             
18498         });
18499          this.menu.on(Roo.apply({}, this.menuListeners, {
18500             scope:this
18501         }));
18502        
18503         
18504         var m = this.menu;
18505         var p = m.picker;
18506         
18507         // hide month picker get's called when we called by 'before hide';
18508         
18509         var ignorehide = true;
18510         p.hideMonthPicker  = function(disableAnim){
18511             if (ignorehide) {
18512                 return;
18513             }
18514              if(this.monthPicker){
18515                 Roo.log("hideMonthPicker called");
18516                 if(disableAnim === true){
18517                     this.monthPicker.hide();
18518                 }else{
18519                     this.monthPicker.slideOut('t', {duration:.2});
18520                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18521                     p.fireEvent("select", this, this.value);
18522                     m.hide();
18523                 }
18524             }
18525         }
18526         
18527         Roo.log('picker set value');
18528         Roo.log(this.getValue());
18529         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18530         m.show(this.el, 'tl-bl?');
18531         ignorehide  = false;
18532         // this will trigger hideMonthPicker..
18533         
18534         
18535         // hidden the day picker
18536         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18537         
18538         
18539         
18540       
18541         
18542         p.showMonthPicker.defer(100, p);
18543     
18544         
18545        
18546     },
18547
18548     beforeBlur : function(){
18549         var v = this.parseDate(this.getRawValue());
18550         if(v){
18551             this.setValue(v);
18552         }
18553     }
18554
18555     /** @cfg {Boolean} grow @hide */
18556     /** @cfg {Number} growMin @hide */
18557     /** @cfg {Number} growMax @hide */
18558     /**
18559      * @hide
18560      * @method autoSize
18561      */
18562 });/*
18563  * Based on:
18564  * Ext JS Library 1.1.1
18565  * Copyright(c) 2006-2007, Ext JS, LLC.
18566  *
18567  * Originally Released Under LGPL - original licence link has changed is not relivant.
18568  *
18569  * Fork - LGPL
18570  * <script type="text/javascript">
18571  */
18572  
18573
18574 /**
18575  * @class Roo.form.ComboBox
18576  * @extends Roo.form.TriggerField
18577  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18578  * @constructor
18579  * Create a new ComboBox.
18580  * @param {Object} config Configuration options
18581  */
18582 Roo.form.ComboBox = function(config){
18583     Roo.form.ComboBox.superclass.constructor.call(this, config);
18584     this.addEvents({
18585         /**
18586          * @event expand
18587          * Fires when the dropdown list is expanded
18588              * @param {Roo.form.ComboBox} combo This combo box
18589              */
18590         'expand' : true,
18591         /**
18592          * @event collapse
18593          * Fires when the dropdown list is collapsed
18594              * @param {Roo.form.ComboBox} combo This combo box
18595              */
18596         'collapse' : true,
18597         /**
18598          * @event beforeselect
18599          * Fires before a list item is selected. Return false to cancel the selection.
18600              * @param {Roo.form.ComboBox} combo This combo box
18601              * @param {Roo.data.Record} record The data record returned from the underlying store
18602              * @param {Number} index The index of the selected item in the dropdown list
18603              */
18604         'beforeselect' : true,
18605         /**
18606          * @event select
18607          * Fires when a list item is selected
18608              * @param {Roo.form.ComboBox} combo This combo box
18609              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18610              * @param {Number} index The index of the selected item in the dropdown list
18611              */
18612         'select' : true,
18613         /**
18614          * @event beforequery
18615          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18616          * The event object passed has these properties:
18617              * @param {Roo.form.ComboBox} combo This combo box
18618              * @param {String} query The query
18619              * @param {Boolean} forceAll true to force "all" query
18620              * @param {Boolean} cancel true to cancel the query
18621              * @param {Object} e The query event object
18622              */
18623         'beforequery': true,
18624          /**
18625          * @event add
18626          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18627              * @param {Roo.form.ComboBox} combo This combo box
18628              */
18629         'add' : true,
18630         /**
18631          * @event edit
18632          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18633              * @param {Roo.form.ComboBox} combo This combo box
18634              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18635              */
18636         'edit' : true
18637         
18638         
18639     });
18640     if(this.transform){
18641         this.allowDomMove = false;
18642         var s = Roo.getDom(this.transform);
18643         if(!this.hiddenName){
18644             this.hiddenName = s.name;
18645         }
18646         if(!this.store){
18647             this.mode = 'local';
18648             var d = [], opts = s.options;
18649             for(var i = 0, len = opts.length;i < len; i++){
18650                 var o = opts[i];
18651                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18652                 if(o.selected) {
18653                     this.value = value;
18654                 }
18655                 d.push([value, o.text]);
18656             }
18657             this.store = new Roo.data.SimpleStore({
18658                 'id': 0,
18659                 fields: ['value', 'text'],
18660                 data : d
18661             });
18662             this.valueField = 'value';
18663             this.displayField = 'text';
18664         }
18665         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18666         if(!this.lazyRender){
18667             this.target = true;
18668             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18669             s.parentNode.removeChild(s); // remove it
18670             this.render(this.el.parentNode);
18671         }else{
18672             s.parentNode.removeChild(s); // remove it
18673         }
18674
18675     }
18676     if (this.store) {
18677         this.store = Roo.factory(this.store, Roo.data);
18678     }
18679     
18680     this.selectedIndex = -1;
18681     if(this.mode == 'local'){
18682         if(config.queryDelay === undefined){
18683             this.queryDelay = 10;
18684         }
18685         if(config.minChars === undefined){
18686             this.minChars = 0;
18687         }
18688     }
18689 };
18690
18691 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18692     /**
18693      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18694      */
18695     /**
18696      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18697      * rendering into an Roo.Editor, defaults to false)
18698      */
18699     /**
18700      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18701      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18702      */
18703     /**
18704      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18705      */
18706     /**
18707      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18708      * the dropdown list (defaults to undefined, with no header element)
18709      */
18710
18711      /**
18712      * @cfg {String/Roo.Template} tpl The template to use to render the output
18713      */
18714      
18715     // private
18716     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18717     /**
18718      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18719      */
18720     listWidth: undefined,
18721     /**
18722      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18723      * mode = 'remote' or 'text' if mode = 'local')
18724      */
18725     displayField: undefined,
18726     /**
18727      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18728      * mode = 'remote' or 'value' if mode = 'local'). 
18729      * Note: use of a valueField requires the user make a selection
18730      * in order for a value to be mapped.
18731      */
18732     valueField: undefined,
18733     
18734     
18735     /**
18736      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18737      * field's data value (defaults to the underlying DOM element's name)
18738      */
18739     hiddenName: undefined,
18740     /**
18741      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18742      */
18743     listClass: '',
18744     /**
18745      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18746      */
18747     selectedClass: 'x-combo-selected',
18748     /**
18749      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18750      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18751      * which displays a downward arrow icon).
18752      */
18753     triggerClass : 'x-form-arrow-trigger',
18754     /**
18755      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18756      */
18757     shadow:'sides',
18758     /**
18759      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18760      * anchor positions (defaults to 'tl-bl')
18761      */
18762     listAlign: 'tl-bl?',
18763     /**
18764      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18765      */
18766     maxHeight: 300,
18767     /**
18768      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18769      * query specified by the allQuery config option (defaults to 'query')
18770      */
18771     triggerAction: 'query',
18772     /**
18773      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18774      * (defaults to 4, does not apply if editable = false)
18775      */
18776     minChars : 4,
18777     /**
18778      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18779      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18780      */
18781     typeAhead: false,
18782     /**
18783      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18784      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18785      */
18786     queryDelay: 500,
18787     /**
18788      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18789      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18790      */
18791     pageSize: 0,
18792     /**
18793      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18794      * when editable = true (defaults to false)
18795      */
18796     selectOnFocus:false,
18797     /**
18798      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18799      */
18800     queryParam: 'query',
18801     /**
18802      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18803      * when mode = 'remote' (defaults to 'Loading...')
18804      */
18805     loadingText: 'Loading...',
18806     /**
18807      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18808      */
18809     resizable: false,
18810     /**
18811      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18812      */
18813     handleHeight : 8,
18814     /**
18815      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18816      * traditional select (defaults to true)
18817      */
18818     editable: true,
18819     /**
18820      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18821      */
18822     allQuery: '',
18823     /**
18824      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18825      */
18826     mode: 'remote',
18827     /**
18828      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18829      * listWidth has a higher value)
18830      */
18831     minListWidth : 70,
18832     /**
18833      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18834      * allow the user to set arbitrary text into the field (defaults to false)
18835      */
18836     forceSelection:false,
18837     /**
18838      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18839      * if typeAhead = true (defaults to 250)
18840      */
18841     typeAheadDelay : 250,
18842     /**
18843      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18844      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18845      */
18846     valueNotFoundText : undefined,
18847     /**
18848      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18849      */
18850     blockFocus : false,
18851     
18852     /**
18853      * @cfg {Boolean} disableClear Disable showing of clear button.
18854      */
18855     disableClear : false,
18856     /**
18857      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18858      */
18859     alwaysQuery : false,
18860     
18861     //private
18862     addicon : false,
18863     editicon: false,
18864     
18865     // element that contains real text value.. (when hidden is used..)
18866      
18867     // private
18868     onRender : function(ct, position){
18869         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18870         if(this.hiddenName){
18871             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18872                     'before', true);
18873             this.hiddenField.value =
18874                 this.hiddenValue !== undefined ? this.hiddenValue :
18875                 this.value !== undefined ? this.value : '';
18876
18877             // prevent input submission
18878             this.el.dom.removeAttribute('name');
18879              
18880              
18881         }
18882         if(Roo.isGecko){
18883             this.el.dom.setAttribute('autocomplete', 'off');
18884         }
18885
18886         var cls = 'x-combo-list';
18887
18888         this.list = new Roo.Layer({
18889             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18890         });
18891
18892         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18893         this.list.setWidth(lw);
18894         this.list.swallowEvent('mousewheel');
18895         this.assetHeight = 0;
18896
18897         if(this.title){
18898             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18899             this.assetHeight += this.header.getHeight();
18900         }
18901
18902         this.innerList = this.list.createChild({cls:cls+'-inner'});
18903         this.innerList.on('mouseover', this.onViewOver, this);
18904         this.innerList.on('mousemove', this.onViewMove, this);
18905         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18906         
18907         if(this.allowBlank && !this.pageSize && !this.disableClear){
18908             this.footer = this.list.createChild({cls:cls+'-ft'});
18909             this.pageTb = new Roo.Toolbar(this.footer);
18910            
18911         }
18912         if(this.pageSize){
18913             this.footer = this.list.createChild({cls:cls+'-ft'});
18914             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18915                     {pageSize: this.pageSize});
18916             
18917         }
18918         
18919         if (this.pageTb && this.allowBlank && !this.disableClear) {
18920             var _this = this;
18921             this.pageTb.add(new Roo.Toolbar.Fill(), {
18922                 cls: 'x-btn-icon x-btn-clear',
18923                 text: '&#160;',
18924                 handler: function()
18925                 {
18926                     _this.collapse();
18927                     _this.clearValue();
18928                     _this.onSelect(false, -1);
18929                 }
18930             });
18931         }
18932         if (this.footer) {
18933             this.assetHeight += this.footer.getHeight();
18934         }
18935         
18936
18937         if(!this.tpl){
18938             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18939         }
18940
18941         this.view = new Roo.View(this.innerList, this.tpl, {
18942             singleSelect:true, store: this.store, selectedClass: this.selectedClass
18943         });
18944
18945         this.view.on('click', this.onViewClick, this);
18946
18947         this.store.on('beforeload', this.onBeforeLoad, this);
18948         this.store.on('load', this.onLoad, this);
18949         this.store.on('loadexception', this.onLoadException, this);
18950
18951         if(this.resizable){
18952             this.resizer = new Roo.Resizable(this.list,  {
18953                pinned:true, handles:'se'
18954             });
18955             this.resizer.on('resize', function(r, w, h){
18956                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18957                 this.listWidth = w;
18958                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18959                 this.restrictHeight();
18960             }, this);
18961             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18962         }
18963         if(!this.editable){
18964             this.editable = true;
18965             this.setEditable(false);
18966         }  
18967         
18968         
18969         if (typeof(this.events.add.listeners) != 'undefined') {
18970             
18971             this.addicon = this.wrap.createChild(
18972                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18973        
18974             this.addicon.on('click', function(e) {
18975                 this.fireEvent('add', this);
18976             }, this);
18977         }
18978         if (typeof(this.events.edit.listeners) != 'undefined') {
18979             
18980             this.editicon = this.wrap.createChild(
18981                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18982             if (this.addicon) {
18983                 this.editicon.setStyle('margin-left', '40px');
18984             }
18985             this.editicon.on('click', function(e) {
18986                 
18987                 // we fire even  if inothing is selected..
18988                 this.fireEvent('edit', this, this.lastData );
18989                 
18990             }, this);
18991         }
18992         
18993         
18994         
18995     },
18996
18997     // private
18998     initEvents : function(){
18999         Roo.form.ComboBox.superclass.initEvents.call(this);
19000
19001         this.keyNav = new Roo.KeyNav(this.el, {
19002             "up" : function(e){
19003                 this.inKeyMode = true;
19004                 this.selectPrev();
19005             },
19006
19007             "down" : function(e){
19008                 if(!this.isExpanded()){
19009                     this.onTriggerClick();
19010                 }else{
19011                     this.inKeyMode = true;
19012                     this.selectNext();
19013                 }
19014             },
19015
19016             "enter" : function(e){
19017                 this.onViewClick();
19018                 //return true;
19019             },
19020
19021             "esc" : function(e){
19022                 this.collapse();
19023             },
19024
19025             "tab" : function(e){
19026                 this.onViewClick(false);
19027                 this.fireEvent("specialkey", this, e);
19028                 return true;
19029             },
19030
19031             scope : this,
19032
19033             doRelay : function(foo, bar, hname){
19034                 if(hname == 'down' || this.scope.isExpanded()){
19035                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
19036                 }
19037                 return true;
19038             },
19039
19040             forceKeyDown: true
19041         });
19042         this.queryDelay = Math.max(this.queryDelay || 10,
19043                 this.mode == 'local' ? 10 : 250);
19044         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
19045         if(this.typeAhead){
19046             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
19047         }
19048         if(this.editable !== false){
19049             this.el.on("keyup", this.onKeyUp, this);
19050         }
19051         if(this.forceSelection){
19052             this.on('blur', this.doForce, this);
19053         }
19054     },
19055
19056     onDestroy : function(){
19057         if(this.view){
19058             this.view.setStore(null);
19059             this.view.el.removeAllListeners();
19060             this.view.el.remove();
19061             this.view.purgeListeners();
19062         }
19063         if(this.list){
19064             this.list.destroy();
19065         }
19066         if(this.store){
19067             this.store.un('beforeload', this.onBeforeLoad, this);
19068             this.store.un('load', this.onLoad, this);
19069             this.store.un('loadexception', this.onLoadException, this);
19070         }
19071         Roo.form.ComboBox.superclass.onDestroy.call(this);
19072     },
19073
19074     // private
19075     fireKey : function(e){
19076         if(e.isNavKeyPress() && !this.list.isVisible()){
19077             this.fireEvent("specialkey", this, e);
19078         }
19079     },
19080
19081     // private
19082     onResize: function(w, h){
19083         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19084         
19085         if(typeof w != 'number'){
19086             // we do not handle it!?!?
19087             return;
19088         }
19089         var tw = this.trigger.getWidth();
19090         tw += this.addicon ? this.addicon.getWidth() : 0;
19091         tw += this.editicon ? this.editicon.getWidth() : 0;
19092         var x = w - tw;
19093         this.el.setWidth( this.adjustWidth('input', x));
19094             
19095         this.trigger.setStyle('left', x+'px');
19096         
19097         if(this.list && this.listWidth === undefined){
19098             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19099             this.list.setWidth(lw);
19100             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19101         }
19102         
19103     
19104         
19105     },
19106
19107     /**
19108      * Allow or prevent the user from directly editing the field text.  If false is passed,
19109      * the user will only be able to select from the items defined in the dropdown list.  This method
19110      * is the runtime equivalent of setting the 'editable' config option at config time.
19111      * @param {Boolean} value True to allow the user to directly edit the field text
19112      */
19113     setEditable : function(value){
19114         if(value == this.editable){
19115             return;
19116         }
19117         this.editable = value;
19118         if(!value){
19119             this.el.dom.setAttribute('readOnly', true);
19120             this.el.on('mousedown', this.onTriggerClick,  this);
19121             this.el.addClass('x-combo-noedit');
19122         }else{
19123             this.el.dom.setAttribute('readOnly', false);
19124             this.el.un('mousedown', this.onTriggerClick,  this);
19125             this.el.removeClass('x-combo-noedit');
19126         }
19127     },
19128
19129     // private
19130     onBeforeLoad : function(){
19131         if(!this.hasFocus){
19132             return;
19133         }
19134         this.innerList.update(this.loadingText ?
19135                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19136         this.restrictHeight();
19137         this.selectedIndex = -1;
19138     },
19139
19140     // private
19141     onLoad : function(){
19142         if(!this.hasFocus){
19143             return;
19144         }
19145         if(this.store.getCount() > 0){
19146             this.expand();
19147             this.restrictHeight();
19148             if(this.lastQuery == this.allQuery){
19149                 if(this.editable){
19150                     this.el.dom.select();
19151                 }
19152                 if(!this.selectByValue(this.value, true)){
19153                     this.select(0, true);
19154                 }
19155             }else{
19156                 this.selectNext();
19157                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19158                     this.taTask.delay(this.typeAheadDelay);
19159                 }
19160             }
19161         }else{
19162             this.onEmptyResults();
19163         }
19164         //this.el.focus();
19165     },
19166     // private
19167     onLoadException : function()
19168     {
19169         this.collapse();
19170         Roo.log(this.store.reader.jsonData);
19171         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19172             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19173         }
19174         
19175         
19176     },
19177     // private
19178     onTypeAhead : function(){
19179         if(this.store.getCount() > 0){
19180             var r = this.store.getAt(0);
19181             var newValue = r.data[this.displayField];
19182             var len = newValue.length;
19183             var selStart = this.getRawValue().length;
19184             if(selStart != len){
19185                 this.setRawValue(newValue);
19186                 this.selectText(selStart, newValue.length);
19187             }
19188         }
19189     },
19190
19191     // private
19192     onSelect : function(record, index){
19193         if(this.fireEvent('beforeselect', this, record, index) !== false){
19194             this.setFromData(index > -1 ? record.data : false);
19195             this.collapse();
19196             this.fireEvent('select', this, record, index);
19197         }
19198     },
19199
19200     /**
19201      * Returns the currently selected field value or empty string if no value is set.
19202      * @return {String} value The selected value
19203      */
19204     getValue : function(){
19205         if(this.valueField){
19206             return typeof this.value != 'undefined' ? this.value : '';
19207         }
19208         return Roo.form.ComboBox.superclass.getValue.call(this);
19209     },
19210
19211     /**
19212      * Clears any text/value currently set in the field
19213      */
19214     clearValue : function(){
19215         if(this.hiddenField){
19216             this.hiddenField.value = '';
19217         }
19218         this.value = '';
19219         this.setRawValue('');
19220         this.lastSelectionText = '';
19221         
19222     },
19223
19224     /**
19225      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19226      * will be displayed in the field.  If the value does not match the data value of an existing item,
19227      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19228      * Otherwise the field will be blank (although the value will still be set).
19229      * @param {String} value The value to match
19230      */
19231     setValue : function(v){
19232         var text = v;
19233         if(this.valueField){
19234             var r = this.findRecord(this.valueField, v);
19235             if(r){
19236                 text = r.data[this.displayField];
19237             }else if(this.valueNotFoundText !== undefined){
19238                 text = this.valueNotFoundText;
19239             }
19240         }
19241         this.lastSelectionText = text;
19242         if(this.hiddenField){
19243             this.hiddenField.value = v;
19244         }
19245         Roo.form.ComboBox.superclass.setValue.call(this, text);
19246         this.value = v;
19247     },
19248     /**
19249      * @property {Object} the last set data for the element
19250      */
19251     
19252     lastData : false,
19253     /**
19254      * Sets the value of the field based on a object which is related to the record format for the store.
19255      * @param {Object} value the value to set as. or false on reset?
19256      */
19257     setFromData : function(o){
19258         var dv = ''; // display value
19259         var vv = ''; // value value..
19260         this.lastData = o;
19261         if (this.displayField) {
19262             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19263         } else {
19264             // this is an error condition!!!
19265             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19266         }
19267         
19268         if(this.valueField){
19269             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19270         }
19271         if(this.hiddenField){
19272             this.hiddenField.value = vv;
19273             
19274             this.lastSelectionText = dv;
19275             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19276             this.value = vv;
19277             return;
19278         }
19279         // no hidden field.. - we store the value in 'value', but still display
19280         // display field!!!!
19281         this.lastSelectionText = dv;
19282         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19283         this.value = vv;
19284         
19285         
19286     },
19287     // private
19288     reset : function(){
19289         // overridden so that last data is reset..
19290         this.setValue(this.resetValue);
19291         this.originalValue = this.getValue();
19292         this.clearInvalid();
19293         this.lastData = false;
19294         if (this.view) {
19295             this.view.clearSelections();
19296         }
19297     },
19298     // private
19299     findRecord : function(prop, value){
19300         var record;
19301         if(this.store.getCount() > 0){
19302             this.store.each(function(r){
19303                 if(r.data[prop] == value){
19304                     record = r;
19305                     return false;
19306                 }
19307                 return true;
19308             });
19309         }
19310         return record;
19311     },
19312     
19313     getName: function()
19314     {
19315         // returns hidden if it's set..
19316         if (!this.rendered) {return ''};
19317         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19318         
19319     },
19320     // private
19321     onViewMove : function(e, t){
19322         this.inKeyMode = false;
19323     },
19324
19325     // private
19326     onViewOver : function(e, t){
19327         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19328             return;
19329         }
19330         var item = this.view.findItemFromChild(t);
19331         if(item){
19332             var index = this.view.indexOf(item);
19333             this.select(index, false);
19334         }
19335     },
19336
19337     // private
19338     onViewClick : function(doFocus)
19339     {
19340         var index = this.view.getSelectedIndexes()[0];
19341         var r = this.store.getAt(index);
19342         if(r){
19343             this.onSelect(r, index);
19344         }
19345         if(doFocus !== false && !this.blockFocus){
19346             this.el.focus();
19347         }
19348     },
19349
19350     // private
19351     restrictHeight : function(){
19352         this.innerList.dom.style.height = '';
19353         var inner = this.innerList.dom;
19354         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19355         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19356         this.list.beginUpdate();
19357         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19358         this.list.alignTo(this.el, this.listAlign);
19359         this.list.endUpdate();
19360     },
19361
19362     // private
19363     onEmptyResults : function(){
19364         this.collapse();
19365     },
19366
19367     /**
19368      * Returns true if the dropdown list is expanded, else false.
19369      */
19370     isExpanded : function(){
19371         return this.list.isVisible();
19372     },
19373
19374     /**
19375      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19376      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19377      * @param {String} value The data value of the item to select
19378      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19379      * selected item if it is not currently in view (defaults to true)
19380      * @return {Boolean} True if the value matched an item in the list, else false
19381      */
19382     selectByValue : function(v, scrollIntoView){
19383         if(v !== undefined && v !== null){
19384             var r = this.findRecord(this.valueField || this.displayField, v);
19385             if(r){
19386                 this.select(this.store.indexOf(r), scrollIntoView);
19387                 return true;
19388             }
19389         }
19390         return false;
19391     },
19392
19393     /**
19394      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19395      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19396      * @param {Number} index The zero-based index of the list item to select
19397      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19398      * selected item if it is not currently in view (defaults to true)
19399      */
19400     select : function(index, scrollIntoView){
19401         this.selectedIndex = index;
19402         this.view.select(index);
19403         if(scrollIntoView !== false){
19404             var el = this.view.getNode(index);
19405             if(el){
19406                 this.innerList.scrollChildIntoView(el, false);
19407             }
19408         }
19409     },
19410
19411     // private
19412     selectNext : function(){
19413         var ct = this.store.getCount();
19414         if(ct > 0){
19415             if(this.selectedIndex == -1){
19416                 this.select(0);
19417             }else if(this.selectedIndex < ct-1){
19418                 this.select(this.selectedIndex+1);
19419             }
19420         }
19421     },
19422
19423     // private
19424     selectPrev : function(){
19425         var ct = this.store.getCount();
19426         if(ct > 0){
19427             if(this.selectedIndex == -1){
19428                 this.select(0);
19429             }else if(this.selectedIndex != 0){
19430                 this.select(this.selectedIndex-1);
19431             }
19432         }
19433     },
19434
19435     // private
19436     onKeyUp : function(e){
19437         if(this.editable !== false && !e.isSpecialKey()){
19438             this.lastKey = e.getKey();
19439             this.dqTask.delay(this.queryDelay);
19440         }
19441     },
19442
19443     // private
19444     validateBlur : function(){
19445         return !this.list || !this.list.isVisible();   
19446     },
19447
19448     // private
19449     initQuery : function(){
19450         this.doQuery(this.getRawValue());
19451     },
19452
19453     // private
19454     doForce : function(){
19455         if(this.el.dom.value.length > 0){
19456             this.el.dom.value =
19457                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19458              
19459         }
19460     },
19461
19462     /**
19463      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19464      * query allowing the query action to be canceled if needed.
19465      * @param {String} query The SQL query to execute
19466      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19467      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19468      * saved in the current store (defaults to false)
19469      */
19470     doQuery : function(q, forceAll){
19471         if(q === undefined || q === null){
19472             q = '';
19473         }
19474         var qe = {
19475             query: q,
19476             forceAll: forceAll,
19477             combo: this,
19478             cancel:false
19479         };
19480         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19481             return false;
19482         }
19483         q = qe.query;
19484         forceAll = qe.forceAll;
19485         if(forceAll === true || (q.length >= this.minChars)){
19486             if(this.lastQuery != q || this.alwaysQuery){
19487                 this.lastQuery = q;
19488                 if(this.mode == 'local'){
19489                     this.selectedIndex = -1;
19490                     if(forceAll){
19491                         this.store.clearFilter();
19492                     }else{
19493                         this.store.filter(this.displayField, q);
19494                     }
19495                     this.onLoad();
19496                 }else{
19497                     this.store.baseParams[this.queryParam] = q;
19498                     this.store.load({
19499                         params: this.getParams(q)
19500                     });
19501                     this.expand();
19502                 }
19503             }else{
19504                 this.selectedIndex = -1;
19505                 this.onLoad();   
19506             }
19507         }
19508     },
19509
19510     // private
19511     getParams : function(q){
19512         var p = {};
19513         //p[this.queryParam] = q;
19514         if(this.pageSize){
19515             p.start = 0;
19516             p.limit = this.pageSize;
19517         }
19518         return p;
19519     },
19520
19521     /**
19522      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19523      */
19524     collapse : function(){
19525         if(!this.isExpanded()){
19526             return;
19527         }
19528         this.list.hide();
19529         Roo.get(document).un('mousedown', this.collapseIf, this);
19530         Roo.get(document).un('mousewheel', this.collapseIf, this);
19531         if (!this.editable) {
19532             Roo.get(document).un('keydown', this.listKeyPress, this);
19533         }
19534         this.fireEvent('collapse', this);
19535     },
19536
19537     // private
19538     collapseIf : function(e){
19539         if(!e.within(this.wrap) && !e.within(this.list)){
19540             this.collapse();
19541         }
19542     },
19543
19544     /**
19545      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19546      */
19547     expand : function(){
19548         if(this.isExpanded() || !this.hasFocus){
19549             return;
19550         }
19551         this.list.alignTo(this.el, this.listAlign);
19552         this.list.show();
19553         Roo.get(document).on('mousedown', this.collapseIf, this);
19554         Roo.get(document).on('mousewheel', this.collapseIf, this);
19555         if (!this.editable) {
19556             Roo.get(document).on('keydown', this.listKeyPress, this);
19557         }
19558         
19559         this.fireEvent('expand', this);
19560     },
19561
19562     // private
19563     // Implements the default empty TriggerField.onTriggerClick function
19564     onTriggerClick : function(){
19565         if(this.disabled){
19566             return;
19567         }
19568         if(this.isExpanded()){
19569             this.collapse();
19570             if (!this.blockFocus) {
19571                 this.el.focus();
19572             }
19573             
19574         }else {
19575             this.hasFocus = true;
19576             if(this.triggerAction == 'all') {
19577                 this.doQuery(this.allQuery, true);
19578             } else {
19579                 this.doQuery(this.getRawValue());
19580             }
19581             if (!this.blockFocus) {
19582                 this.el.focus();
19583             }
19584         }
19585     },
19586     listKeyPress : function(e)
19587     {
19588         //Roo.log('listkeypress');
19589         // scroll to first matching element based on key pres..
19590         if (e.isSpecialKey()) {
19591             return false;
19592         }
19593         var k = String.fromCharCode(e.getKey()).toUpperCase();
19594         //Roo.log(k);
19595         var match  = false;
19596         var csel = this.view.getSelectedNodes();
19597         var cselitem = false;
19598         if (csel.length) {
19599             var ix = this.view.indexOf(csel[0]);
19600             cselitem  = this.store.getAt(ix);
19601             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19602                 cselitem = false;
19603             }
19604             
19605         }
19606         
19607         this.store.each(function(v) { 
19608             if (cselitem) {
19609                 // start at existing selection.
19610                 if (cselitem.id == v.id) {
19611                     cselitem = false;
19612                 }
19613                 return;
19614             }
19615                 
19616             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19617                 match = this.store.indexOf(v);
19618                 return false;
19619             }
19620         }, this);
19621         
19622         if (match === false) {
19623             return true; // no more action?
19624         }
19625         // scroll to?
19626         this.view.select(match);
19627         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19628         sn.scrollIntoView(sn.dom.parentNode, false);
19629     }
19630
19631     /** 
19632     * @cfg {Boolean} grow 
19633     * @hide 
19634     */
19635     /** 
19636     * @cfg {Number} growMin 
19637     * @hide 
19638     */
19639     /** 
19640     * @cfg {Number} growMax 
19641     * @hide 
19642     */
19643     /**
19644      * @hide
19645      * @method autoSize
19646      */
19647 });/*
19648  * Copyright(c) 2010-2012, Roo J Solutions Limited
19649  *
19650  * Licence LGPL
19651  *
19652  */
19653
19654 /**
19655  * @class Roo.form.ComboBoxArray
19656  * @extends Roo.form.TextField
19657  * A facebook style adder... for lists of email / people / countries  etc...
19658  * pick multiple items from a combo box, and shows each one.
19659  *
19660  *  Fred [x]  Brian [x]  [Pick another |v]
19661  *
19662  *
19663  *  For this to work: it needs various extra information
19664  *    - normal combo problay has
19665  *      name, hiddenName
19666  *    + displayField, valueField
19667  *
19668  *    For our purpose...
19669  *
19670  *
19671  *   If we change from 'extends' to wrapping...
19672  *   
19673  *  
19674  *
19675  
19676  
19677  * @constructor
19678  * Create a new ComboBoxArray.
19679  * @param {Object} config Configuration options
19680  */
19681  
19682
19683 Roo.form.ComboBoxArray = function(config)
19684 {
19685     this.addEvents({
19686         /**
19687          * @event beforeremove
19688          * Fires before remove the value from the list
19689              * @param {Roo.form.ComboBoxArray} _self This combo box array
19690              * @param {Roo.form.ComboBoxArray.Item} item removed item
19691              */
19692         'beforeremove' : true,
19693         /**
19694          * @event remove
19695          * Fires when remove the value from the list
19696              * @param {Roo.form.ComboBoxArray} _self This combo box array
19697              * @param {Roo.form.ComboBoxArray.Item} item removed item
19698              */
19699         'remove' : true
19700         
19701         
19702     });
19703     
19704     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19705     
19706     this.items = new Roo.util.MixedCollection(false);
19707     
19708     // construct the child combo...
19709     
19710     
19711     
19712     
19713    
19714     
19715 }
19716
19717  
19718 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19719
19720     /**
19721      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19722      */
19723     
19724     lastData : false,
19725     
19726     // behavies liek a hiddne field
19727     inputType:      'hidden',
19728     /**
19729      * @cfg {Number} width The width of the box that displays the selected element
19730      */ 
19731     width:          300,
19732
19733     
19734     
19735     /**
19736      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19737      */
19738     name : false,
19739     /**
19740      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19741      */
19742     hiddenName : false,
19743     
19744     
19745     // private the array of items that are displayed..
19746     items  : false,
19747     // private - the hidden field el.
19748     hiddenEl : false,
19749     // private - the filed el..
19750     el : false,
19751     
19752     //validateValue : function() { return true; }, // all values are ok!
19753     //onAddClick: function() { },
19754     
19755     onRender : function(ct, position) 
19756     {
19757         
19758         // create the standard hidden element
19759         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19760         
19761         
19762         // give fake names to child combo;
19763         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19764         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
19765         
19766         this.combo = Roo.factory(this.combo, Roo.form);
19767         this.combo.onRender(ct, position);
19768         if (typeof(this.combo.width) != 'undefined') {
19769             this.combo.onResize(this.combo.width,0);
19770         }
19771         
19772         this.combo.initEvents();
19773         
19774         // assigned so form know we need to do this..
19775         this.store          = this.combo.store;
19776         this.valueField     = this.combo.valueField;
19777         this.displayField   = this.combo.displayField ;
19778         
19779         
19780         this.combo.wrap.addClass('x-cbarray-grp');
19781         
19782         var cbwrap = this.combo.wrap.createChild(
19783             {tag: 'div', cls: 'x-cbarray-cb'},
19784             this.combo.el.dom
19785         );
19786         
19787              
19788         this.hiddenEl = this.combo.wrap.createChild({
19789             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19790         });
19791         this.el = this.combo.wrap.createChild({
19792             tag: 'input',  type:'hidden' , name: this.name, value : ''
19793         });
19794          //   this.el.dom.removeAttribute("name");
19795         
19796         
19797         this.outerWrap = this.combo.wrap;
19798         this.wrap = cbwrap;
19799         
19800         this.outerWrap.setWidth(this.width);
19801         this.outerWrap.dom.removeChild(this.el.dom);
19802         
19803         this.wrap.dom.appendChild(this.el.dom);
19804         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19805         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19806         
19807         this.combo.trigger.setStyle('position','relative');
19808         this.combo.trigger.setStyle('left', '0px');
19809         this.combo.trigger.setStyle('top', '2px');
19810         
19811         this.combo.el.setStyle('vertical-align', 'text-bottom');
19812         
19813         //this.trigger.setStyle('vertical-align', 'top');
19814         
19815         // this should use the code from combo really... on('add' ....)
19816         if (this.adder) {
19817             
19818         
19819             this.adder = this.outerWrap.createChild(
19820                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19821             var _t = this;
19822             this.adder.on('click', function(e) {
19823                 _t.fireEvent('adderclick', this, e);
19824             }, _t);
19825         }
19826         //var _t = this;
19827         //this.adder.on('click', this.onAddClick, _t);
19828         
19829         
19830         this.combo.on('select', function(cb, rec, ix) {
19831             this.addItem(rec.data);
19832             
19833             cb.setValue('');
19834             cb.el.dom.value = '';
19835             //cb.lastData = rec.data;
19836             // add to list
19837             
19838         }, this);
19839         
19840         
19841     },
19842     
19843     
19844     getName: function()
19845     {
19846         // returns hidden if it's set..
19847         if (!this.rendered) {return ''};
19848         return  this.hiddenName ? this.hiddenName : this.name;
19849         
19850     },
19851     
19852     
19853     onResize: function(w, h){
19854         
19855         return;
19856         // not sure if this is needed..
19857         //this.combo.onResize(w,h);
19858         
19859         if(typeof w != 'number'){
19860             // we do not handle it!?!?
19861             return;
19862         }
19863         var tw = this.combo.trigger.getWidth();
19864         tw += this.addicon ? this.addicon.getWidth() : 0;
19865         tw += this.editicon ? this.editicon.getWidth() : 0;
19866         var x = w - tw;
19867         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19868             
19869         this.combo.trigger.setStyle('left', '0px');
19870         
19871         if(this.list && this.listWidth === undefined){
19872             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19873             this.list.setWidth(lw);
19874             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19875         }
19876         
19877     
19878         
19879     },
19880     
19881     addItem: function(rec)
19882     {
19883         var valueField = this.combo.valueField;
19884         var displayField = this.combo.displayField;
19885         if (this.items.indexOfKey(rec[valueField]) > -1) {
19886             //console.log("GOT " + rec.data.id);
19887             return;
19888         }
19889         
19890         var x = new Roo.form.ComboBoxArray.Item({
19891             //id : rec[this.idField],
19892             data : rec,
19893             displayField : displayField ,
19894             tipField : displayField ,
19895             cb : this
19896         });
19897         // use the 
19898         this.items.add(rec[valueField],x);
19899         // add it before the element..
19900         this.updateHiddenEl();
19901         x.render(this.outerWrap, this.wrap.dom);
19902         // add the image handler..
19903     },
19904     
19905     updateHiddenEl : function()
19906     {
19907         this.validate();
19908         if (!this.hiddenEl) {
19909             return;
19910         }
19911         var ar = [];
19912         var idField = this.combo.valueField;
19913         
19914         this.items.each(function(f) {
19915             ar.push(f.data[idField]);
19916            
19917         });
19918         this.hiddenEl.dom.value = ar.join(',');
19919         this.validate();
19920     },
19921     
19922     reset : function()
19923     {
19924         this.items.clear();
19925         
19926         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19927            el.remove();
19928         });
19929         
19930         this.el.dom.value = '';
19931         if (this.hiddenEl) {
19932             this.hiddenEl.dom.value = '';
19933         }
19934         
19935     },
19936     getValue: function()
19937     {
19938         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19939     },
19940     setValue: function(v) // not a valid action - must use addItems..
19941     {
19942          
19943         this.reset();
19944         
19945         
19946         
19947         if (this.store.isLocal && (typeof(v) == 'string')) {
19948             // then we can use the store to find the values..
19949             // comma seperated at present.. this needs to allow JSON based encoding..
19950             this.hiddenEl.value  = v;
19951             var v_ar = [];
19952             Roo.each(v.split(','), function(k) {
19953                 Roo.log("CHECK " + this.valueField + ',' + k);
19954                 var li = this.store.query(this.valueField, k);
19955                 if (!li.length) {
19956                     return;
19957                 }
19958                 var add = {};
19959                 add[this.valueField] = k;
19960                 add[this.displayField] = li.item(0).data[this.displayField];
19961                 
19962                 this.addItem(add);
19963             }, this) 
19964              
19965         }
19966         if (typeof(v) == 'object' ) {
19967             // then let's assume it's an array of objects..
19968             Roo.each(v, function(l) {
19969                 this.addItem(l);
19970             }, this);
19971              
19972         }
19973         
19974         
19975     },
19976     setFromData: function(v)
19977     {
19978         // this recieves an object, if setValues is called.
19979         this.reset();
19980         this.el.dom.value = v[this.displayField];
19981         this.hiddenEl.dom.value = v[this.valueField];
19982         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19983             return;
19984         }
19985         var kv = v[this.valueField];
19986         var dv = v[this.displayField];
19987         kv = typeof(kv) != 'string' ? '' : kv;
19988         dv = typeof(dv) != 'string' ? '' : dv;
19989         
19990         
19991         var keys = kv.split(',');
19992         var display = dv.split(',');
19993         for (var i = 0 ; i < keys.length; i++) {
19994             
19995             add = {};
19996             add[this.valueField] = keys[i];
19997             add[this.displayField] = display[i];
19998             this.addItem(add);
19999         }
20000       
20001         
20002     },
20003     
20004     /**
20005      * Validates the combox array value
20006      * @return {Boolean} True if the value is valid, else false
20007      */
20008     validate : function(){
20009         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
20010             this.clearInvalid();
20011             return true;
20012         }
20013         return false;
20014     },
20015     
20016     validateValue : function(value){
20017         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
20018         
20019     },
20020     
20021     /*@
20022      * overide
20023      * 
20024      */
20025     isDirty : function() {
20026         if(this.disabled) {
20027             return false;
20028         }
20029         
20030         try {
20031             var d = Roo.decode(String(this.originalValue));
20032         } catch (e) {
20033             return String(this.getValue()) !== String(this.originalValue);
20034         }
20035         
20036         var originalValue = [];
20037         
20038         for (var i = 0; i < d.length; i++){
20039             originalValue.push(d[i][this.valueField]);
20040         }
20041         
20042         return String(this.getValue()) !== String(originalValue.join(','));
20043         
20044     }
20045     
20046 });
20047
20048
20049
20050 /**
20051  * @class Roo.form.ComboBoxArray.Item
20052  * @extends Roo.BoxComponent
20053  * A selected item in the list
20054  *  Fred [x]  Brian [x]  [Pick another |v]
20055  * 
20056  * @constructor
20057  * Create a new item.
20058  * @param {Object} config Configuration options
20059  */
20060  
20061 Roo.form.ComboBoxArray.Item = function(config) {
20062     config.id = Roo.id();
20063     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20064 }
20065
20066 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20067     data : {},
20068     cb: false,
20069     displayField : false,
20070     tipField : false,
20071     
20072     
20073     defaultAutoCreate : {
20074         tag: 'div',
20075         cls: 'x-cbarray-item',
20076         cn : [ 
20077             { tag: 'div' },
20078             {
20079                 tag: 'img',
20080                 width:16,
20081                 height : 16,
20082                 src : Roo.BLANK_IMAGE_URL ,
20083                 align: 'center'
20084             }
20085         ]
20086         
20087     },
20088     
20089  
20090     onRender : function(ct, position)
20091     {
20092         Roo.form.Field.superclass.onRender.call(this, ct, position);
20093         
20094         if(!this.el){
20095             var cfg = this.getAutoCreate();
20096             this.el = ct.createChild(cfg, position);
20097         }
20098         
20099         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20100         
20101         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20102             this.cb.renderer(this.data) :
20103             String.format('{0}',this.data[this.displayField]);
20104         
20105             
20106         this.el.child('div').dom.setAttribute('qtip',
20107                         String.format('{0}',this.data[this.tipField])
20108         );
20109         
20110         this.el.child('img').on('click', this.remove, this);
20111         
20112     },
20113    
20114     remove : function()
20115     {
20116         if(this.cb.disabled){
20117             return;
20118         }
20119         
20120         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20121             this.cb.items.remove(this);
20122             this.el.child('img').un('click', this.remove, this);
20123             this.el.remove();
20124             this.cb.updateHiddenEl();
20125
20126             this.cb.fireEvent('remove', this.cb, this);
20127         }
20128         
20129     }
20130 });/*
20131  * Based on:
20132  * Ext JS Library 1.1.1
20133  * Copyright(c) 2006-2007, Ext JS, LLC.
20134  *
20135  * Originally Released Under LGPL - original licence link has changed is not relivant.
20136  *
20137  * Fork - LGPL
20138  * <script type="text/javascript">
20139  */
20140 /**
20141  * @class Roo.form.Checkbox
20142  * @extends Roo.form.Field
20143  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20144  * @constructor
20145  * Creates a new Checkbox
20146  * @param {Object} config Configuration options
20147  */
20148 Roo.form.Checkbox = function(config){
20149     Roo.form.Checkbox.superclass.constructor.call(this, config);
20150     this.addEvents({
20151         /**
20152          * @event check
20153          * Fires when the checkbox is checked or unchecked.
20154              * @param {Roo.form.Checkbox} this This checkbox
20155              * @param {Boolean} checked The new checked value
20156              */
20157         check : true
20158     });
20159 };
20160
20161 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20162     /**
20163      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20164      */
20165     focusClass : undefined,
20166     /**
20167      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20168      */
20169     fieldClass: "x-form-field",
20170     /**
20171      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20172      */
20173     checked: false,
20174     /**
20175      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20176      * {tag: "input", type: "checkbox", autocomplete: "off"})
20177      */
20178     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20179     /**
20180      * @cfg {String} boxLabel The text that appears beside the checkbox
20181      */
20182     boxLabel : "",
20183     /**
20184      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20185      */  
20186     inputValue : '1',
20187     /**
20188      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20189      */
20190      valueOff: '0', // value when not checked..
20191
20192     actionMode : 'viewEl', 
20193     //
20194     // private
20195     itemCls : 'x-menu-check-item x-form-item',
20196     groupClass : 'x-menu-group-item',
20197     inputType : 'hidden',
20198     
20199     
20200     inSetChecked: false, // check that we are not calling self...
20201     
20202     inputElement: false, // real input element?
20203     basedOn: false, // ????
20204     
20205     isFormField: true, // not sure where this is needed!!!!
20206
20207     onResize : function(){
20208         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20209         if(!this.boxLabel){
20210             this.el.alignTo(this.wrap, 'c-c');
20211         }
20212     },
20213
20214     initEvents : function(){
20215         Roo.form.Checkbox.superclass.initEvents.call(this);
20216         this.el.on("click", this.onClick,  this);
20217         this.el.on("change", this.onClick,  this);
20218     },
20219
20220
20221     getResizeEl : function(){
20222         return this.wrap;
20223     },
20224
20225     getPositionEl : function(){
20226         return this.wrap;
20227     },
20228
20229     // private
20230     onRender : function(ct, position){
20231         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20232         /*
20233         if(this.inputValue !== undefined){
20234             this.el.dom.value = this.inputValue;
20235         }
20236         */
20237         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20238         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20239         var viewEl = this.wrap.createChild({ 
20240             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20241         this.viewEl = viewEl;   
20242         this.wrap.on('click', this.onClick,  this); 
20243         
20244         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20245         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20246         
20247         
20248         
20249         if(this.boxLabel){
20250             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20251         //    viewEl.on('click', this.onClick,  this); 
20252         }
20253         //if(this.checked){
20254             this.setChecked(this.checked);
20255         //}else{
20256             //this.checked = this.el.dom;
20257         //}
20258
20259     },
20260
20261     // private
20262     initValue : Roo.emptyFn,
20263
20264     /**
20265      * Returns the checked state of the checkbox.
20266      * @return {Boolean} True if checked, else false
20267      */
20268     getValue : function(){
20269         if(this.el){
20270             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20271         }
20272         return this.valueOff;
20273         
20274     },
20275
20276         // private
20277     onClick : function(){ 
20278         if (this.disabled) {
20279             return;
20280         }
20281         this.setChecked(!this.checked);
20282
20283         //if(this.el.dom.checked != this.checked){
20284         //    this.setValue(this.el.dom.checked);
20285        // }
20286     },
20287
20288     /**
20289      * Sets the checked state of the checkbox.
20290      * On is always based on a string comparison between inputValue and the param.
20291      * @param {Boolean/String} value - the value to set 
20292      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20293      */
20294     setValue : function(v,suppressEvent){
20295         
20296         
20297         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20298         //if(this.el && this.el.dom){
20299         //    this.el.dom.checked = this.checked;
20300         //    this.el.dom.defaultChecked = this.checked;
20301         //}
20302         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20303         //this.fireEvent("check", this, this.checked);
20304     },
20305     // private..
20306     setChecked : function(state,suppressEvent)
20307     {
20308         if (this.inSetChecked) {
20309             this.checked = state;
20310             return;
20311         }
20312         
20313     
20314         if(this.wrap){
20315             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20316         }
20317         this.checked = state;
20318         if(suppressEvent !== true){
20319             this.fireEvent('check', this, state);
20320         }
20321         this.inSetChecked = true;
20322         this.el.dom.value = state ? this.inputValue : this.valueOff;
20323         this.inSetChecked = false;
20324         
20325     },
20326     // handle setting of hidden value by some other method!!?!?
20327     setFromHidden: function()
20328     {
20329         if(!this.el){
20330             return;
20331         }
20332         //console.log("SET FROM HIDDEN");
20333         //alert('setFrom hidden');
20334         this.setValue(this.el.dom.value);
20335     },
20336     
20337     onDestroy : function()
20338     {
20339         if(this.viewEl){
20340             Roo.get(this.viewEl).remove();
20341         }
20342          
20343         Roo.form.Checkbox.superclass.onDestroy.call(this);
20344     },
20345     
20346     setBoxLabel : function(str)
20347     {
20348         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20349     }
20350
20351 });/*
20352  * Based on:
20353  * Ext JS Library 1.1.1
20354  * Copyright(c) 2006-2007, Ext JS, LLC.
20355  *
20356  * Originally Released Under LGPL - original licence link has changed is not relivant.
20357  *
20358  * Fork - LGPL
20359  * <script type="text/javascript">
20360  */
20361  
20362 /**
20363  * @class Roo.form.Radio
20364  * @extends Roo.form.Checkbox
20365  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20366  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20367  * @constructor
20368  * Creates a new Radio
20369  * @param {Object} config Configuration options
20370  */
20371 Roo.form.Radio = function(){
20372     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20373 };
20374 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20375     inputType: 'radio',
20376
20377     /**
20378      * If this radio is part of a group, it will return the selected value
20379      * @return {String}
20380      */
20381     getGroupValue : function(){
20382         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20383     },
20384     
20385     
20386     onRender : function(ct, position){
20387         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20388         
20389         if(this.inputValue !== undefined){
20390             this.el.dom.value = this.inputValue;
20391         }
20392          
20393         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20394         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20395         //var viewEl = this.wrap.createChild({ 
20396         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20397         //this.viewEl = viewEl;   
20398         //this.wrap.on('click', this.onClick,  this); 
20399         
20400         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20401         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20402         
20403         
20404         
20405         if(this.boxLabel){
20406             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20407         //    viewEl.on('click', this.onClick,  this); 
20408         }
20409          if(this.checked){
20410             this.el.dom.checked =   'checked' ;
20411         }
20412          
20413     } 
20414     
20415     
20416 });//<script type="text/javascript">
20417
20418 /*
20419  * Based  Ext JS Library 1.1.1
20420  * Copyright(c) 2006-2007, Ext JS, LLC.
20421  * LGPL
20422  *
20423  */
20424  
20425 /**
20426  * @class Roo.HtmlEditorCore
20427  * @extends Roo.Component
20428  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20429  *
20430  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20431  */
20432
20433 Roo.HtmlEditorCore = function(config){
20434     
20435     
20436     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20437     
20438     
20439     this.addEvents({
20440         /**
20441          * @event initialize
20442          * Fires when the editor is fully initialized (including the iframe)
20443          * @param {Roo.HtmlEditorCore} this
20444          */
20445         initialize: true,
20446         /**
20447          * @event activate
20448          * Fires when the editor is first receives the focus. Any insertion must wait
20449          * until after this event.
20450          * @param {Roo.HtmlEditorCore} this
20451          */
20452         activate: true,
20453          /**
20454          * @event beforesync
20455          * Fires before the textarea is updated with content from the editor iframe. Return false
20456          * to cancel the sync.
20457          * @param {Roo.HtmlEditorCore} this
20458          * @param {String} html
20459          */
20460         beforesync: true,
20461          /**
20462          * @event beforepush
20463          * Fires before the iframe editor is updated with content from the textarea. Return false
20464          * to cancel the push.
20465          * @param {Roo.HtmlEditorCore} this
20466          * @param {String} html
20467          */
20468         beforepush: true,
20469          /**
20470          * @event sync
20471          * Fires when the textarea is updated with content from the editor iframe.
20472          * @param {Roo.HtmlEditorCore} this
20473          * @param {String} html
20474          */
20475         sync: true,
20476          /**
20477          * @event push
20478          * Fires when the iframe editor is updated with content from the textarea.
20479          * @param {Roo.HtmlEditorCore} this
20480          * @param {String} html
20481          */
20482         push: true,
20483         
20484         /**
20485          * @event editorevent
20486          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20487          * @param {Roo.HtmlEditorCore} this
20488          */
20489         editorevent: true
20490         
20491     });
20492     
20493     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20494     
20495     // defaults : white / black...
20496     this.applyBlacklists();
20497     
20498     
20499     
20500 };
20501
20502
20503 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20504
20505
20506      /**
20507      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20508      */
20509     
20510     owner : false,
20511     
20512      /**
20513      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20514      *                        Roo.resizable.
20515      */
20516     resizable : false,
20517      /**
20518      * @cfg {Number} height (in pixels)
20519      */   
20520     height: 300,
20521    /**
20522      * @cfg {Number} width (in pixels)
20523      */   
20524     width: 500,
20525     
20526     /**
20527      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20528      * 
20529      */
20530     stylesheets: false,
20531     
20532     // id of frame..
20533     frameId: false,
20534     
20535     // private properties
20536     validationEvent : false,
20537     deferHeight: true,
20538     initialized : false,
20539     activated : false,
20540     sourceEditMode : false,
20541     onFocus : Roo.emptyFn,
20542     iframePad:3,
20543     hideMode:'offsets',
20544     
20545     clearUp: true,
20546     
20547     // blacklist + whitelisted elements..
20548     black: false,
20549     white: false,
20550      
20551     bodyCls : '',
20552
20553     /**
20554      * Protected method that will not generally be called directly. It
20555      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20556      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20557      */
20558     getDocMarkup : function(){
20559         // body styles..
20560         var st = '';
20561         
20562         // inherit styels from page...?? 
20563         if (this.stylesheets === false) {
20564             
20565             Roo.get(document.head).select('style').each(function(node) {
20566                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20567             });
20568             
20569             Roo.get(document.head).select('link').each(function(node) { 
20570                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20571             });
20572             
20573         } else if (!this.stylesheets.length) {
20574                 // simple..
20575                 st = '<style type="text/css">' +
20576                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20577                    '</style>';
20578         } else { 
20579             st = '<style type="text/css">' +
20580                     this.stylesheets +
20581                 '</style>';
20582         }
20583         
20584         st +=  '<style type="text/css">' +
20585             'IMG { cursor: pointer } ' +
20586         '</style>';
20587
20588         var cls = 'roo-htmleditor-body';
20589         
20590         if(this.bodyCls.length){
20591             cls += ' ' + this.bodyCls;
20592         }
20593         
20594         return '<html><head>' + st  +
20595             //<style type="text/css">' +
20596             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20597             //'</style>' +
20598             ' </head><body class="' +  cls + '"></body></html>';
20599     },
20600
20601     // private
20602     onRender : function(ct, position)
20603     {
20604         var _t = this;
20605         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20606         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20607         
20608         
20609         this.el.dom.style.border = '0 none';
20610         this.el.dom.setAttribute('tabIndex', -1);
20611         this.el.addClass('x-hidden hide');
20612         
20613         
20614         
20615         if(Roo.isIE){ // fix IE 1px bogus margin
20616             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20617         }
20618        
20619         
20620         this.frameId = Roo.id();
20621         
20622          
20623         
20624         var iframe = this.owner.wrap.createChild({
20625             tag: 'iframe',
20626             cls: 'form-control', // bootstrap..
20627             id: this.frameId,
20628             name: this.frameId,
20629             frameBorder : 'no',
20630             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20631         }, this.el
20632         );
20633         
20634         
20635         this.iframe = iframe.dom;
20636
20637          this.assignDocWin();
20638         
20639         this.doc.designMode = 'on';
20640        
20641         this.doc.open();
20642         this.doc.write(this.getDocMarkup());
20643         this.doc.close();
20644
20645         
20646         var task = { // must defer to wait for browser to be ready
20647             run : function(){
20648                 //console.log("run task?" + this.doc.readyState);
20649                 this.assignDocWin();
20650                 if(this.doc.body || this.doc.readyState == 'complete'){
20651                     try {
20652                         this.doc.designMode="on";
20653                     } catch (e) {
20654                         return;
20655                     }
20656                     Roo.TaskMgr.stop(task);
20657                     this.initEditor.defer(10, this);
20658                 }
20659             },
20660             interval : 10,
20661             duration: 10000,
20662             scope: this
20663         };
20664         Roo.TaskMgr.start(task);
20665
20666     },
20667
20668     // private
20669     onResize : function(w, h)
20670     {
20671          Roo.log('resize: ' +w + ',' + h );
20672         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20673         if(!this.iframe){
20674             return;
20675         }
20676         if(typeof w == 'number'){
20677             
20678             this.iframe.style.width = w + 'px';
20679         }
20680         if(typeof h == 'number'){
20681             
20682             this.iframe.style.height = h + 'px';
20683             if(this.doc){
20684                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20685             }
20686         }
20687         
20688     },
20689
20690     /**
20691      * Toggles the editor between standard and source edit mode.
20692      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20693      */
20694     toggleSourceEdit : function(sourceEditMode){
20695         
20696         this.sourceEditMode = sourceEditMode === true;
20697         
20698         if(this.sourceEditMode){
20699  
20700             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20701             
20702         }else{
20703             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20704             //this.iframe.className = '';
20705             this.deferFocus();
20706         }
20707         //this.setSize(this.owner.wrap.getSize());
20708         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20709     },
20710
20711     
20712   
20713
20714     /**
20715      * Protected method that will not generally be called directly. If you need/want
20716      * custom HTML cleanup, this is the method you should override.
20717      * @param {String} html The HTML to be cleaned
20718      * return {String} The cleaned HTML
20719      */
20720     cleanHtml : function(html){
20721         html = String(html);
20722         if(html.length > 5){
20723             if(Roo.isSafari){ // strip safari nonsense
20724                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20725             }
20726         }
20727         if(html == '&nbsp;'){
20728             html = '';
20729         }
20730         return html;
20731     },
20732
20733     /**
20734      * HTML Editor -> Textarea
20735      * Protected method that will not generally be called directly. Syncs the contents
20736      * of the editor iframe with the textarea.
20737      */
20738     syncValue : function(){
20739         if(this.initialized){
20740             var bd = (this.doc.body || this.doc.documentElement);
20741             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20742             var html = bd.innerHTML;
20743             if(Roo.isSafari){
20744                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20745                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20746                 if(m && m[1]){
20747                     html = '<div style="'+m[0]+'">' + html + '</div>';
20748                 }
20749             }
20750             html = this.cleanHtml(html);
20751             // fix up the special chars.. normaly like back quotes in word...
20752             // however we do not want to do this with chinese..
20753             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20754                 var cc = b.charCodeAt();
20755                 if (
20756                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20757                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20758                     (cc >= 0xf900 && cc < 0xfb00 )
20759                 ) {
20760                         return b;
20761                 }
20762                 return "&#"+cc+";" 
20763             });
20764             if(this.owner.fireEvent('beforesync', this, html) !== false){
20765                 this.el.dom.value = html;
20766                 this.owner.fireEvent('sync', this, html);
20767             }
20768         }
20769     },
20770
20771     /**
20772      * Protected method that will not generally be called directly. Pushes the value of the textarea
20773      * into the iframe editor.
20774      */
20775     pushValue : function(){
20776         if(this.initialized){
20777             var v = this.el.dom.value.trim();
20778             
20779 //            if(v.length < 1){
20780 //                v = '&#160;';
20781 //            }
20782             
20783             if(this.owner.fireEvent('beforepush', this, v) !== false){
20784                 var d = (this.doc.body || this.doc.documentElement);
20785                 d.innerHTML = v;
20786                 this.cleanUpPaste();
20787                 this.el.dom.value = d.innerHTML;
20788                 this.owner.fireEvent('push', this, v);
20789             }
20790         }
20791     },
20792
20793     // private
20794     deferFocus : function(){
20795         this.focus.defer(10, this);
20796     },
20797
20798     // doc'ed in Field
20799     focus : function(){
20800         if(this.win && !this.sourceEditMode){
20801             this.win.focus();
20802         }else{
20803             this.el.focus();
20804         }
20805     },
20806     
20807     assignDocWin: function()
20808     {
20809         var iframe = this.iframe;
20810         
20811          if(Roo.isIE){
20812             this.doc = iframe.contentWindow.document;
20813             this.win = iframe.contentWindow;
20814         } else {
20815 //            if (!Roo.get(this.frameId)) {
20816 //                return;
20817 //            }
20818 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20819 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20820             
20821             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20822                 return;
20823             }
20824             
20825             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20826             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20827         }
20828     },
20829     
20830     // private
20831     initEditor : function(){
20832         //console.log("INIT EDITOR");
20833         this.assignDocWin();
20834         
20835         
20836         
20837         this.doc.designMode="on";
20838         this.doc.open();
20839         this.doc.write(this.getDocMarkup());
20840         this.doc.close();
20841         
20842         var dbody = (this.doc.body || this.doc.documentElement);
20843         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20844         // this copies styles from the containing element into thsi one..
20845         // not sure why we need all of this..
20846         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20847         
20848         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20849         //ss['background-attachment'] = 'fixed'; // w3c
20850         dbody.bgProperties = 'fixed'; // ie
20851         //Roo.DomHelper.applyStyles(dbody, ss);
20852         Roo.EventManager.on(this.doc, {
20853             //'mousedown': this.onEditorEvent,
20854             'mouseup': this.onEditorEvent,
20855             'dblclick': this.onEditorEvent,
20856             'click': this.onEditorEvent,
20857             'keyup': this.onEditorEvent,
20858             buffer:100,
20859             scope: this
20860         });
20861         if(Roo.isGecko){
20862             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20863         }
20864         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20865             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20866         }
20867         this.initialized = true;
20868
20869         this.owner.fireEvent('initialize', this);
20870         this.pushValue();
20871     },
20872
20873     // private
20874     onDestroy : function(){
20875         
20876         
20877         
20878         if(this.rendered){
20879             
20880             //for (var i =0; i < this.toolbars.length;i++) {
20881             //    // fixme - ask toolbars for heights?
20882             //    this.toolbars[i].onDestroy();
20883            // }
20884             
20885             //this.wrap.dom.innerHTML = '';
20886             //this.wrap.remove();
20887         }
20888     },
20889
20890     // private
20891     onFirstFocus : function(){
20892         
20893         this.assignDocWin();
20894         
20895         
20896         this.activated = true;
20897          
20898     
20899         if(Roo.isGecko){ // prevent silly gecko errors
20900             this.win.focus();
20901             var s = this.win.getSelection();
20902             if(!s.focusNode || s.focusNode.nodeType != 3){
20903                 var r = s.getRangeAt(0);
20904                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20905                 r.collapse(true);
20906                 this.deferFocus();
20907             }
20908             try{
20909                 this.execCmd('useCSS', true);
20910                 this.execCmd('styleWithCSS', false);
20911             }catch(e){}
20912         }
20913         this.owner.fireEvent('activate', this);
20914     },
20915
20916     // private
20917     adjustFont: function(btn){
20918         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20919         //if(Roo.isSafari){ // safari
20920         //    adjust *= 2;
20921        // }
20922         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20923         if(Roo.isSafari){ // safari
20924             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20925             v =  (v < 10) ? 10 : v;
20926             v =  (v > 48) ? 48 : v;
20927             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20928             
20929         }
20930         
20931         
20932         v = Math.max(1, v+adjust);
20933         
20934         this.execCmd('FontSize', v  );
20935     },
20936
20937     onEditorEvent : function(e)
20938     {
20939         this.owner.fireEvent('editorevent', this, e);
20940       //  this.updateToolbar();
20941         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20942     },
20943
20944     insertTag : function(tg)
20945     {
20946         // could be a bit smarter... -> wrap the current selected tRoo..
20947         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20948             
20949             range = this.createRange(this.getSelection());
20950             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20951             wrappingNode.appendChild(range.extractContents());
20952             range.insertNode(wrappingNode);
20953
20954             return;
20955             
20956             
20957             
20958         }
20959         this.execCmd("formatblock",   tg);
20960         
20961     },
20962     
20963     insertText : function(txt)
20964     {
20965         
20966         
20967         var range = this.createRange();
20968         range.deleteContents();
20969                //alert(Sender.getAttribute('label'));
20970                
20971         range.insertNode(this.doc.createTextNode(txt));
20972     } ,
20973     
20974      
20975
20976     /**
20977      * Executes a Midas editor command on the editor document and performs necessary focus and
20978      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20979      * @param {String} cmd The Midas command
20980      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20981      */
20982     relayCmd : function(cmd, value){
20983         this.win.focus();
20984         this.execCmd(cmd, value);
20985         this.owner.fireEvent('editorevent', this);
20986         //this.updateToolbar();
20987         this.owner.deferFocus();
20988     },
20989
20990     /**
20991      * Executes a Midas editor command directly on the editor document.
20992      * For visual commands, you should use {@link #relayCmd} instead.
20993      * <b>This should only be called after the editor is initialized.</b>
20994      * @param {String} cmd The Midas command
20995      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20996      */
20997     execCmd : function(cmd, value){
20998         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20999         this.syncValue();
21000     },
21001  
21002  
21003    
21004     /**
21005      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21006      * to insert tRoo.
21007      * @param {String} text | dom node.. 
21008      */
21009     insertAtCursor : function(text)
21010     {
21011         
21012         if(!this.activated){
21013             return;
21014         }
21015         /*
21016         if(Roo.isIE){
21017             this.win.focus();
21018             var r = this.doc.selection.createRange();
21019             if(r){
21020                 r.collapse(true);
21021                 r.pasteHTML(text);
21022                 this.syncValue();
21023                 this.deferFocus();
21024             
21025             }
21026             return;
21027         }
21028         */
21029         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21030             this.win.focus();
21031             
21032             
21033             // from jquery ui (MIT licenced)
21034             var range, node;
21035             var win = this.win;
21036             
21037             if (win.getSelection && win.getSelection().getRangeAt) {
21038                 range = win.getSelection().getRangeAt(0);
21039                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21040                 range.insertNode(node);
21041             } else if (win.document.selection && win.document.selection.createRange) {
21042                 // no firefox support
21043                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21044                 win.document.selection.createRange().pasteHTML(txt);
21045             } else {
21046                 // no firefox support
21047                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21048                 this.execCmd('InsertHTML', txt);
21049             } 
21050             
21051             this.syncValue();
21052             
21053             this.deferFocus();
21054         }
21055     },
21056  // private
21057     mozKeyPress : function(e){
21058         if(e.ctrlKey){
21059             var c = e.getCharCode(), cmd;
21060           
21061             if(c > 0){
21062                 c = String.fromCharCode(c).toLowerCase();
21063                 switch(c){
21064                     case 'b':
21065                         cmd = 'bold';
21066                         break;
21067                     case 'i':
21068                         cmd = 'italic';
21069                         break;
21070                     
21071                     case 'u':
21072                         cmd = 'underline';
21073                         break;
21074                     
21075                     case 'v':
21076                         this.cleanUpPaste.defer(100, this);
21077                         return;
21078                         
21079                 }
21080                 if(cmd){
21081                     this.win.focus();
21082                     this.execCmd(cmd);
21083                     this.deferFocus();
21084                     e.preventDefault();
21085                 }
21086                 
21087             }
21088         }
21089     },
21090
21091     // private
21092     fixKeys : function(){ // load time branching for fastest keydown performance
21093         if(Roo.isIE){
21094             return function(e){
21095                 var k = e.getKey(), r;
21096                 if(k == e.TAB){
21097                     e.stopEvent();
21098                     r = this.doc.selection.createRange();
21099                     if(r){
21100                         r.collapse(true);
21101                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21102                         this.deferFocus();
21103                     }
21104                     return;
21105                 }
21106                 
21107                 if(k == e.ENTER){
21108                     r = this.doc.selection.createRange();
21109                     if(r){
21110                         var target = r.parentElement();
21111                         if(!target || target.tagName.toLowerCase() != 'li'){
21112                             e.stopEvent();
21113                             r.pasteHTML('<br />');
21114                             r.collapse(false);
21115                             r.select();
21116                         }
21117                     }
21118                 }
21119                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21120                     this.cleanUpPaste.defer(100, this);
21121                     return;
21122                 }
21123                 
21124                 
21125             };
21126         }else if(Roo.isOpera){
21127             return function(e){
21128                 var k = e.getKey();
21129                 if(k == e.TAB){
21130                     e.stopEvent();
21131                     this.win.focus();
21132                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21133                     this.deferFocus();
21134                 }
21135                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21136                     this.cleanUpPaste.defer(100, this);
21137                     return;
21138                 }
21139                 
21140             };
21141         }else if(Roo.isSafari){
21142             return function(e){
21143                 var k = e.getKey();
21144                 
21145                 if(k == e.TAB){
21146                     e.stopEvent();
21147                     this.execCmd('InsertText','\t');
21148                     this.deferFocus();
21149                     return;
21150                 }
21151                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21152                     this.cleanUpPaste.defer(100, this);
21153                     return;
21154                 }
21155                 
21156              };
21157         }
21158     }(),
21159     
21160     getAllAncestors: function()
21161     {
21162         var p = this.getSelectedNode();
21163         var a = [];
21164         if (!p) {
21165             a.push(p); // push blank onto stack..
21166             p = this.getParentElement();
21167         }
21168         
21169         
21170         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21171             a.push(p);
21172             p = p.parentNode;
21173         }
21174         a.push(this.doc.body);
21175         return a;
21176     },
21177     lastSel : false,
21178     lastSelNode : false,
21179     
21180     
21181     getSelection : function() 
21182     {
21183         this.assignDocWin();
21184         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21185     },
21186     
21187     getSelectedNode: function() 
21188     {
21189         // this may only work on Gecko!!!
21190         
21191         // should we cache this!!!!
21192         
21193         
21194         
21195          
21196         var range = this.createRange(this.getSelection()).cloneRange();
21197         
21198         if (Roo.isIE) {
21199             var parent = range.parentElement();
21200             while (true) {
21201                 var testRange = range.duplicate();
21202                 testRange.moveToElementText(parent);
21203                 if (testRange.inRange(range)) {
21204                     break;
21205                 }
21206                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21207                     break;
21208                 }
21209                 parent = parent.parentElement;
21210             }
21211             return parent;
21212         }
21213         
21214         // is ancestor a text element.
21215         var ac =  range.commonAncestorContainer;
21216         if (ac.nodeType == 3) {
21217             ac = ac.parentNode;
21218         }
21219         
21220         var ar = ac.childNodes;
21221          
21222         var nodes = [];
21223         var other_nodes = [];
21224         var has_other_nodes = false;
21225         for (var i=0;i<ar.length;i++) {
21226             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21227                 continue;
21228             }
21229             // fullly contained node.
21230             
21231             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21232                 nodes.push(ar[i]);
21233                 continue;
21234             }
21235             
21236             // probably selected..
21237             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21238                 other_nodes.push(ar[i]);
21239                 continue;
21240             }
21241             // outer..
21242             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21243                 continue;
21244             }
21245             
21246             
21247             has_other_nodes = true;
21248         }
21249         if (!nodes.length && other_nodes.length) {
21250             nodes= other_nodes;
21251         }
21252         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21253             return false;
21254         }
21255         
21256         return nodes[0];
21257     },
21258     createRange: function(sel)
21259     {
21260         // this has strange effects when using with 
21261         // top toolbar - not sure if it's a great idea.
21262         //this.editor.contentWindow.focus();
21263         if (typeof sel != "undefined") {
21264             try {
21265                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21266             } catch(e) {
21267                 return this.doc.createRange();
21268             }
21269         } else {
21270             return this.doc.createRange();
21271         }
21272     },
21273     getParentElement: function()
21274     {
21275         
21276         this.assignDocWin();
21277         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21278         
21279         var range = this.createRange(sel);
21280          
21281         try {
21282             var p = range.commonAncestorContainer;
21283             while (p.nodeType == 3) { // text node
21284                 p = p.parentNode;
21285             }
21286             return p;
21287         } catch (e) {
21288             return null;
21289         }
21290     
21291     },
21292     /***
21293      *
21294      * Range intersection.. the hard stuff...
21295      *  '-1' = before
21296      *  '0' = hits..
21297      *  '1' = after.
21298      *         [ -- selected range --- ]
21299      *   [fail]                        [fail]
21300      *
21301      *    basically..
21302      *      if end is before start or  hits it. fail.
21303      *      if start is after end or hits it fail.
21304      *
21305      *   if either hits (but other is outside. - then it's not 
21306      *   
21307      *    
21308      **/
21309     
21310     
21311     // @see http://www.thismuchiknow.co.uk/?p=64.
21312     rangeIntersectsNode : function(range, node)
21313     {
21314         var nodeRange = node.ownerDocument.createRange();
21315         try {
21316             nodeRange.selectNode(node);
21317         } catch (e) {
21318             nodeRange.selectNodeContents(node);
21319         }
21320     
21321         var rangeStartRange = range.cloneRange();
21322         rangeStartRange.collapse(true);
21323     
21324         var rangeEndRange = range.cloneRange();
21325         rangeEndRange.collapse(false);
21326     
21327         var nodeStartRange = nodeRange.cloneRange();
21328         nodeStartRange.collapse(true);
21329     
21330         var nodeEndRange = nodeRange.cloneRange();
21331         nodeEndRange.collapse(false);
21332     
21333         return rangeStartRange.compareBoundaryPoints(
21334                  Range.START_TO_START, nodeEndRange) == -1 &&
21335                rangeEndRange.compareBoundaryPoints(
21336                  Range.START_TO_START, nodeStartRange) == 1;
21337         
21338          
21339     },
21340     rangeCompareNode : function(range, node)
21341     {
21342         var nodeRange = node.ownerDocument.createRange();
21343         try {
21344             nodeRange.selectNode(node);
21345         } catch (e) {
21346             nodeRange.selectNodeContents(node);
21347         }
21348         
21349         
21350         range.collapse(true);
21351     
21352         nodeRange.collapse(true);
21353      
21354         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21355         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21356          
21357         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21358         
21359         var nodeIsBefore   =  ss == 1;
21360         var nodeIsAfter    = ee == -1;
21361         
21362         if (nodeIsBefore && nodeIsAfter) {
21363             return 0; // outer
21364         }
21365         if (!nodeIsBefore && nodeIsAfter) {
21366             return 1; //right trailed.
21367         }
21368         
21369         if (nodeIsBefore && !nodeIsAfter) {
21370             return 2;  // left trailed.
21371         }
21372         // fully contined.
21373         return 3;
21374     },
21375
21376     // private? - in a new class?
21377     cleanUpPaste :  function()
21378     {
21379         // cleans up the whole document..
21380         Roo.log('cleanuppaste');
21381         
21382         this.cleanUpChildren(this.doc.body);
21383         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21384         if (clean != this.doc.body.innerHTML) {
21385             this.doc.body.innerHTML = clean;
21386         }
21387         
21388     },
21389     
21390     cleanWordChars : function(input) {// change the chars to hex code
21391         var he = Roo.HtmlEditorCore;
21392         
21393         var output = input;
21394         Roo.each(he.swapCodes, function(sw) { 
21395             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21396             
21397             output = output.replace(swapper, sw[1]);
21398         });
21399         
21400         return output;
21401     },
21402     
21403     
21404     cleanUpChildren : function (n)
21405     {
21406         if (!n.childNodes.length) {
21407             return;
21408         }
21409         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21410            this.cleanUpChild(n.childNodes[i]);
21411         }
21412     },
21413     
21414     
21415         
21416     
21417     cleanUpChild : function (node)
21418     {
21419         var ed = this;
21420         //console.log(node);
21421         if (node.nodeName == "#text") {
21422             // clean up silly Windows -- stuff?
21423             return; 
21424         }
21425         if (node.nodeName == "#comment") {
21426             node.parentNode.removeChild(node);
21427             // clean up silly Windows -- stuff?
21428             return; 
21429         }
21430         var lcname = node.tagName.toLowerCase();
21431         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21432         // whitelist of tags..
21433         
21434         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21435             // remove node.
21436             node.parentNode.removeChild(node);
21437             return;
21438             
21439         }
21440         
21441         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21442         
21443         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21444         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21445         
21446         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21447         //    remove_keep_children = true;
21448         //}
21449         
21450         if (remove_keep_children) {
21451             this.cleanUpChildren(node);
21452             // inserts everything just before this node...
21453             while (node.childNodes.length) {
21454                 var cn = node.childNodes[0];
21455                 node.removeChild(cn);
21456                 node.parentNode.insertBefore(cn, node);
21457             }
21458             node.parentNode.removeChild(node);
21459             return;
21460         }
21461         
21462         if (!node.attributes || !node.attributes.length) {
21463             this.cleanUpChildren(node);
21464             return;
21465         }
21466         
21467         function cleanAttr(n,v)
21468         {
21469             
21470             if (v.match(/^\./) || v.match(/^\//)) {
21471                 return;
21472             }
21473             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21474                 return;
21475             }
21476             if (v.match(/^#/)) {
21477                 return;
21478             }
21479 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21480             node.removeAttribute(n);
21481             
21482         }
21483         
21484         var cwhite = this.cwhite;
21485         var cblack = this.cblack;
21486             
21487         function cleanStyle(n,v)
21488         {
21489             if (v.match(/expression/)) { //XSS?? should we even bother..
21490                 node.removeAttribute(n);
21491                 return;
21492             }
21493             
21494             var parts = v.split(/;/);
21495             var clean = [];
21496             
21497             Roo.each(parts, function(p) {
21498                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21499                 if (!p.length) {
21500                     return true;
21501                 }
21502                 var l = p.split(':').shift().replace(/\s+/g,'');
21503                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21504                 
21505                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21506 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21507                     //node.removeAttribute(n);
21508                     return true;
21509                 }
21510                 //Roo.log()
21511                 // only allow 'c whitelisted system attributes'
21512                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21513 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21514                     //node.removeAttribute(n);
21515                     return true;
21516                 }
21517                 
21518                 
21519                  
21520                 
21521                 clean.push(p);
21522                 return true;
21523             });
21524             if (clean.length) { 
21525                 node.setAttribute(n, clean.join(';'));
21526             } else {
21527                 node.removeAttribute(n);
21528             }
21529             
21530         }
21531         
21532         
21533         for (var i = node.attributes.length-1; i > -1 ; i--) {
21534             var a = node.attributes[i];
21535             //console.log(a);
21536             
21537             if (a.name.toLowerCase().substr(0,2)=='on')  {
21538                 node.removeAttribute(a.name);
21539                 continue;
21540             }
21541             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21542                 node.removeAttribute(a.name);
21543                 continue;
21544             }
21545             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21546                 cleanAttr(a.name,a.value); // fixme..
21547                 continue;
21548             }
21549             if (a.name == 'style') {
21550                 cleanStyle(a.name,a.value);
21551                 continue;
21552             }
21553             /// clean up MS crap..
21554             // tecnically this should be a list of valid class'es..
21555             
21556             
21557             if (a.name == 'class') {
21558                 if (a.value.match(/^Mso/)) {
21559                     node.className = '';
21560                 }
21561                 
21562                 if (a.value.match(/^body$/)) {
21563                     node.className = '';
21564                 }
21565                 continue;
21566             }
21567             
21568             // style cleanup!?
21569             // class cleanup?
21570             
21571         }
21572         
21573         
21574         this.cleanUpChildren(node);
21575         
21576         
21577     },
21578     
21579     /**
21580      * Clean up MS wordisms...
21581      */
21582     cleanWord : function(node)
21583     {
21584         
21585         
21586         if (!node) {
21587             this.cleanWord(this.doc.body);
21588             return;
21589         }
21590         if (node.nodeName == "#text") {
21591             // clean up silly Windows -- stuff?
21592             return; 
21593         }
21594         if (node.nodeName == "#comment") {
21595             node.parentNode.removeChild(node);
21596             // clean up silly Windows -- stuff?
21597             return; 
21598         }
21599         
21600         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21601             node.parentNode.removeChild(node);
21602             return;
21603         }
21604         
21605         // remove - but keep children..
21606         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21607             while (node.childNodes.length) {
21608                 var cn = node.childNodes[0];
21609                 node.removeChild(cn);
21610                 node.parentNode.insertBefore(cn, node);
21611             }
21612             node.parentNode.removeChild(node);
21613             this.iterateChildren(node, this.cleanWord);
21614             return;
21615         }
21616         // clean styles
21617         if (node.className.length) {
21618             
21619             var cn = node.className.split(/\W+/);
21620             var cna = [];
21621             Roo.each(cn, function(cls) {
21622                 if (cls.match(/Mso[a-zA-Z]+/)) {
21623                     return;
21624                 }
21625                 cna.push(cls);
21626             });
21627             node.className = cna.length ? cna.join(' ') : '';
21628             if (!cna.length) {
21629                 node.removeAttribute("class");
21630             }
21631         }
21632         
21633         if (node.hasAttribute("lang")) {
21634             node.removeAttribute("lang");
21635         }
21636         
21637         if (node.hasAttribute("style")) {
21638             
21639             var styles = node.getAttribute("style").split(";");
21640             var nstyle = [];
21641             Roo.each(styles, function(s) {
21642                 if (!s.match(/:/)) {
21643                     return;
21644                 }
21645                 var kv = s.split(":");
21646                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21647                     return;
21648                 }
21649                 // what ever is left... we allow.
21650                 nstyle.push(s);
21651             });
21652             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21653             if (!nstyle.length) {
21654                 node.removeAttribute('style');
21655             }
21656         }
21657         this.iterateChildren(node, this.cleanWord);
21658         
21659         
21660         
21661     },
21662     /**
21663      * iterateChildren of a Node, calling fn each time, using this as the scole..
21664      * @param {DomNode} node node to iterate children of.
21665      * @param {Function} fn method of this class to call on each item.
21666      */
21667     iterateChildren : function(node, fn)
21668     {
21669         if (!node.childNodes.length) {
21670                 return;
21671         }
21672         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21673            fn.call(this, node.childNodes[i])
21674         }
21675     },
21676     
21677     
21678     /**
21679      * cleanTableWidths.
21680      *
21681      * Quite often pasting from word etc.. results in tables with column and widths.
21682      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21683      *
21684      */
21685     cleanTableWidths : function(node)
21686     {
21687          
21688          
21689         if (!node) {
21690             this.cleanTableWidths(this.doc.body);
21691             return;
21692         }
21693         
21694         // ignore list...
21695         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21696             return; 
21697         }
21698         Roo.log(node.tagName);
21699         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21700             this.iterateChildren(node, this.cleanTableWidths);
21701             return;
21702         }
21703         if (node.hasAttribute('width')) {
21704             node.removeAttribute('width');
21705         }
21706         
21707          
21708         if (node.hasAttribute("style")) {
21709             // pretty basic...
21710             
21711             var styles = node.getAttribute("style").split(";");
21712             var nstyle = [];
21713             Roo.each(styles, function(s) {
21714                 if (!s.match(/:/)) {
21715                     return;
21716                 }
21717                 var kv = s.split(":");
21718                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21719                     return;
21720                 }
21721                 // what ever is left... we allow.
21722                 nstyle.push(s);
21723             });
21724             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21725             if (!nstyle.length) {
21726                 node.removeAttribute('style');
21727             }
21728         }
21729         
21730         this.iterateChildren(node, this.cleanTableWidths);
21731         
21732         
21733     },
21734     
21735     
21736     
21737     
21738     domToHTML : function(currentElement, depth, nopadtext) {
21739         
21740         depth = depth || 0;
21741         nopadtext = nopadtext || false;
21742     
21743         if (!currentElement) {
21744             return this.domToHTML(this.doc.body);
21745         }
21746         
21747         //Roo.log(currentElement);
21748         var j;
21749         var allText = false;
21750         var nodeName = currentElement.nodeName;
21751         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21752         
21753         if  (nodeName == '#text') {
21754             
21755             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21756         }
21757         
21758         
21759         var ret = '';
21760         if (nodeName != 'BODY') {
21761              
21762             var i = 0;
21763             // Prints the node tagName, such as <A>, <IMG>, etc
21764             if (tagName) {
21765                 var attr = [];
21766                 for(i = 0; i < currentElement.attributes.length;i++) {
21767                     // quoting?
21768                     var aname = currentElement.attributes.item(i).name;
21769                     if (!currentElement.attributes.item(i).value.length) {
21770                         continue;
21771                     }
21772                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21773                 }
21774                 
21775                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21776             } 
21777             else {
21778                 
21779                 // eack
21780             }
21781         } else {
21782             tagName = false;
21783         }
21784         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21785             return ret;
21786         }
21787         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21788             nopadtext = true;
21789         }
21790         
21791         
21792         // Traverse the tree
21793         i = 0;
21794         var currentElementChild = currentElement.childNodes.item(i);
21795         var allText = true;
21796         var innerHTML  = '';
21797         lastnode = '';
21798         while (currentElementChild) {
21799             // Formatting code (indent the tree so it looks nice on the screen)
21800             var nopad = nopadtext;
21801             if (lastnode == 'SPAN') {
21802                 nopad  = true;
21803             }
21804             // text
21805             if  (currentElementChild.nodeName == '#text') {
21806                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21807                 toadd = nopadtext ? toadd : toadd.trim();
21808                 if (!nopad && toadd.length > 80) {
21809                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21810                 }
21811                 innerHTML  += toadd;
21812                 
21813                 i++;
21814                 currentElementChild = currentElement.childNodes.item(i);
21815                 lastNode = '';
21816                 continue;
21817             }
21818             allText = false;
21819             
21820             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21821                 
21822             // Recursively traverse the tree structure of the child node
21823             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21824             lastnode = currentElementChild.nodeName;
21825             i++;
21826             currentElementChild=currentElement.childNodes.item(i);
21827         }
21828         
21829         ret += innerHTML;
21830         
21831         if (!allText) {
21832                 // The remaining code is mostly for formatting the tree
21833             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21834         }
21835         
21836         
21837         if (tagName) {
21838             ret+= "</"+tagName+">";
21839         }
21840         return ret;
21841         
21842     },
21843         
21844     applyBlacklists : function()
21845     {
21846         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21847         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21848         
21849         this.white = [];
21850         this.black = [];
21851         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21852             if (b.indexOf(tag) > -1) {
21853                 return;
21854             }
21855             this.white.push(tag);
21856             
21857         }, this);
21858         
21859         Roo.each(w, function(tag) {
21860             if (b.indexOf(tag) > -1) {
21861                 return;
21862             }
21863             if (this.white.indexOf(tag) > -1) {
21864                 return;
21865             }
21866             this.white.push(tag);
21867             
21868         }, this);
21869         
21870         
21871         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21872             if (w.indexOf(tag) > -1) {
21873                 return;
21874             }
21875             this.black.push(tag);
21876             
21877         }, this);
21878         
21879         Roo.each(b, function(tag) {
21880             if (w.indexOf(tag) > -1) {
21881                 return;
21882             }
21883             if (this.black.indexOf(tag) > -1) {
21884                 return;
21885             }
21886             this.black.push(tag);
21887             
21888         }, this);
21889         
21890         
21891         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21892         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21893         
21894         this.cwhite = [];
21895         this.cblack = [];
21896         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21897             if (b.indexOf(tag) > -1) {
21898                 return;
21899             }
21900             this.cwhite.push(tag);
21901             
21902         }, this);
21903         
21904         Roo.each(w, function(tag) {
21905             if (b.indexOf(tag) > -1) {
21906                 return;
21907             }
21908             if (this.cwhite.indexOf(tag) > -1) {
21909                 return;
21910             }
21911             this.cwhite.push(tag);
21912             
21913         }, this);
21914         
21915         
21916         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21917             if (w.indexOf(tag) > -1) {
21918                 return;
21919             }
21920             this.cblack.push(tag);
21921             
21922         }, this);
21923         
21924         Roo.each(b, function(tag) {
21925             if (w.indexOf(tag) > -1) {
21926                 return;
21927             }
21928             if (this.cblack.indexOf(tag) > -1) {
21929                 return;
21930             }
21931             this.cblack.push(tag);
21932             
21933         }, this);
21934     },
21935     
21936     setStylesheets : function(stylesheets)
21937     {
21938         if(typeof(stylesheets) == 'string'){
21939             Roo.get(this.iframe.contentDocument.head).createChild({
21940                 tag : 'link',
21941                 rel : 'stylesheet',
21942                 type : 'text/css',
21943                 href : stylesheets
21944             });
21945             
21946             return;
21947         }
21948         var _this = this;
21949      
21950         Roo.each(stylesheets, function(s) {
21951             if(!s.length){
21952                 return;
21953             }
21954             
21955             Roo.get(_this.iframe.contentDocument.head).createChild({
21956                 tag : 'link',
21957                 rel : 'stylesheet',
21958                 type : 'text/css',
21959                 href : s
21960             });
21961         });
21962
21963         
21964     },
21965     
21966     removeStylesheets : function()
21967     {
21968         var _this = this;
21969         
21970         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21971             s.remove();
21972         });
21973     },
21974     
21975     setStyle : function(style)
21976     {
21977         Roo.get(this.iframe.contentDocument.head).createChild({
21978             tag : 'style',
21979             type : 'text/css',
21980             html : style
21981         });
21982
21983         return;
21984     }
21985     
21986     // hide stuff that is not compatible
21987     /**
21988      * @event blur
21989      * @hide
21990      */
21991     /**
21992      * @event change
21993      * @hide
21994      */
21995     /**
21996      * @event focus
21997      * @hide
21998      */
21999     /**
22000      * @event specialkey
22001      * @hide
22002      */
22003     /**
22004      * @cfg {String} fieldClass @hide
22005      */
22006     /**
22007      * @cfg {String} focusClass @hide
22008      */
22009     /**
22010      * @cfg {String} autoCreate @hide
22011      */
22012     /**
22013      * @cfg {String} inputType @hide
22014      */
22015     /**
22016      * @cfg {String} invalidClass @hide
22017      */
22018     /**
22019      * @cfg {String} invalidText @hide
22020      */
22021     /**
22022      * @cfg {String} msgFx @hide
22023      */
22024     /**
22025      * @cfg {String} validateOnBlur @hide
22026      */
22027 });
22028
22029 Roo.HtmlEditorCore.white = [
22030         'area', 'br', 'img', 'input', 'hr', 'wbr',
22031         
22032        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22033        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22034        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22035        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22036        'table',   'ul',         'xmp', 
22037        
22038        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22039       'thead',   'tr', 
22040      
22041       'dir', 'menu', 'ol', 'ul', 'dl',
22042        
22043       'embed',  'object'
22044 ];
22045
22046
22047 Roo.HtmlEditorCore.black = [
22048     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22049         'applet', // 
22050         'base',   'basefont', 'bgsound', 'blink',  'body', 
22051         'frame',  'frameset', 'head',    'html',   'ilayer', 
22052         'iframe', 'layer',  'link',     'meta',    'object',   
22053         'script', 'style' ,'title',  'xml' // clean later..
22054 ];
22055 Roo.HtmlEditorCore.clean = [
22056     'script', 'style', 'title', 'xml'
22057 ];
22058 Roo.HtmlEditorCore.remove = [
22059     'font'
22060 ];
22061 // attributes..
22062
22063 Roo.HtmlEditorCore.ablack = [
22064     'on'
22065 ];
22066     
22067 Roo.HtmlEditorCore.aclean = [ 
22068     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22069 ];
22070
22071 // protocols..
22072 Roo.HtmlEditorCore.pwhite= [
22073         'http',  'https',  'mailto'
22074 ];
22075
22076 // white listed style attributes.
22077 Roo.HtmlEditorCore.cwhite= [
22078       //  'text-align', /// default is to allow most things..
22079       
22080          
22081 //        'font-size'//??
22082 ];
22083
22084 // black listed style attributes.
22085 Roo.HtmlEditorCore.cblack= [
22086       //  'font-size' -- this can be set by the project 
22087 ];
22088
22089
22090 Roo.HtmlEditorCore.swapCodes   =[ 
22091     [    8211, "--" ], 
22092     [    8212, "--" ], 
22093     [    8216,  "'" ],  
22094     [    8217, "'" ],  
22095     [    8220, '"' ],  
22096     [    8221, '"' ],  
22097     [    8226, "*" ],  
22098     [    8230, "..." ]
22099 ]; 
22100
22101     //<script type="text/javascript">
22102
22103 /*
22104  * Ext JS Library 1.1.1
22105  * Copyright(c) 2006-2007, Ext JS, LLC.
22106  * Licence LGPL
22107  * 
22108  */
22109  
22110  
22111 Roo.form.HtmlEditor = function(config){
22112     
22113     
22114     
22115     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22116     
22117     if (!this.toolbars) {
22118         this.toolbars = [];
22119     }
22120     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22121     
22122     
22123 };
22124
22125 /**
22126  * @class Roo.form.HtmlEditor
22127  * @extends Roo.form.Field
22128  * Provides a lightweight HTML Editor component.
22129  *
22130  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22131  * 
22132  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22133  * supported by this editor.</b><br/><br/>
22134  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22135  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22136  */
22137 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22138     /**
22139      * @cfg {Boolean} clearUp
22140      */
22141     clearUp : true,
22142       /**
22143      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22144      */
22145     toolbars : false,
22146    
22147      /**
22148      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22149      *                        Roo.resizable.
22150      */
22151     resizable : false,
22152      /**
22153      * @cfg {Number} height (in pixels)
22154      */   
22155     height: 300,
22156    /**
22157      * @cfg {Number} width (in pixels)
22158      */   
22159     width: 500,
22160     
22161     /**
22162      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22163      * 
22164      */
22165     stylesheets: false,
22166     
22167     
22168      /**
22169      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22170      * 
22171      */
22172     cblack: false,
22173     /**
22174      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22175      * 
22176      */
22177     cwhite: false,
22178     
22179      /**
22180      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22181      * 
22182      */
22183     black: false,
22184     /**
22185      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22186      * 
22187      */
22188     white: false,
22189     
22190     // id of frame..
22191     frameId: false,
22192     
22193     // private properties
22194     validationEvent : false,
22195     deferHeight: true,
22196     initialized : false,
22197     activated : false,
22198     
22199     onFocus : Roo.emptyFn,
22200     iframePad:3,
22201     hideMode:'offsets',
22202     
22203     actionMode : 'container', // defaults to hiding it...
22204     
22205     defaultAutoCreate : { // modified by initCompnoent..
22206         tag: "textarea",
22207         style:"width:500px;height:300px;",
22208         autocomplete: "new-password"
22209     },
22210
22211     // private
22212     initComponent : function(){
22213         this.addEvents({
22214             /**
22215              * @event initialize
22216              * Fires when the editor is fully initialized (including the iframe)
22217              * @param {HtmlEditor} this
22218              */
22219             initialize: true,
22220             /**
22221              * @event activate
22222              * Fires when the editor is first receives the focus. Any insertion must wait
22223              * until after this event.
22224              * @param {HtmlEditor} this
22225              */
22226             activate: true,
22227              /**
22228              * @event beforesync
22229              * Fires before the textarea is updated with content from the editor iframe. Return false
22230              * to cancel the sync.
22231              * @param {HtmlEditor} this
22232              * @param {String} html
22233              */
22234             beforesync: true,
22235              /**
22236              * @event beforepush
22237              * Fires before the iframe editor is updated with content from the textarea. Return false
22238              * to cancel the push.
22239              * @param {HtmlEditor} this
22240              * @param {String} html
22241              */
22242             beforepush: true,
22243              /**
22244              * @event sync
22245              * Fires when the textarea is updated with content from the editor iframe.
22246              * @param {HtmlEditor} this
22247              * @param {String} html
22248              */
22249             sync: true,
22250              /**
22251              * @event push
22252              * Fires when the iframe editor is updated with content from the textarea.
22253              * @param {HtmlEditor} this
22254              * @param {String} html
22255              */
22256             push: true,
22257              /**
22258              * @event editmodechange
22259              * Fires when the editor switches edit modes
22260              * @param {HtmlEditor} this
22261              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22262              */
22263             editmodechange: true,
22264             /**
22265              * @event editorevent
22266              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22267              * @param {HtmlEditor} this
22268              */
22269             editorevent: true,
22270             /**
22271              * @event firstfocus
22272              * Fires when on first focus - needed by toolbars..
22273              * @param {HtmlEditor} this
22274              */
22275             firstfocus: true,
22276             /**
22277              * @event autosave
22278              * Auto save the htmlEditor value as a file into Events
22279              * @param {HtmlEditor} this
22280              */
22281             autosave: true,
22282             /**
22283              * @event savedpreview
22284              * preview the saved version of htmlEditor
22285              * @param {HtmlEditor} this
22286              */
22287             savedpreview: true,
22288             
22289             /**
22290             * @event stylesheetsclick
22291             * Fires when press the Sytlesheets button
22292             * @param {Roo.HtmlEditorCore} this
22293             */
22294             stylesheetsclick: true
22295         });
22296         this.defaultAutoCreate =  {
22297             tag: "textarea",
22298             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22299             autocomplete: "new-password"
22300         };
22301     },
22302
22303     /**
22304      * Protected method that will not generally be called directly. It
22305      * is called when the editor creates its toolbar. Override this method if you need to
22306      * add custom toolbar buttons.
22307      * @param {HtmlEditor} editor
22308      */
22309     createToolbar : function(editor){
22310         Roo.log("create toolbars");
22311         if (!editor.toolbars || !editor.toolbars.length) {
22312             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22313         }
22314         
22315         for (var i =0 ; i < editor.toolbars.length;i++) {
22316             editor.toolbars[i] = Roo.factory(
22317                     typeof(editor.toolbars[i]) == 'string' ?
22318                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22319                 Roo.form.HtmlEditor);
22320             editor.toolbars[i].init(editor);
22321         }
22322          
22323         
22324     },
22325
22326      
22327     // private
22328     onRender : function(ct, position)
22329     {
22330         var _t = this;
22331         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22332         
22333         this.wrap = this.el.wrap({
22334             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22335         });
22336         
22337         this.editorcore.onRender(ct, position);
22338          
22339         if (this.resizable) {
22340             this.resizeEl = new Roo.Resizable(this.wrap, {
22341                 pinned : true,
22342                 wrap: true,
22343                 dynamic : true,
22344                 minHeight : this.height,
22345                 height: this.height,
22346                 handles : this.resizable,
22347                 width: this.width,
22348                 listeners : {
22349                     resize : function(r, w, h) {
22350                         _t.onResize(w,h); // -something
22351                     }
22352                 }
22353             });
22354             
22355         }
22356         this.createToolbar(this);
22357        
22358         
22359         if(!this.width){
22360             this.setSize(this.wrap.getSize());
22361         }
22362         if (this.resizeEl) {
22363             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22364             // should trigger onReize..
22365         }
22366         
22367         this.keyNav = new Roo.KeyNav(this.el, {
22368             
22369             "tab" : function(e){
22370                 e.preventDefault();
22371                 
22372                 var value = this.getValue();
22373                 
22374                 var start = this.el.dom.selectionStart;
22375                 var end = this.el.dom.selectionEnd;
22376                 
22377                 if(!e.shiftKey){
22378                     
22379                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22380                     this.el.dom.setSelectionRange(end + 1, end + 1);
22381                     return;
22382                 }
22383                 
22384                 var f = value.substring(0, start).split("\t");
22385                 
22386                 if(f.pop().length != 0){
22387                     return;
22388                 }
22389                 
22390                 this.setValue(f.join("\t") + value.substring(end));
22391                 this.el.dom.setSelectionRange(start - 1, start - 1);
22392                 
22393             },
22394             
22395             "home" : function(e){
22396                 e.preventDefault();
22397                 
22398                 var curr = this.el.dom.selectionStart;
22399                 var lines = this.getValue().split("\n");
22400                 
22401                 if(!lines.length){
22402                     return;
22403                 }
22404                 
22405                 if(e.ctrlKey){
22406                     this.el.dom.setSelectionRange(0, 0);
22407                     return;
22408                 }
22409                 
22410                 var pos = 0;
22411                 
22412                 for (var i = 0; i < lines.length;i++) {
22413                     pos += lines[i].length;
22414                     
22415                     if(i != 0){
22416                         pos += 1;
22417                     }
22418                     
22419                     if(pos < curr){
22420                         continue;
22421                     }
22422                     
22423                     pos -= lines[i].length;
22424                     
22425                     break;
22426                 }
22427                 
22428                 if(!e.shiftKey){
22429                     this.el.dom.setSelectionRange(pos, pos);
22430                     return;
22431                 }
22432                 
22433                 this.el.dom.selectionStart = pos;
22434                 this.el.dom.selectionEnd = curr;
22435             },
22436             
22437             "end" : function(e){
22438                 e.preventDefault();
22439                 
22440                 var curr = this.el.dom.selectionStart;
22441                 var lines = this.getValue().split("\n");
22442                 
22443                 if(!lines.length){
22444                     return;
22445                 }
22446                 
22447                 if(e.ctrlKey){
22448                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22449                     return;
22450                 }
22451                 
22452                 var pos = 0;
22453                 
22454                 for (var i = 0; i < lines.length;i++) {
22455                     
22456                     pos += lines[i].length;
22457                     
22458                     if(i != 0){
22459                         pos += 1;
22460                     }
22461                     
22462                     if(pos < curr){
22463                         continue;
22464                     }
22465                     
22466                     break;
22467                 }
22468                 
22469                 if(!e.shiftKey){
22470                     this.el.dom.setSelectionRange(pos, pos);
22471                     return;
22472                 }
22473                 
22474                 this.el.dom.selectionStart = curr;
22475                 this.el.dom.selectionEnd = pos;
22476             },
22477
22478             scope : this,
22479
22480             doRelay : function(foo, bar, hname){
22481                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22482             },
22483
22484             forceKeyDown: true
22485         });
22486         
22487 //        if(this.autosave && this.w){
22488 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22489 //        }
22490     },
22491
22492     // private
22493     onResize : function(w, h)
22494     {
22495         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22496         var ew = false;
22497         var eh = false;
22498         
22499         if(this.el ){
22500             if(typeof w == 'number'){
22501                 var aw = w - this.wrap.getFrameWidth('lr');
22502                 this.el.setWidth(this.adjustWidth('textarea', aw));
22503                 ew = aw;
22504             }
22505             if(typeof h == 'number'){
22506                 var tbh = 0;
22507                 for (var i =0; i < this.toolbars.length;i++) {
22508                     // fixme - ask toolbars for heights?
22509                     tbh += this.toolbars[i].tb.el.getHeight();
22510                     if (this.toolbars[i].footer) {
22511                         tbh += this.toolbars[i].footer.el.getHeight();
22512                     }
22513                 }
22514                 
22515                 
22516                 
22517                 
22518                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22519                 ah -= 5; // knock a few pixes off for look..
22520 //                Roo.log(ah);
22521                 this.el.setHeight(this.adjustWidth('textarea', ah));
22522                 var eh = ah;
22523             }
22524         }
22525         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22526         this.editorcore.onResize(ew,eh);
22527         
22528     },
22529
22530     /**
22531      * Toggles the editor between standard and source edit mode.
22532      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22533      */
22534     toggleSourceEdit : function(sourceEditMode)
22535     {
22536         this.editorcore.toggleSourceEdit(sourceEditMode);
22537         
22538         if(this.editorcore.sourceEditMode){
22539             Roo.log('editor - showing textarea');
22540             
22541 //            Roo.log('in');
22542 //            Roo.log(this.syncValue());
22543             this.editorcore.syncValue();
22544             this.el.removeClass('x-hidden');
22545             this.el.dom.removeAttribute('tabIndex');
22546             this.el.focus();
22547             
22548             for (var i = 0; i < this.toolbars.length; i++) {
22549                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22550                     this.toolbars[i].tb.hide();
22551                     this.toolbars[i].footer.hide();
22552                 }
22553             }
22554             
22555         }else{
22556             Roo.log('editor - hiding textarea');
22557 //            Roo.log('out')
22558 //            Roo.log(this.pushValue()); 
22559             this.editorcore.pushValue();
22560             
22561             this.el.addClass('x-hidden');
22562             this.el.dom.setAttribute('tabIndex', -1);
22563             
22564             for (var i = 0; i < this.toolbars.length; i++) {
22565                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22566                     this.toolbars[i].tb.show();
22567                     this.toolbars[i].footer.show();
22568                 }
22569             }
22570             
22571             //this.deferFocus();
22572         }
22573         
22574         this.setSize(this.wrap.getSize());
22575         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22576         
22577         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22578     },
22579  
22580     // private (for BoxComponent)
22581     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22582
22583     // private (for BoxComponent)
22584     getResizeEl : function(){
22585         return this.wrap;
22586     },
22587
22588     // private (for BoxComponent)
22589     getPositionEl : function(){
22590         return this.wrap;
22591     },
22592
22593     // private
22594     initEvents : function(){
22595         this.originalValue = this.getValue();
22596     },
22597
22598     /**
22599      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22600      * @method
22601      */
22602     markInvalid : Roo.emptyFn,
22603     /**
22604      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22605      * @method
22606      */
22607     clearInvalid : Roo.emptyFn,
22608
22609     setValue : function(v){
22610         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22611         this.editorcore.pushValue();
22612     },
22613
22614      
22615     // private
22616     deferFocus : function(){
22617         this.focus.defer(10, this);
22618     },
22619
22620     // doc'ed in Field
22621     focus : function(){
22622         this.editorcore.focus();
22623         
22624     },
22625       
22626
22627     // private
22628     onDestroy : function(){
22629         
22630         
22631         
22632         if(this.rendered){
22633             
22634             for (var i =0; i < this.toolbars.length;i++) {
22635                 // fixme - ask toolbars for heights?
22636                 this.toolbars[i].onDestroy();
22637             }
22638             
22639             this.wrap.dom.innerHTML = '';
22640             this.wrap.remove();
22641         }
22642     },
22643
22644     // private
22645     onFirstFocus : function(){
22646         //Roo.log("onFirstFocus");
22647         this.editorcore.onFirstFocus();
22648          for (var i =0; i < this.toolbars.length;i++) {
22649             this.toolbars[i].onFirstFocus();
22650         }
22651         
22652     },
22653     
22654     // private
22655     syncValue : function()
22656     {
22657         this.editorcore.syncValue();
22658     },
22659     
22660     pushValue : function()
22661     {
22662         this.editorcore.pushValue();
22663     },
22664     
22665     setStylesheets : function(stylesheets)
22666     {
22667         this.editorcore.setStylesheets(stylesheets);
22668     },
22669     
22670     removeStylesheets : function()
22671     {
22672         this.editorcore.removeStylesheets();
22673     }
22674      
22675     
22676     // hide stuff that is not compatible
22677     /**
22678      * @event blur
22679      * @hide
22680      */
22681     /**
22682      * @event change
22683      * @hide
22684      */
22685     /**
22686      * @event focus
22687      * @hide
22688      */
22689     /**
22690      * @event specialkey
22691      * @hide
22692      */
22693     /**
22694      * @cfg {String} fieldClass @hide
22695      */
22696     /**
22697      * @cfg {String} focusClass @hide
22698      */
22699     /**
22700      * @cfg {String} autoCreate @hide
22701      */
22702     /**
22703      * @cfg {String} inputType @hide
22704      */
22705     /**
22706      * @cfg {String} invalidClass @hide
22707      */
22708     /**
22709      * @cfg {String} invalidText @hide
22710      */
22711     /**
22712      * @cfg {String} msgFx @hide
22713      */
22714     /**
22715      * @cfg {String} validateOnBlur @hide
22716      */
22717 });
22718  
22719     // <script type="text/javascript">
22720 /*
22721  * Based on
22722  * Ext JS Library 1.1.1
22723  * Copyright(c) 2006-2007, Ext JS, LLC.
22724  *  
22725  
22726  */
22727
22728 /**
22729  * @class Roo.form.HtmlEditorToolbar1
22730  * Basic Toolbar
22731  * 
22732  * Usage:
22733  *
22734  new Roo.form.HtmlEditor({
22735     ....
22736     toolbars : [
22737         new Roo.form.HtmlEditorToolbar1({
22738             disable : { fonts: 1 , format: 1, ..., ... , ...],
22739             btns : [ .... ]
22740         })
22741     }
22742      
22743  * 
22744  * @cfg {Object} disable List of elements to disable..
22745  * @cfg {Array} btns List of additional buttons.
22746  * 
22747  * 
22748  * NEEDS Extra CSS? 
22749  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22750  */
22751  
22752 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22753 {
22754     
22755     Roo.apply(this, config);
22756     
22757     // default disabled, based on 'good practice'..
22758     this.disable = this.disable || {};
22759     Roo.applyIf(this.disable, {
22760         fontSize : true,
22761         colors : true,
22762         specialElements : true
22763     });
22764     
22765     
22766     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22767     // dont call parent... till later.
22768 }
22769
22770 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22771     
22772     tb: false,
22773     
22774     rendered: false,
22775     
22776     editor : false,
22777     editorcore : false,
22778     /**
22779      * @cfg {Object} disable  List of toolbar elements to disable
22780          
22781      */
22782     disable : false,
22783     
22784     
22785      /**
22786      * @cfg {String} createLinkText The default text for the create link prompt
22787      */
22788     createLinkText : 'Please enter the URL for the link:',
22789     /**
22790      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22791      */
22792     defaultLinkValue : 'http:/'+'/',
22793    
22794     
22795       /**
22796      * @cfg {Array} fontFamilies An array of available font families
22797      */
22798     fontFamilies : [
22799         'Arial',
22800         'Courier New',
22801         'Tahoma',
22802         'Times New Roman',
22803         'Verdana'
22804     ],
22805     
22806     specialChars : [
22807            "&#169;",
22808           "&#174;",     
22809           "&#8482;",    
22810           "&#163;" ,    
22811          // "&#8212;",    
22812           "&#8230;",    
22813           "&#247;" ,    
22814         //  "&#225;" ,     ?? a acute?
22815            "&#8364;"    , //Euro
22816        //   "&#8220;"    ,
22817         //  "&#8221;"    ,
22818         //  "&#8226;"    ,
22819           "&#176;"  //   , // degrees
22820
22821          // "&#233;"     , // e ecute
22822          // "&#250;"     , // u ecute?
22823     ],
22824     
22825     specialElements : [
22826         {
22827             text: "Insert Table",
22828             xtype: 'MenuItem',
22829             xns : Roo.Menu,
22830             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22831                 
22832         },
22833         {    
22834             text: "Insert Image",
22835             xtype: 'MenuItem',
22836             xns : Roo.Menu,
22837             ihtml : '<img src="about:blank"/>'
22838             
22839         }
22840         
22841          
22842     ],
22843     
22844     
22845     inputElements : [ 
22846             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22847             "input:submit", "input:button", "select", "textarea", "label" ],
22848     formats : [
22849         ["p"] ,  
22850         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22851         ["pre"],[ "code"], 
22852         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22853         ['div'],['span']
22854     ],
22855     
22856     cleanStyles : [
22857         "font-size"
22858     ],
22859      /**
22860      * @cfg {String} defaultFont default font to use.
22861      */
22862     defaultFont: 'tahoma',
22863    
22864     fontSelect : false,
22865     
22866     
22867     formatCombo : false,
22868     
22869     init : function(editor)
22870     {
22871         this.editor = editor;
22872         this.editorcore = editor.editorcore ? editor.editorcore : editor;
22873         var editorcore = this.editorcore;
22874         
22875         var _t = this;
22876         
22877         var fid = editorcore.frameId;
22878         var etb = this;
22879         function btn(id, toggle, handler){
22880             var xid = fid + '-'+ id ;
22881             return {
22882                 id : xid,
22883                 cmd : id,
22884                 cls : 'x-btn-icon x-edit-'+id,
22885                 enableToggle:toggle !== false,
22886                 scope: _t, // was editor...
22887                 handler:handler||_t.relayBtnCmd,
22888                 clickEvent:'mousedown',
22889                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22890                 tabIndex:-1
22891             };
22892         }
22893         
22894         
22895         
22896         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
22897         this.tb = tb;
22898          // stop form submits
22899         tb.el.on('click', function(e){
22900             e.preventDefault(); // what does this do?
22901         });
22902
22903         if(!this.disable.font) { // && !Roo.isSafari){
22904             /* why no safari for fonts 
22905             editor.fontSelect = tb.el.createChild({
22906                 tag:'select',
22907                 tabIndex: -1,
22908                 cls:'x-font-select',
22909                 html: this.createFontOptions()
22910             });
22911             
22912             editor.fontSelect.on('change', function(){
22913                 var font = editor.fontSelect.dom.value;
22914                 editor.relayCmd('fontname', font);
22915                 editor.deferFocus();
22916             }, editor);
22917             
22918             tb.add(
22919                 editor.fontSelect.dom,
22920                 '-'
22921             );
22922             */
22923             
22924         };
22925         if(!this.disable.formats){
22926             this.formatCombo = new Roo.form.ComboBox({
22927                 store: new Roo.data.SimpleStore({
22928                     id : 'tag',
22929                     fields: ['tag'],
22930                     data : this.formats // from states.js
22931                 }),
22932                 blockFocus : true,
22933                 name : '',
22934                 //autoCreate : {tag: "div",  size: "20"},
22935                 displayField:'tag',
22936                 typeAhead: false,
22937                 mode: 'local',
22938                 editable : false,
22939                 triggerAction: 'all',
22940                 emptyText:'Add tag',
22941                 selectOnFocus:true,
22942                 width:135,
22943                 listeners : {
22944                     'select': function(c, r, i) {
22945                         editorcore.insertTag(r.get('tag'));
22946                         editor.focus();
22947                     }
22948                 }
22949
22950             });
22951             tb.addField(this.formatCombo);
22952             
22953         }
22954         
22955         if(!this.disable.format){
22956             tb.add(
22957                 btn('bold'),
22958                 btn('italic'),
22959                 btn('underline'),
22960                 btn('strikethrough')
22961             );
22962         };
22963         if(!this.disable.fontSize){
22964             tb.add(
22965                 '-',
22966                 
22967                 
22968                 btn('increasefontsize', false, editorcore.adjustFont),
22969                 btn('decreasefontsize', false, editorcore.adjustFont)
22970             );
22971         };
22972         
22973         
22974         if(!this.disable.colors){
22975             tb.add(
22976                 '-', {
22977                     id:editorcore.frameId +'-forecolor',
22978                     cls:'x-btn-icon x-edit-forecolor',
22979                     clickEvent:'mousedown',
22980                     tooltip: this.buttonTips['forecolor'] || undefined,
22981                     tabIndex:-1,
22982                     menu : new Roo.menu.ColorMenu({
22983                         allowReselect: true,
22984                         focus: Roo.emptyFn,
22985                         value:'000000',
22986                         plain:true,
22987                         selectHandler: function(cp, color){
22988                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
22989                             editor.deferFocus();
22990                         },
22991                         scope: editorcore,
22992                         clickEvent:'mousedown'
22993                     })
22994                 }, {
22995                     id:editorcore.frameId +'backcolor',
22996                     cls:'x-btn-icon x-edit-backcolor',
22997                     clickEvent:'mousedown',
22998                     tooltip: this.buttonTips['backcolor'] || undefined,
22999                     tabIndex:-1,
23000                     menu : new Roo.menu.ColorMenu({
23001                         focus: Roo.emptyFn,
23002                         value:'FFFFFF',
23003                         plain:true,
23004                         allowReselect: true,
23005                         selectHandler: function(cp, color){
23006                             if(Roo.isGecko){
23007                                 editorcore.execCmd('useCSS', false);
23008                                 editorcore.execCmd('hilitecolor', color);
23009                                 editorcore.execCmd('useCSS', true);
23010                                 editor.deferFocus();
23011                             }else{
23012                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23013                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23014                                 editor.deferFocus();
23015                             }
23016                         },
23017                         scope:editorcore,
23018                         clickEvent:'mousedown'
23019                     })
23020                 }
23021             );
23022         };
23023         // now add all the items...
23024         
23025
23026         if(!this.disable.alignments){
23027             tb.add(
23028                 '-',
23029                 btn('justifyleft'),
23030                 btn('justifycenter'),
23031                 btn('justifyright')
23032             );
23033         };
23034
23035         //if(!Roo.isSafari){
23036             if(!this.disable.links){
23037                 tb.add(
23038                     '-',
23039                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23040                 );
23041             };
23042
23043             if(!this.disable.lists){
23044                 tb.add(
23045                     '-',
23046                     btn('insertorderedlist'),
23047                     btn('insertunorderedlist')
23048                 );
23049             }
23050             if(!this.disable.sourceEdit){
23051                 tb.add(
23052                     '-',
23053                     btn('sourceedit', true, function(btn){
23054                         this.toggleSourceEdit(btn.pressed);
23055                     })
23056                 );
23057             }
23058         //}
23059         
23060         var smenu = { };
23061         // special menu.. - needs to be tidied up..
23062         if (!this.disable.special) {
23063             smenu = {
23064                 text: "&#169;",
23065                 cls: 'x-edit-none',
23066                 
23067                 menu : {
23068                     items : []
23069                 }
23070             };
23071             for (var i =0; i < this.specialChars.length; i++) {
23072                 smenu.menu.items.push({
23073                     
23074                     html: this.specialChars[i],
23075                     handler: function(a,b) {
23076                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23077                         //editor.insertAtCursor(a.html);
23078                         
23079                     },
23080                     tabIndex:-1
23081                 });
23082             }
23083             
23084             
23085             tb.add(smenu);
23086             
23087             
23088         }
23089         
23090         var cmenu = { };
23091         if (!this.disable.cleanStyles) {
23092             cmenu = {
23093                 cls: 'x-btn-icon x-btn-clear',
23094                 
23095                 menu : {
23096                     items : []
23097                 }
23098             };
23099             for (var i =0; i < this.cleanStyles.length; i++) {
23100                 cmenu.menu.items.push({
23101                     actiontype : this.cleanStyles[i],
23102                     html: 'Remove ' + this.cleanStyles[i],
23103                     handler: function(a,b) {
23104 //                        Roo.log(a);
23105 //                        Roo.log(b);
23106                         var c = Roo.get(editorcore.doc.body);
23107                         c.select('[style]').each(function(s) {
23108                             s.dom.style.removeProperty(a.actiontype);
23109                         });
23110                         editorcore.syncValue();
23111                     },
23112                     tabIndex:-1
23113                 });
23114             }
23115              cmenu.menu.items.push({
23116                 actiontype : 'tablewidths',
23117                 html: 'Remove Table Widths',
23118                 handler: function(a,b) {
23119                     editorcore.cleanTableWidths();
23120                     editorcore.syncValue();
23121                 },
23122                 tabIndex:-1
23123             });
23124             cmenu.menu.items.push({
23125                 actiontype : 'word',
23126                 html: 'Remove MS Word Formating',
23127                 handler: function(a,b) {
23128                     editorcore.cleanWord();
23129                     editorcore.syncValue();
23130                 },
23131                 tabIndex:-1
23132             });
23133             
23134             cmenu.menu.items.push({
23135                 actiontype : 'all',
23136                 html: 'Remove All Styles',
23137                 handler: function(a,b) {
23138                     
23139                     var c = Roo.get(editorcore.doc.body);
23140                     c.select('[style]').each(function(s) {
23141                         s.dom.removeAttribute('style');
23142                     });
23143                     editorcore.syncValue();
23144                 },
23145                 tabIndex:-1
23146             });
23147             
23148             cmenu.menu.items.push({
23149                 actiontype : 'all',
23150                 html: 'Remove All CSS Classes',
23151                 handler: function(a,b) {
23152                     
23153                     var c = Roo.get(editorcore.doc.body);
23154                     c.select('[class]').each(function(s) {
23155                         s.dom.className = '';
23156                     });
23157                     editorcore.syncValue();
23158                 },
23159                 tabIndex:-1
23160             });
23161             
23162              cmenu.menu.items.push({
23163                 actiontype : 'tidy',
23164                 html: 'Tidy HTML Source',
23165                 handler: function(a,b) {
23166                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23167                     editorcore.syncValue();
23168                 },
23169                 tabIndex:-1
23170             });
23171             
23172             
23173             tb.add(cmenu);
23174         }
23175          
23176         if (!this.disable.specialElements) {
23177             var semenu = {
23178                 text: "Other;",
23179                 cls: 'x-edit-none',
23180                 menu : {
23181                     items : []
23182                 }
23183             };
23184             for (var i =0; i < this.specialElements.length; i++) {
23185                 semenu.menu.items.push(
23186                     Roo.apply({ 
23187                         handler: function(a,b) {
23188                             editor.insertAtCursor(this.ihtml);
23189                         }
23190                     }, this.specialElements[i])
23191                 );
23192                     
23193             }
23194             
23195             tb.add(semenu);
23196             
23197             
23198         }
23199          
23200         
23201         if (this.btns) {
23202             for(var i =0; i< this.btns.length;i++) {
23203                 var b = Roo.factory(this.btns[i],Roo.form);
23204                 b.cls =  'x-edit-none';
23205                 
23206                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23207                     b.cls += ' x-init-enable';
23208                 }
23209                 
23210                 b.scope = editorcore;
23211                 tb.add(b);
23212             }
23213         
23214         }
23215         
23216         
23217         
23218         // disable everything...
23219         
23220         this.tb.items.each(function(item){
23221             
23222            if(
23223                 item.id != editorcore.frameId+ '-sourceedit' && 
23224                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23225             ){
23226                 
23227                 item.disable();
23228             }
23229         });
23230         this.rendered = true;
23231         
23232         // the all the btns;
23233         editor.on('editorevent', this.updateToolbar, this);
23234         // other toolbars need to implement this..
23235         //editor.on('editmodechange', this.updateToolbar, this);
23236     },
23237     
23238     
23239     relayBtnCmd : function(btn) {
23240         this.editorcore.relayCmd(btn.cmd);
23241     },
23242     // private used internally
23243     createLink : function(){
23244         Roo.log("create link?");
23245         var url = prompt(this.createLinkText, this.defaultLinkValue);
23246         if(url && url != 'http:/'+'/'){
23247             this.editorcore.relayCmd('createlink', url);
23248         }
23249     },
23250
23251     
23252     /**
23253      * Protected method that will not generally be called directly. It triggers
23254      * a toolbar update by reading the markup state of the current selection in the editor.
23255      */
23256     updateToolbar: function(){
23257
23258         if(!this.editorcore.activated){
23259             this.editor.onFirstFocus();
23260             return;
23261         }
23262
23263         var btns = this.tb.items.map, 
23264             doc = this.editorcore.doc,
23265             frameId = this.editorcore.frameId;
23266
23267         if(!this.disable.font && !Roo.isSafari){
23268             /*
23269             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23270             if(name != this.fontSelect.dom.value){
23271                 this.fontSelect.dom.value = name;
23272             }
23273             */
23274         }
23275         if(!this.disable.format){
23276             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23277             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23278             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23279             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23280         }
23281         if(!this.disable.alignments){
23282             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23283             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23284             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23285         }
23286         if(!Roo.isSafari && !this.disable.lists){
23287             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23288             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23289         }
23290         
23291         var ans = this.editorcore.getAllAncestors();
23292         if (this.formatCombo) {
23293             
23294             
23295             var store = this.formatCombo.store;
23296             this.formatCombo.setValue("");
23297             for (var i =0; i < ans.length;i++) {
23298                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23299                     // select it..
23300                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23301                     break;
23302                 }
23303             }
23304         }
23305         
23306         
23307         
23308         // hides menus... - so this cant be on a menu...
23309         Roo.menu.MenuMgr.hideAll();
23310
23311         //this.editorsyncValue();
23312     },
23313    
23314     
23315     createFontOptions : function(){
23316         var buf = [], fs = this.fontFamilies, ff, lc;
23317         
23318         
23319         
23320         for(var i = 0, len = fs.length; i< len; i++){
23321             ff = fs[i];
23322             lc = ff.toLowerCase();
23323             buf.push(
23324                 '<option value="',lc,'" style="font-family:',ff,';"',
23325                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23326                     ff,
23327                 '</option>'
23328             );
23329         }
23330         return buf.join('');
23331     },
23332     
23333     toggleSourceEdit : function(sourceEditMode){
23334         
23335         Roo.log("toolbar toogle");
23336         if(sourceEditMode === undefined){
23337             sourceEditMode = !this.sourceEditMode;
23338         }
23339         this.sourceEditMode = sourceEditMode === true;
23340         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23341         // just toggle the button?
23342         if(btn.pressed !== this.sourceEditMode){
23343             btn.toggle(this.sourceEditMode);
23344             return;
23345         }
23346         
23347         if(sourceEditMode){
23348             Roo.log("disabling buttons");
23349             this.tb.items.each(function(item){
23350                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23351                     item.disable();
23352                 }
23353             });
23354           
23355         }else{
23356             Roo.log("enabling buttons");
23357             if(this.editorcore.initialized){
23358                 this.tb.items.each(function(item){
23359                     item.enable();
23360                 });
23361             }
23362             
23363         }
23364         Roo.log("calling toggole on editor");
23365         // tell the editor that it's been pressed..
23366         this.editor.toggleSourceEdit(sourceEditMode);
23367        
23368     },
23369      /**
23370      * Object collection of toolbar tooltips for the buttons in the editor. The key
23371      * is the command id associated with that button and the value is a valid QuickTips object.
23372      * For example:
23373 <pre><code>
23374 {
23375     bold : {
23376         title: 'Bold (Ctrl+B)',
23377         text: 'Make the selected text bold.',
23378         cls: 'x-html-editor-tip'
23379     },
23380     italic : {
23381         title: 'Italic (Ctrl+I)',
23382         text: 'Make the selected text italic.',
23383         cls: 'x-html-editor-tip'
23384     },
23385     ...
23386 </code></pre>
23387     * @type Object
23388      */
23389     buttonTips : {
23390         bold : {
23391             title: 'Bold (Ctrl+B)',
23392             text: 'Make the selected text bold.',
23393             cls: 'x-html-editor-tip'
23394         },
23395         italic : {
23396             title: 'Italic (Ctrl+I)',
23397             text: 'Make the selected text italic.',
23398             cls: 'x-html-editor-tip'
23399         },
23400         underline : {
23401             title: 'Underline (Ctrl+U)',
23402             text: 'Underline the selected text.',
23403             cls: 'x-html-editor-tip'
23404         },
23405         strikethrough : {
23406             title: 'Strikethrough',
23407             text: 'Strikethrough the selected text.',
23408             cls: 'x-html-editor-tip'
23409         },
23410         increasefontsize : {
23411             title: 'Grow Text',
23412             text: 'Increase the font size.',
23413             cls: 'x-html-editor-tip'
23414         },
23415         decreasefontsize : {
23416             title: 'Shrink Text',
23417             text: 'Decrease the font size.',
23418             cls: 'x-html-editor-tip'
23419         },
23420         backcolor : {
23421             title: 'Text Highlight Color',
23422             text: 'Change the background color of the selected text.',
23423             cls: 'x-html-editor-tip'
23424         },
23425         forecolor : {
23426             title: 'Font Color',
23427             text: 'Change the color of the selected text.',
23428             cls: 'x-html-editor-tip'
23429         },
23430         justifyleft : {
23431             title: 'Align Text Left',
23432             text: 'Align text to the left.',
23433             cls: 'x-html-editor-tip'
23434         },
23435         justifycenter : {
23436             title: 'Center Text',
23437             text: 'Center text in the editor.',
23438             cls: 'x-html-editor-tip'
23439         },
23440         justifyright : {
23441             title: 'Align Text Right',
23442             text: 'Align text to the right.',
23443             cls: 'x-html-editor-tip'
23444         },
23445         insertunorderedlist : {
23446             title: 'Bullet List',
23447             text: 'Start a bulleted list.',
23448             cls: 'x-html-editor-tip'
23449         },
23450         insertorderedlist : {
23451             title: 'Numbered List',
23452             text: 'Start a numbered list.',
23453             cls: 'x-html-editor-tip'
23454         },
23455         createlink : {
23456             title: 'Hyperlink',
23457             text: 'Make the selected text a hyperlink.',
23458             cls: 'x-html-editor-tip'
23459         },
23460         sourceedit : {
23461             title: 'Source Edit',
23462             text: 'Switch to source editing mode.',
23463             cls: 'x-html-editor-tip'
23464         }
23465     },
23466     // private
23467     onDestroy : function(){
23468         if(this.rendered){
23469             
23470             this.tb.items.each(function(item){
23471                 if(item.menu){
23472                     item.menu.removeAll();
23473                     if(item.menu.el){
23474                         item.menu.el.destroy();
23475                     }
23476                 }
23477                 item.destroy();
23478             });
23479              
23480         }
23481     },
23482     onFirstFocus: function() {
23483         this.tb.items.each(function(item){
23484            item.enable();
23485         });
23486     }
23487 });
23488
23489
23490
23491
23492 // <script type="text/javascript">
23493 /*
23494  * Based on
23495  * Ext JS Library 1.1.1
23496  * Copyright(c) 2006-2007, Ext JS, LLC.
23497  *  
23498  
23499  */
23500
23501  
23502 /**
23503  * @class Roo.form.HtmlEditor.ToolbarContext
23504  * Context Toolbar
23505  * 
23506  * Usage:
23507  *
23508  new Roo.form.HtmlEditor({
23509     ....
23510     toolbars : [
23511         { xtype: 'ToolbarStandard', styles : {} }
23512         { xtype: 'ToolbarContext', disable : {} }
23513     ]
23514 })
23515
23516      
23517  * 
23518  * @config : {Object} disable List of elements to disable.. (not done yet.)
23519  * @config : {Object} styles  Map of styles available.
23520  * 
23521  */
23522
23523 Roo.form.HtmlEditor.ToolbarContext = function(config)
23524 {
23525     
23526     Roo.apply(this, config);
23527     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23528     // dont call parent... till later.
23529     this.styles = this.styles || {};
23530 }
23531
23532  
23533
23534 Roo.form.HtmlEditor.ToolbarContext.types = {
23535     'IMG' : {
23536         width : {
23537             title: "Width",
23538             width: 40
23539         },
23540         height:  {
23541             title: "Height",
23542             width: 40
23543         },
23544         align: {
23545             title: "Align",
23546             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23547             width : 80
23548             
23549         },
23550         border: {
23551             title: "Border",
23552             width: 40
23553         },
23554         alt: {
23555             title: "Alt",
23556             width: 120
23557         },
23558         src : {
23559             title: "Src",
23560             width: 220
23561         }
23562         
23563     },
23564     'A' : {
23565         name : {
23566             title: "Name",
23567             width: 50
23568         },
23569         target:  {
23570             title: "Target",
23571             width: 120
23572         },
23573         href:  {
23574             title: "Href",
23575             width: 220
23576         } // border?
23577         
23578     },
23579     'TABLE' : {
23580         rows : {
23581             title: "Rows",
23582             width: 20
23583         },
23584         cols : {
23585             title: "Cols",
23586             width: 20
23587         },
23588         width : {
23589             title: "Width",
23590             width: 40
23591         },
23592         height : {
23593             title: "Height",
23594             width: 40
23595         },
23596         border : {
23597             title: "Border",
23598             width: 20
23599         }
23600     },
23601     'TD' : {
23602         width : {
23603             title: "Width",
23604             width: 40
23605         },
23606         height : {
23607             title: "Height",
23608             width: 40
23609         },   
23610         align: {
23611             title: "Align",
23612             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23613             width: 80
23614         },
23615         valign: {
23616             title: "Valign",
23617             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23618             width: 80
23619         },
23620         colspan: {
23621             title: "Colspan",
23622             width: 20
23623             
23624         },
23625          'font-family'  : {
23626             title : "Font",
23627             style : 'fontFamily',
23628             displayField: 'display',
23629             optname : 'font-family',
23630             width: 140
23631         }
23632     },
23633     'INPUT' : {
23634         name : {
23635             title: "name",
23636             width: 120
23637         },
23638         value : {
23639             title: "Value",
23640             width: 120
23641         },
23642         width : {
23643             title: "Width",
23644             width: 40
23645         }
23646     },
23647     'LABEL' : {
23648         'for' : {
23649             title: "For",
23650             width: 120
23651         }
23652     },
23653     'TEXTAREA' : {
23654           name : {
23655             title: "name",
23656             width: 120
23657         },
23658         rows : {
23659             title: "Rows",
23660             width: 20
23661         },
23662         cols : {
23663             title: "Cols",
23664             width: 20
23665         }
23666     },
23667     'SELECT' : {
23668         name : {
23669             title: "name",
23670             width: 120
23671         },
23672         selectoptions : {
23673             title: "Options",
23674             width: 200
23675         }
23676     },
23677     
23678     // should we really allow this??
23679     // should this just be 
23680     'BODY' : {
23681         title : {
23682             title: "Title",
23683             width: 200,
23684             disabled : true
23685         }
23686     },
23687     'SPAN' : {
23688         'font-family'  : {
23689             title : "Font",
23690             style : 'fontFamily',
23691             displayField: 'display',
23692             optname : 'font-family',
23693             width: 140
23694         }
23695     },
23696     'DIV' : {
23697         'font-family'  : {
23698             title : "Font",
23699             style : 'fontFamily',
23700             displayField: 'display',
23701             optname : 'font-family',
23702             width: 140
23703         }
23704     },
23705      'P' : {
23706         'font-family'  : {
23707             title : "Font",
23708             style : 'fontFamily',
23709             displayField: 'display',
23710             optname : 'font-family',
23711             width: 140
23712         }
23713     },
23714     
23715     '*' : {
23716         // empty..
23717     }
23718
23719 };
23720
23721 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23722 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23723
23724 Roo.form.HtmlEditor.ToolbarContext.options = {
23725         'font-family'  : [ 
23726                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23727                 [ 'Courier New', 'Courier New'],
23728                 [ 'Tahoma', 'Tahoma'],
23729                 [ 'Times New Roman,serif', 'Times'],
23730                 [ 'Verdana','Verdana' ]
23731         ]
23732 };
23733
23734 // fixme - these need to be configurable..
23735  
23736
23737 //Roo.form.HtmlEditor.ToolbarContext.types
23738
23739
23740 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23741     
23742     tb: false,
23743     
23744     rendered: false,
23745     
23746     editor : false,
23747     editorcore : false,
23748     /**
23749      * @cfg {Object} disable  List of toolbar elements to disable
23750          
23751      */
23752     disable : false,
23753     /**
23754      * @cfg {Object} styles List of styles 
23755      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23756      *
23757      * These must be defined in the page, so they get rendered correctly..
23758      * .headline { }
23759      * TD.underline { }
23760      * 
23761      */
23762     styles : false,
23763     
23764     options: false,
23765     
23766     toolbars : false,
23767     
23768     init : function(editor)
23769     {
23770         this.editor = editor;
23771         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23772         var editorcore = this.editorcore;
23773         
23774         var fid = editorcore.frameId;
23775         var etb = this;
23776         function btn(id, toggle, handler){
23777             var xid = fid + '-'+ id ;
23778             return {
23779                 id : xid,
23780                 cmd : id,
23781                 cls : 'x-btn-icon x-edit-'+id,
23782                 enableToggle:toggle !== false,
23783                 scope: editorcore, // was editor...
23784                 handler:handler||editorcore.relayBtnCmd,
23785                 clickEvent:'mousedown',
23786                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23787                 tabIndex:-1
23788             };
23789         }
23790         // create a new element.
23791         var wdiv = editor.wrap.createChild({
23792                 tag: 'div'
23793             }, editor.wrap.dom.firstChild.nextSibling, true);
23794         
23795         // can we do this more than once??
23796         
23797          // stop form submits
23798       
23799  
23800         // disable everything...
23801         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23802         this.toolbars = {};
23803            
23804         for (var i in  ty) {
23805           
23806             this.toolbars[i] = this.buildToolbar(ty[i],i);
23807         }
23808         this.tb = this.toolbars.BODY;
23809         this.tb.el.show();
23810         this.buildFooter();
23811         this.footer.show();
23812         editor.on('hide', function( ) { this.footer.hide() }, this);
23813         editor.on('show', function( ) { this.footer.show() }, this);
23814         
23815          
23816         this.rendered = true;
23817         
23818         // the all the btns;
23819         editor.on('editorevent', this.updateToolbar, this);
23820         // other toolbars need to implement this..
23821         //editor.on('editmodechange', this.updateToolbar, this);
23822     },
23823     
23824     
23825     
23826     /**
23827      * Protected method that will not generally be called directly. It triggers
23828      * a toolbar update by reading the markup state of the current selection in the editor.
23829      *
23830      * Note you can force an update by calling on('editorevent', scope, false)
23831      */
23832     updateToolbar: function(editor,ev,sel){
23833
23834         //Roo.log(ev);
23835         // capture mouse up - this is handy for selecting images..
23836         // perhaps should go somewhere else...
23837         if(!this.editorcore.activated){
23838              this.editor.onFirstFocus();
23839             return;
23840         }
23841         
23842         
23843         
23844         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23845         // selectNode - might want to handle IE?
23846         if (ev &&
23847             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23848             ev.target && ev.target.tagName == 'IMG') {
23849             // they have click on an image...
23850             // let's see if we can change the selection...
23851             sel = ev.target;
23852          
23853               var nodeRange = sel.ownerDocument.createRange();
23854             try {
23855                 nodeRange.selectNode(sel);
23856             } catch (e) {
23857                 nodeRange.selectNodeContents(sel);
23858             }
23859             //nodeRange.collapse(true);
23860             var s = this.editorcore.win.getSelection();
23861             s.removeAllRanges();
23862             s.addRange(nodeRange);
23863         }  
23864         
23865       
23866         var updateFooter = sel ? false : true;
23867         
23868         
23869         var ans = this.editorcore.getAllAncestors();
23870         
23871         // pick
23872         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23873         
23874         if (!sel) { 
23875             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
23876             sel = sel ? sel : this.editorcore.doc.body;
23877             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23878             
23879         }
23880         // pick a menu that exists..
23881         var tn = sel.tagName.toUpperCase();
23882         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23883         
23884         tn = sel.tagName.toUpperCase();
23885         
23886         var lastSel = this.tb.selectedNode;
23887         
23888         this.tb.selectedNode = sel;
23889         
23890         // if current menu does not match..
23891         
23892         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23893                 
23894             this.tb.el.hide();
23895             ///console.log("show: " + tn);
23896             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
23897             this.tb.el.show();
23898             // update name
23899             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
23900             
23901             
23902             // update attributes
23903             if (this.tb.fields) {
23904                 this.tb.fields.each(function(e) {
23905                     if (e.stylename) {
23906                         e.setValue(sel.style[e.stylename]);
23907                         return;
23908                     } 
23909                    e.setValue(sel.getAttribute(e.attrname));
23910                 });
23911             }
23912             
23913             var hasStyles = false;
23914             for(var i in this.styles) {
23915                 hasStyles = true;
23916                 break;
23917             }
23918             
23919             // update styles
23920             if (hasStyles) { 
23921                 var st = this.tb.fields.item(0);
23922                 
23923                 st.store.removeAll();
23924                
23925                 
23926                 var cn = sel.className.split(/\s+/);
23927                 
23928                 var avs = [];
23929                 if (this.styles['*']) {
23930                     
23931                     Roo.each(this.styles['*'], function(v) {
23932                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23933                     });
23934                 }
23935                 if (this.styles[tn]) { 
23936                     Roo.each(this.styles[tn], function(v) {
23937                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23938                     });
23939                 }
23940                 
23941                 st.store.loadData(avs);
23942                 st.collapse();
23943                 st.setValue(cn);
23944             }
23945             // flag our selected Node.
23946             this.tb.selectedNode = sel;
23947            
23948            
23949             Roo.menu.MenuMgr.hideAll();
23950
23951         }
23952         
23953         if (!updateFooter) {
23954             //this.footDisp.dom.innerHTML = ''; 
23955             return;
23956         }
23957         // update the footer
23958         //
23959         var html = '';
23960         
23961         this.footerEls = ans.reverse();
23962         Roo.each(this.footerEls, function(a,i) {
23963             if (!a) { return; }
23964             html += html.length ? ' &gt; '  :  '';
23965             
23966             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
23967             
23968         });
23969        
23970         // 
23971         var sz = this.footDisp.up('td').getSize();
23972         this.footDisp.dom.style.width = (sz.width -10) + 'px';
23973         this.footDisp.dom.style.marginLeft = '5px';
23974         
23975         this.footDisp.dom.style.overflow = 'hidden';
23976         
23977         this.footDisp.dom.innerHTML = html;
23978             
23979         //this.editorsyncValue();
23980     },
23981      
23982     
23983    
23984        
23985     // private
23986     onDestroy : function(){
23987         if(this.rendered){
23988             
23989             this.tb.items.each(function(item){
23990                 if(item.menu){
23991                     item.menu.removeAll();
23992                     if(item.menu.el){
23993                         item.menu.el.destroy();
23994                     }
23995                 }
23996                 item.destroy();
23997             });
23998              
23999         }
24000     },
24001     onFirstFocus: function() {
24002         // need to do this for all the toolbars..
24003         this.tb.items.each(function(item){
24004            item.enable();
24005         });
24006     },
24007     buildToolbar: function(tlist, nm)
24008     {
24009         var editor = this.editor;
24010         var editorcore = this.editorcore;
24011          // create a new element.
24012         var wdiv = editor.wrap.createChild({
24013                 tag: 'div'
24014             }, editor.wrap.dom.firstChild.nextSibling, true);
24015         
24016        
24017         var tb = new Roo.Toolbar(wdiv);
24018         // add the name..
24019         
24020         tb.add(nm+ ":&nbsp;");
24021         
24022         var styles = [];
24023         for(var i in this.styles) {
24024             styles.push(i);
24025         }
24026         
24027         // styles...
24028         if (styles && styles.length) {
24029             
24030             // this needs a multi-select checkbox...
24031             tb.addField( new Roo.form.ComboBox({
24032                 store: new Roo.data.SimpleStore({
24033                     id : 'val',
24034                     fields: ['val', 'selected'],
24035                     data : [] 
24036                 }),
24037                 name : '-roo-edit-className',
24038                 attrname : 'className',
24039                 displayField: 'val',
24040                 typeAhead: false,
24041                 mode: 'local',
24042                 editable : false,
24043                 triggerAction: 'all',
24044                 emptyText:'Select Style',
24045                 selectOnFocus:true,
24046                 width: 130,
24047                 listeners : {
24048                     'select': function(c, r, i) {
24049                         // initial support only for on class per el..
24050                         tb.selectedNode.className =  r ? r.get('val') : '';
24051                         editorcore.syncValue();
24052                     }
24053                 }
24054     
24055             }));
24056         }
24057         
24058         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24059         var tbops = tbc.options;
24060         
24061         for (var i in tlist) {
24062             
24063             var item = tlist[i];
24064             tb.add(item.title + ":&nbsp;");
24065             
24066             
24067             //optname == used so you can configure the options available..
24068             var opts = item.opts ? item.opts : false;
24069             if (item.optname) {
24070                 opts = tbops[item.optname];
24071            
24072             }
24073             
24074             if (opts) {
24075                 // opts == pulldown..
24076                 tb.addField( new Roo.form.ComboBox({
24077                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24078                         id : 'val',
24079                         fields: ['val', 'display'],
24080                         data : opts  
24081                     }),
24082                     name : '-roo-edit-' + i,
24083                     attrname : i,
24084                     stylename : item.style ? item.style : false,
24085                     displayField: item.displayField ? item.displayField : 'val',
24086                     valueField :  'val',
24087                     typeAhead: false,
24088                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24089                     editable : false,
24090                     triggerAction: 'all',
24091                     emptyText:'Select',
24092                     selectOnFocus:true,
24093                     width: item.width ? item.width  : 130,
24094                     listeners : {
24095                         'select': function(c, r, i) {
24096                             if (c.stylename) {
24097                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24098                                 return;
24099                             }
24100                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24101                         }
24102                     }
24103
24104                 }));
24105                 continue;
24106                     
24107                  
24108                 
24109                 tb.addField( new Roo.form.TextField({
24110                     name: i,
24111                     width: 100,
24112                     //allowBlank:false,
24113                     value: ''
24114                 }));
24115                 continue;
24116             }
24117             tb.addField( new Roo.form.TextField({
24118                 name: '-roo-edit-' + i,
24119                 attrname : i,
24120                 
24121                 width: item.width,
24122                 //allowBlank:true,
24123                 value: '',
24124                 listeners: {
24125                     'change' : function(f, nv, ov) {
24126                         tb.selectedNode.setAttribute(f.attrname, nv);
24127                         editorcore.syncValue();
24128                     }
24129                 }
24130             }));
24131              
24132         }
24133         
24134         var _this = this;
24135         
24136         if(nm == 'BODY'){
24137             tb.addSeparator();
24138         
24139             tb.addButton( {
24140                 text: 'Stylesheets',
24141
24142                 listeners : {
24143                     click : function ()
24144                     {
24145                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24146                     }
24147                 }
24148             });
24149         }
24150         
24151         tb.addFill();
24152         tb.addButton( {
24153             text: 'Remove Tag',
24154     
24155             listeners : {
24156                 click : function ()
24157                 {
24158                     // remove
24159                     // undo does not work.
24160                      
24161                     var sn = tb.selectedNode;
24162                     
24163                     var pn = sn.parentNode;
24164                     
24165                     var stn =  sn.childNodes[0];
24166                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24167                     while (sn.childNodes.length) {
24168                         var node = sn.childNodes[0];
24169                         sn.removeChild(node);
24170                         //Roo.log(node);
24171                         pn.insertBefore(node, sn);
24172                         
24173                     }
24174                     pn.removeChild(sn);
24175                     var range = editorcore.createRange();
24176         
24177                     range.setStart(stn,0);
24178                     range.setEnd(en,0); //????
24179                     //range.selectNode(sel);
24180                     
24181                     
24182                     var selection = editorcore.getSelection();
24183                     selection.removeAllRanges();
24184                     selection.addRange(range);
24185                     
24186                     
24187                     
24188                     //_this.updateToolbar(null, null, pn);
24189                     _this.updateToolbar(null, null, null);
24190                     _this.footDisp.dom.innerHTML = ''; 
24191                 }
24192             }
24193             
24194                     
24195                 
24196             
24197         });
24198         
24199         
24200         tb.el.on('click', function(e){
24201             e.preventDefault(); // what does this do?
24202         });
24203         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24204         tb.el.hide();
24205         tb.name = nm;
24206         // dont need to disable them... as they will get hidden
24207         return tb;
24208          
24209         
24210     },
24211     buildFooter : function()
24212     {
24213         
24214         var fel = this.editor.wrap.createChild();
24215         this.footer = new Roo.Toolbar(fel);
24216         // toolbar has scrolly on left / right?
24217         var footDisp= new Roo.Toolbar.Fill();
24218         var _t = this;
24219         this.footer.add(
24220             {
24221                 text : '&lt;',
24222                 xtype: 'Button',
24223                 handler : function() {
24224                     _t.footDisp.scrollTo('left',0,true)
24225                 }
24226             }
24227         );
24228         this.footer.add( footDisp );
24229         this.footer.add( 
24230             {
24231                 text : '&gt;',
24232                 xtype: 'Button',
24233                 handler : function() {
24234                     // no animation..
24235                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24236                 }
24237             }
24238         );
24239         var fel = Roo.get(footDisp.el);
24240         fel.addClass('x-editor-context');
24241         this.footDispWrap = fel; 
24242         this.footDispWrap.overflow  = 'hidden';
24243         
24244         this.footDisp = fel.createChild();
24245         this.footDispWrap.on('click', this.onContextClick, this)
24246         
24247         
24248     },
24249     onContextClick : function (ev,dom)
24250     {
24251         ev.preventDefault();
24252         var  cn = dom.className;
24253         //Roo.log(cn);
24254         if (!cn.match(/x-ed-loc-/)) {
24255             return;
24256         }
24257         var n = cn.split('-').pop();
24258         var ans = this.footerEls;
24259         var sel = ans[n];
24260         
24261          // pick
24262         var range = this.editorcore.createRange();
24263         
24264         range.selectNodeContents(sel);
24265         //range.selectNode(sel);
24266         
24267         
24268         var selection = this.editorcore.getSelection();
24269         selection.removeAllRanges();
24270         selection.addRange(range);
24271         
24272         
24273         
24274         this.updateToolbar(null, null, sel);
24275         
24276         
24277     }
24278     
24279     
24280     
24281     
24282     
24283 });
24284
24285
24286
24287
24288
24289 /*
24290  * Based on:
24291  * Ext JS Library 1.1.1
24292  * Copyright(c) 2006-2007, Ext JS, LLC.
24293  *
24294  * Originally Released Under LGPL - original licence link has changed is not relivant.
24295  *
24296  * Fork - LGPL
24297  * <script type="text/javascript">
24298  */
24299  
24300 /**
24301  * @class Roo.form.BasicForm
24302  * @extends Roo.util.Observable
24303  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24304  * @constructor
24305  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24306  * @param {Object} config Configuration options
24307  */
24308 Roo.form.BasicForm = function(el, config){
24309     this.allItems = [];
24310     this.childForms = [];
24311     Roo.apply(this, config);
24312     /*
24313      * The Roo.form.Field items in this form.
24314      * @type MixedCollection
24315      */
24316      
24317      
24318     this.items = new Roo.util.MixedCollection(false, function(o){
24319         return o.id || (o.id = Roo.id());
24320     });
24321     this.addEvents({
24322         /**
24323          * @event beforeaction
24324          * Fires before any action is performed. Return false to cancel the action.
24325          * @param {Form} this
24326          * @param {Action} action The action to be performed
24327          */
24328         beforeaction: true,
24329         /**
24330          * @event actionfailed
24331          * Fires when an action fails.
24332          * @param {Form} this
24333          * @param {Action} action The action that failed
24334          */
24335         actionfailed : true,
24336         /**
24337          * @event actioncomplete
24338          * Fires when an action is completed.
24339          * @param {Form} this
24340          * @param {Action} action The action that completed
24341          */
24342         actioncomplete : true
24343     });
24344     if(el){
24345         this.initEl(el);
24346     }
24347     Roo.form.BasicForm.superclass.constructor.call(this);
24348 };
24349
24350 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24351     /**
24352      * @cfg {String} method
24353      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24354      */
24355     /**
24356      * @cfg {DataReader} reader
24357      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24358      * This is optional as there is built-in support for processing JSON.
24359      */
24360     /**
24361      * @cfg {DataReader} errorReader
24362      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24363      * This is completely optional as there is built-in support for processing JSON.
24364      */
24365     /**
24366      * @cfg {String} url
24367      * The URL to use for form actions if one isn't supplied in the action options.
24368      */
24369     /**
24370      * @cfg {Boolean} fileUpload
24371      * Set to true if this form is a file upload.
24372      */
24373      
24374     /**
24375      * @cfg {Object} baseParams
24376      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24377      */
24378      /**
24379      
24380     /**
24381      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24382      */
24383     timeout: 30,
24384
24385     // private
24386     activeAction : null,
24387
24388     /**
24389      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24390      * or setValues() data instead of when the form was first created.
24391      */
24392     trackResetOnLoad : false,
24393     
24394     
24395     /**
24396      * childForms - used for multi-tab forms
24397      * @type {Array}
24398      */
24399     childForms : false,
24400     
24401     /**
24402      * allItems - full list of fields.
24403      * @type {Array}
24404      */
24405     allItems : false,
24406     
24407     /**
24408      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24409      * element by passing it or its id or mask the form itself by passing in true.
24410      * @type Mixed
24411      */
24412     waitMsgTarget : false,
24413     
24414     /**
24415      * @type Boolean
24416      */
24417     disableMask : false,
24418
24419     // private
24420     initEl : function(el){
24421         this.el = Roo.get(el);
24422         this.id = this.el.id || Roo.id();
24423         this.el.on('submit', this.onSubmit, this);
24424         this.el.addClass('x-form');
24425     },
24426
24427     // private
24428     onSubmit : function(e){
24429         e.stopEvent();
24430     },
24431
24432     /**
24433      * Returns true if client-side validation on the form is successful.
24434      * @return Boolean
24435      */
24436     isValid : function(){
24437         var valid = true;
24438         this.items.each(function(f){
24439            if(!f.validate()){
24440                valid = false;
24441            }
24442         });
24443         return valid;
24444     },
24445
24446     /**
24447      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24448      * @return Boolean
24449      */
24450     isDirty : function(){
24451         var dirty = false;
24452         this.items.each(function(f){
24453            if(f.isDirty()){
24454                dirty = true;
24455                return false;
24456            }
24457         });
24458         return dirty;
24459     },
24460     
24461     /**
24462      * Returns true if any fields in this form have changed since their original load. (New version)
24463      * @return Boolean
24464      */
24465     
24466     hasChanged : function()
24467     {
24468         var dirty = false;
24469         this.items.each(function(f){
24470            if(f.hasChanged()){
24471                dirty = true;
24472                return false;
24473            }
24474         });
24475         return dirty;
24476         
24477     },
24478     /**
24479      * Resets all hasChanged to 'false' -
24480      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24481      * So hasChanged storage is only to be used for this purpose
24482      * @return Boolean
24483      */
24484     resetHasChanged : function()
24485     {
24486         this.items.each(function(f){
24487            f.resetHasChanged();
24488         });
24489         
24490     },
24491     
24492     
24493     /**
24494      * Performs a predefined action (submit or load) or custom actions you define on this form.
24495      * @param {String} actionName The name of the action type
24496      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24497      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24498      * accept other config options):
24499      * <pre>
24500 Property          Type             Description
24501 ----------------  ---------------  ----------------------------------------------------------------------------------
24502 url               String           The url for the action (defaults to the form's url)
24503 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24504 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24505 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24506                                    validate the form on the client (defaults to false)
24507      * </pre>
24508      * @return {BasicForm} this
24509      */
24510     doAction : function(action, options){
24511         if(typeof action == 'string'){
24512             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24513         }
24514         if(this.fireEvent('beforeaction', this, action) !== false){
24515             this.beforeAction(action);
24516             action.run.defer(100, action);
24517         }
24518         return this;
24519     },
24520
24521     /**
24522      * Shortcut to do a submit action.
24523      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24524      * @return {BasicForm} this
24525      */
24526     submit : function(options){
24527         this.doAction('submit', options);
24528         return this;
24529     },
24530
24531     /**
24532      * Shortcut to do a load action.
24533      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24534      * @return {BasicForm} this
24535      */
24536     load : function(options){
24537         this.doAction('load', options);
24538         return this;
24539     },
24540
24541     /**
24542      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24543      * @param {Record} record The record to edit
24544      * @return {BasicForm} this
24545      */
24546     updateRecord : function(record){
24547         record.beginEdit();
24548         var fs = record.fields;
24549         fs.each(function(f){
24550             var field = this.findField(f.name);
24551             if(field){
24552                 record.set(f.name, field.getValue());
24553             }
24554         }, this);
24555         record.endEdit();
24556         return this;
24557     },
24558
24559     /**
24560      * Loads an Roo.data.Record into this form.
24561      * @param {Record} record The record to load
24562      * @return {BasicForm} this
24563      */
24564     loadRecord : function(record){
24565         this.setValues(record.data);
24566         return this;
24567     },
24568
24569     // private
24570     beforeAction : function(action){
24571         var o = action.options;
24572         
24573         if(!this.disableMask) {
24574             if(this.waitMsgTarget === true){
24575                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24576             }else if(this.waitMsgTarget){
24577                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24578                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24579             }else {
24580                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24581             }
24582         }
24583         
24584          
24585     },
24586
24587     // private
24588     afterAction : function(action, success){
24589         this.activeAction = null;
24590         var o = action.options;
24591         
24592         if(!this.disableMask) {
24593             if(this.waitMsgTarget === true){
24594                 this.el.unmask();
24595             }else if(this.waitMsgTarget){
24596                 this.waitMsgTarget.unmask();
24597             }else{
24598                 Roo.MessageBox.updateProgress(1);
24599                 Roo.MessageBox.hide();
24600             }
24601         }
24602         
24603         if(success){
24604             if(o.reset){
24605                 this.reset();
24606             }
24607             Roo.callback(o.success, o.scope, [this, action]);
24608             this.fireEvent('actioncomplete', this, action);
24609             
24610         }else{
24611             
24612             // failure condition..
24613             // we have a scenario where updates need confirming.
24614             // eg. if a locking scenario exists..
24615             // we look for { errors : { needs_confirm : true }} in the response.
24616             if (
24617                 (typeof(action.result) != 'undefined')  &&
24618                 (typeof(action.result.errors) != 'undefined')  &&
24619                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24620            ){
24621                 var _t = this;
24622                 Roo.MessageBox.confirm(
24623                     "Change requires confirmation",
24624                     action.result.errorMsg,
24625                     function(r) {
24626                         if (r != 'yes') {
24627                             return;
24628                         }
24629                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24630                     }
24631                     
24632                 );
24633                 
24634                 
24635                 
24636                 return;
24637             }
24638             
24639             Roo.callback(o.failure, o.scope, [this, action]);
24640             // show an error message if no failed handler is set..
24641             if (!this.hasListener('actionfailed')) {
24642                 Roo.MessageBox.alert("Error",
24643                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24644                         action.result.errorMsg :
24645                         "Saving Failed, please check your entries or try again"
24646                 );
24647             }
24648             
24649             this.fireEvent('actionfailed', this, action);
24650         }
24651         
24652     },
24653
24654     /**
24655      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24656      * @param {String} id The value to search for
24657      * @return Field
24658      */
24659     findField : function(id){
24660         var field = this.items.get(id);
24661         if(!field){
24662             this.items.each(function(f){
24663                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24664                     field = f;
24665                     return false;
24666                 }
24667             });
24668         }
24669         return field || null;
24670     },
24671
24672     /**
24673      * Add a secondary form to this one, 
24674      * Used to provide tabbed forms. One form is primary, with hidden values 
24675      * which mirror the elements from the other forms.
24676      * 
24677      * @param {Roo.form.Form} form to add.
24678      * 
24679      */
24680     addForm : function(form)
24681     {
24682        
24683         if (this.childForms.indexOf(form) > -1) {
24684             // already added..
24685             return;
24686         }
24687         this.childForms.push(form);
24688         var n = '';
24689         Roo.each(form.allItems, function (fe) {
24690             
24691             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24692             if (this.findField(n)) { // already added..
24693                 return;
24694             }
24695             var add = new Roo.form.Hidden({
24696                 name : n
24697             });
24698             add.render(this.el);
24699             
24700             this.add( add );
24701         }, this);
24702         
24703     },
24704     /**
24705      * Mark fields in this form invalid in bulk.
24706      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24707      * @return {BasicForm} this
24708      */
24709     markInvalid : function(errors){
24710         if(errors instanceof Array){
24711             for(var i = 0, len = errors.length; i < len; i++){
24712                 var fieldError = errors[i];
24713                 var f = this.findField(fieldError.id);
24714                 if(f){
24715                     f.markInvalid(fieldError.msg);
24716                 }
24717             }
24718         }else{
24719             var field, id;
24720             for(id in errors){
24721                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24722                     field.markInvalid(errors[id]);
24723                 }
24724             }
24725         }
24726         Roo.each(this.childForms || [], function (f) {
24727             f.markInvalid(errors);
24728         });
24729         
24730         return this;
24731     },
24732
24733     /**
24734      * Set values for fields in this form in bulk.
24735      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24736      * @return {BasicForm} this
24737      */
24738     setValues : function(values){
24739         if(values instanceof Array){ // array of objects
24740             for(var i = 0, len = values.length; i < len; i++){
24741                 var v = values[i];
24742                 var f = this.findField(v.id);
24743                 if(f){
24744                     f.setValue(v.value);
24745                     if(this.trackResetOnLoad){
24746                         f.originalValue = f.getValue();
24747                     }
24748                 }
24749             }
24750         }else{ // object hash
24751             var field, id;
24752             for(id in values){
24753                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24754                     
24755                     if (field.setFromData && 
24756                         field.valueField && 
24757                         field.displayField &&
24758                         // combos' with local stores can 
24759                         // be queried via setValue()
24760                         // to set their value..
24761                         (field.store && !field.store.isLocal)
24762                         ) {
24763                         // it's a combo
24764                         var sd = { };
24765                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24766                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24767                         field.setFromData(sd);
24768                         
24769                     } else {
24770                         field.setValue(values[id]);
24771                     }
24772                     
24773                     
24774                     if(this.trackResetOnLoad){
24775                         field.originalValue = field.getValue();
24776                     }
24777                 }
24778             }
24779         }
24780         this.resetHasChanged();
24781         
24782         
24783         Roo.each(this.childForms || [], function (f) {
24784             f.setValues(values);
24785             f.resetHasChanged();
24786         });
24787                 
24788         return this;
24789     },
24790
24791     /**
24792      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24793      * they are returned as an array.
24794      * @param {Boolean} asString
24795      * @return {Object}
24796      */
24797     getValues : function(asString){
24798         if (this.childForms) {
24799             // copy values from the child forms
24800             Roo.each(this.childForms, function (f) {
24801                 this.setValues(f.getValues());
24802             }, this);
24803         }
24804         
24805         
24806         
24807         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24808         if(asString === true){
24809             return fs;
24810         }
24811         return Roo.urlDecode(fs);
24812     },
24813     
24814     /**
24815      * Returns the fields in this form as an object with key/value pairs. 
24816      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24817      * @return {Object}
24818      */
24819     getFieldValues : function(with_hidden)
24820     {
24821         if (this.childForms) {
24822             // copy values from the child forms
24823             // should this call getFieldValues - probably not as we do not currently copy
24824             // hidden fields when we generate..
24825             Roo.each(this.childForms, function (f) {
24826                 this.setValues(f.getValues());
24827             }, this);
24828         }
24829         
24830         var ret = {};
24831         this.items.each(function(f){
24832             if (!f.getName()) {
24833                 return;
24834             }
24835             var v = f.getValue();
24836             if (f.inputType =='radio') {
24837                 if (typeof(ret[f.getName()]) == 'undefined') {
24838                     ret[f.getName()] = ''; // empty..
24839                 }
24840                 
24841                 if (!f.el.dom.checked) {
24842                     return;
24843                     
24844                 }
24845                 v = f.el.dom.value;
24846                 
24847             }
24848             
24849             // not sure if this supported any more..
24850             if ((typeof(v) == 'object') && f.getRawValue) {
24851                 v = f.getRawValue() ; // dates..
24852             }
24853             // combo boxes where name != hiddenName...
24854             if (f.name != f.getName()) {
24855                 ret[f.name] = f.getRawValue();
24856             }
24857             ret[f.getName()] = v;
24858         });
24859         
24860         return ret;
24861     },
24862
24863     /**
24864      * Clears all invalid messages in this form.
24865      * @return {BasicForm} this
24866      */
24867     clearInvalid : function(){
24868         this.items.each(function(f){
24869            f.clearInvalid();
24870         });
24871         
24872         Roo.each(this.childForms || [], function (f) {
24873             f.clearInvalid();
24874         });
24875         
24876         
24877         return this;
24878     },
24879
24880     /**
24881      * Resets this form.
24882      * @return {BasicForm} this
24883      */
24884     reset : function(){
24885         this.items.each(function(f){
24886             f.reset();
24887         });
24888         
24889         Roo.each(this.childForms || [], function (f) {
24890             f.reset();
24891         });
24892         this.resetHasChanged();
24893         
24894         return this;
24895     },
24896
24897     /**
24898      * Add Roo.form components to this form.
24899      * @param {Field} field1
24900      * @param {Field} field2 (optional)
24901      * @param {Field} etc (optional)
24902      * @return {BasicForm} this
24903      */
24904     add : function(){
24905         this.items.addAll(Array.prototype.slice.call(arguments, 0));
24906         return this;
24907     },
24908
24909
24910     /**
24911      * Removes a field from the items collection (does NOT remove its markup).
24912      * @param {Field} field
24913      * @return {BasicForm} this
24914      */
24915     remove : function(field){
24916         this.items.remove(field);
24917         return this;
24918     },
24919
24920     /**
24921      * Looks at the fields in this form, checks them for an id attribute,
24922      * and calls applyTo on the existing dom element with that id.
24923      * @return {BasicForm} this
24924      */
24925     render : function(){
24926         this.items.each(function(f){
24927             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
24928                 f.applyTo(f.id);
24929             }
24930         });
24931         return this;
24932     },
24933
24934     /**
24935      * Calls {@link Ext#apply} for all fields in this form with the passed object.
24936      * @param {Object} values
24937      * @return {BasicForm} this
24938      */
24939     applyToFields : function(o){
24940         this.items.each(function(f){
24941            Roo.apply(f, o);
24942         });
24943         return this;
24944     },
24945
24946     /**
24947      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
24948      * @param {Object} values
24949      * @return {BasicForm} this
24950      */
24951     applyIfToFields : function(o){
24952         this.items.each(function(f){
24953            Roo.applyIf(f, o);
24954         });
24955         return this;
24956     }
24957 });
24958
24959 // back compat
24960 Roo.BasicForm = Roo.form.BasicForm;/*
24961  * Based on:
24962  * Ext JS Library 1.1.1
24963  * Copyright(c) 2006-2007, Ext JS, LLC.
24964  *
24965  * Originally Released Under LGPL - original licence link has changed is not relivant.
24966  *
24967  * Fork - LGPL
24968  * <script type="text/javascript">
24969  */
24970
24971 /**
24972  * @class Roo.form.Form
24973  * @extends Roo.form.BasicForm
24974  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
24975  * @constructor
24976  * @param {Object} config Configuration options
24977  */
24978 Roo.form.Form = function(config){
24979     var xitems =  [];
24980     if (config.items) {
24981         xitems = config.items;
24982         delete config.items;
24983     }
24984    
24985     
24986     Roo.form.Form.superclass.constructor.call(this, null, config);
24987     this.url = this.url || this.action;
24988     if(!this.root){
24989         this.root = new Roo.form.Layout(Roo.applyIf({
24990             id: Roo.id()
24991         }, config));
24992     }
24993     this.active = this.root;
24994     /**
24995      * Array of all the buttons that have been added to this form via {@link addButton}
24996      * @type Array
24997      */
24998     this.buttons = [];
24999     this.allItems = [];
25000     this.addEvents({
25001         /**
25002          * @event clientvalidation
25003          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25004          * @param {Form} this
25005          * @param {Boolean} valid true if the form has passed client-side validation
25006          */
25007         clientvalidation: true,
25008         /**
25009          * @event rendered
25010          * Fires when the form is rendered
25011          * @param {Roo.form.Form} form
25012          */
25013         rendered : true
25014     });
25015     
25016     if (this.progressUrl) {
25017             // push a hidden field onto the list of fields..
25018             this.addxtype( {
25019                     xns: Roo.form, 
25020                     xtype : 'Hidden', 
25021                     name : 'UPLOAD_IDENTIFIER' 
25022             });
25023         }
25024         
25025     
25026     Roo.each(xitems, this.addxtype, this);
25027     
25028     
25029     
25030 };
25031
25032 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25033     /**
25034      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25035      */
25036     /**
25037      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25038      */
25039     /**
25040      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25041      */
25042     buttonAlign:'center',
25043
25044     /**
25045      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25046      */
25047     minButtonWidth:75,
25048
25049     /**
25050      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25051      * This property cascades to child containers if not set.
25052      */
25053     labelAlign:'left',
25054
25055     /**
25056      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25057      * fires a looping event with that state. This is required to bind buttons to the valid
25058      * state using the config value formBind:true on the button.
25059      */
25060     monitorValid : false,
25061
25062     /**
25063      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25064      */
25065     monitorPoll : 200,
25066     
25067     /**
25068      * @cfg {String} progressUrl - Url to return progress data 
25069      */
25070     
25071     progressUrl : false,
25072     /**
25073      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25074      * sending a formdata with extra parameters - eg uploaded elements.
25075      */
25076     
25077     formData : false,
25078     
25079     /**
25080      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25081      * fields are added and the column is closed. If no fields are passed the column remains open
25082      * until end() is called.
25083      * @param {Object} config The config to pass to the column
25084      * @param {Field} field1 (optional)
25085      * @param {Field} field2 (optional)
25086      * @param {Field} etc (optional)
25087      * @return Column The column container object
25088      */
25089     column : function(c){
25090         var col = new Roo.form.Column(c);
25091         this.start(col);
25092         if(arguments.length > 1){ // duplicate code required because of Opera
25093             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25094             this.end();
25095         }
25096         return col;
25097     },
25098
25099     /**
25100      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25101      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25102      * until end() is called.
25103      * @param {Object} config The config to pass to the fieldset
25104      * @param {Field} field1 (optional)
25105      * @param {Field} field2 (optional)
25106      * @param {Field} etc (optional)
25107      * @return FieldSet The fieldset container object
25108      */
25109     fieldset : function(c){
25110         var fs = new Roo.form.FieldSet(c);
25111         this.start(fs);
25112         if(arguments.length > 1){ // duplicate code required because of Opera
25113             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25114             this.end();
25115         }
25116         return fs;
25117     },
25118
25119     /**
25120      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25121      * fields are added and the container is closed. If no fields are passed the container remains open
25122      * until end() is called.
25123      * @param {Object} config The config to pass to the Layout
25124      * @param {Field} field1 (optional)
25125      * @param {Field} field2 (optional)
25126      * @param {Field} etc (optional)
25127      * @return Layout The container object
25128      */
25129     container : function(c){
25130         var l = new Roo.form.Layout(c);
25131         this.start(l);
25132         if(arguments.length > 1){ // duplicate code required because of Opera
25133             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25134             this.end();
25135         }
25136         return l;
25137     },
25138
25139     /**
25140      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25141      * @param {Object} container A Roo.form.Layout or subclass of Layout
25142      * @return {Form} this
25143      */
25144     start : function(c){
25145         // cascade label info
25146         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25147         this.active.stack.push(c);
25148         c.ownerCt = this.active;
25149         this.active = c;
25150         return this;
25151     },
25152
25153     /**
25154      * Closes the current open container
25155      * @return {Form} this
25156      */
25157     end : function(){
25158         if(this.active == this.root){
25159             return this;
25160         }
25161         this.active = this.active.ownerCt;
25162         return this;
25163     },
25164
25165     /**
25166      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25167      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25168      * as the label of the field.
25169      * @param {Field} field1
25170      * @param {Field} field2 (optional)
25171      * @param {Field} etc. (optional)
25172      * @return {Form} this
25173      */
25174     add : function(){
25175         this.active.stack.push.apply(this.active.stack, arguments);
25176         this.allItems.push.apply(this.allItems,arguments);
25177         var r = [];
25178         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25179             if(a[i].isFormField){
25180                 r.push(a[i]);
25181             }
25182         }
25183         if(r.length > 0){
25184             Roo.form.Form.superclass.add.apply(this, r);
25185         }
25186         return this;
25187     },
25188     
25189
25190     
25191     
25192     
25193      /**
25194      * Find any element that has been added to a form, using it's ID or name
25195      * This can include framesets, columns etc. along with regular fields..
25196      * @param {String} id - id or name to find.
25197      
25198      * @return {Element} e - or false if nothing found.
25199      */
25200     findbyId : function(id)
25201     {
25202         var ret = false;
25203         if (!id) {
25204             return ret;
25205         }
25206         Roo.each(this.allItems, function(f){
25207             if (f.id == id || f.name == id ){
25208                 ret = f;
25209                 return false;
25210             }
25211         });
25212         return ret;
25213     },
25214
25215     
25216     
25217     /**
25218      * Render this form into the passed container. This should only be called once!
25219      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25220      * @return {Form} this
25221      */
25222     render : function(ct)
25223     {
25224         
25225         
25226         
25227         ct = Roo.get(ct);
25228         var o = this.autoCreate || {
25229             tag: 'form',
25230             method : this.method || 'POST',
25231             id : this.id || Roo.id()
25232         };
25233         this.initEl(ct.createChild(o));
25234
25235         this.root.render(this.el);
25236         
25237        
25238              
25239         this.items.each(function(f){
25240             f.render('x-form-el-'+f.id);
25241         });
25242
25243         if(this.buttons.length > 0){
25244             // tables are required to maintain order and for correct IE layout
25245             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25246                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25247                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25248             }}, null, true);
25249             var tr = tb.getElementsByTagName('tr')[0];
25250             for(var i = 0, len = this.buttons.length; i < len; i++) {
25251                 var b = this.buttons[i];
25252                 var td = document.createElement('td');
25253                 td.className = 'x-form-btn-td';
25254                 b.render(tr.appendChild(td));
25255             }
25256         }
25257         if(this.monitorValid){ // initialize after render
25258             this.startMonitoring();
25259         }
25260         this.fireEvent('rendered', this);
25261         return this;
25262     },
25263
25264     /**
25265      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25266      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25267      * object or a valid Roo.DomHelper element config
25268      * @param {Function} handler The function called when the button is clicked
25269      * @param {Object} scope (optional) The scope of the handler function
25270      * @return {Roo.Button}
25271      */
25272     addButton : function(config, handler, scope){
25273         var bc = {
25274             handler: handler,
25275             scope: scope,
25276             minWidth: this.minButtonWidth,
25277             hideParent:true
25278         };
25279         if(typeof config == "string"){
25280             bc.text = config;
25281         }else{
25282             Roo.apply(bc, config);
25283         }
25284         var btn = new Roo.Button(null, bc);
25285         this.buttons.push(btn);
25286         return btn;
25287     },
25288
25289      /**
25290      * Adds a series of form elements (using the xtype property as the factory method.
25291      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25292      * @param {Object} config 
25293      */
25294     
25295     addxtype : function()
25296     {
25297         var ar = Array.prototype.slice.call(arguments, 0);
25298         var ret = false;
25299         for(var i = 0; i < ar.length; i++) {
25300             if (!ar[i]) {
25301                 continue; // skip -- if this happends something invalid got sent, we 
25302                 // should ignore it, as basically that interface element will not show up
25303                 // and that should be pretty obvious!!
25304             }
25305             
25306             if (Roo.form[ar[i].xtype]) {
25307                 ar[i].form = this;
25308                 var fe = Roo.factory(ar[i], Roo.form);
25309                 if (!ret) {
25310                     ret = fe;
25311                 }
25312                 fe.form = this;
25313                 if (fe.store) {
25314                     fe.store.form = this;
25315                 }
25316                 if (fe.isLayout) {  
25317                          
25318                     this.start(fe);
25319                     this.allItems.push(fe);
25320                     if (fe.items && fe.addxtype) {
25321                         fe.addxtype.apply(fe, fe.items);
25322                         delete fe.items;
25323                     }
25324                      this.end();
25325                     continue;
25326                 }
25327                 
25328                 
25329                  
25330                 this.add(fe);
25331               //  console.log('adding ' + ar[i].xtype);
25332             }
25333             if (ar[i].xtype == 'Button') {  
25334                 //console.log('adding button');
25335                 //console.log(ar[i]);
25336                 this.addButton(ar[i]);
25337                 this.allItems.push(fe);
25338                 continue;
25339             }
25340             
25341             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25342                 alert('end is not supported on xtype any more, use items');
25343             //    this.end();
25344             //    //console.log('adding end');
25345             }
25346             
25347         }
25348         return ret;
25349     },
25350     
25351     /**
25352      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25353      * option "monitorValid"
25354      */
25355     startMonitoring : function(){
25356         if(!this.bound){
25357             this.bound = true;
25358             Roo.TaskMgr.start({
25359                 run : this.bindHandler,
25360                 interval : this.monitorPoll || 200,
25361                 scope: this
25362             });
25363         }
25364     },
25365
25366     /**
25367      * Stops monitoring of the valid state of this form
25368      */
25369     stopMonitoring : function(){
25370         this.bound = false;
25371     },
25372
25373     // private
25374     bindHandler : function(){
25375         if(!this.bound){
25376             return false; // stops binding
25377         }
25378         var valid = true;
25379         this.items.each(function(f){
25380             if(!f.isValid(true)){
25381                 valid = false;
25382                 return false;
25383             }
25384         });
25385         for(var i = 0, len = this.buttons.length; i < len; i++){
25386             var btn = this.buttons[i];
25387             if(btn.formBind === true && btn.disabled === valid){
25388                 btn.setDisabled(!valid);
25389             }
25390         }
25391         this.fireEvent('clientvalidation', this, valid);
25392     }
25393     
25394     
25395     
25396     
25397     
25398     
25399     
25400     
25401 });
25402
25403
25404 // back compat
25405 Roo.Form = Roo.form.Form;
25406 /*
25407  * Based on:
25408  * Ext JS Library 1.1.1
25409  * Copyright(c) 2006-2007, Ext JS, LLC.
25410  *
25411  * Originally Released Under LGPL - original licence link has changed is not relivant.
25412  *
25413  * Fork - LGPL
25414  * <script type="text/javascript">
25415  */
25416
25417 // as we use this in bootstrap.
25418 Roo.namespace('Roo.form');
25419  /**
25420  * @class Roo.form.Action
25421  * Internal Class used to handle form actions
25422  * @constructor
25423  * @param {Roo.form.BasicForm} el The form element or its id
25424  * @param {Object} config Configuration options
25425  */
25426
25427  
25428  
25429 // define the action interface
25430 Roo.form.Action = function(form, options){
25431     this.form = form;
25432     this.options = options || {};
25433 };
25434 /**
25435  * Client Validation Failed
25436  * @const 
25437  */
25438 Roo.form.Action.CLIENT_INVALID = 'client';
25439 /**
25440  * Server Validation Failed
25441  * @const 
25442  */
25443 Roo.form.Action.SERVER_INVALID = 'server';
25444  /**
25445  * Connect to Server Failed
25446  * @const 
25447  */
25448 Roo.form.Action.CONNECT_FAILURE = 'connect';
25449 /**
25450  * Reading Data from Server Failed
25451  * @const 
25452  */
25453 Roo.form.Action.LOAD_FAILURE = 'load';
25454
25455 Roo.form.Action.prototype = {
25456     type : 'default',
25457     failureType : undefined,
25458     response : undefined,
25459     result : undefined,
25460
25461     // interface method
25462     run : function(options){
25463
25464     },
25465
25466     // interface method
25467     success : function(response){
25468
25469     },
25470
25471     // interface method
25472     handleResponse : function(response){
25473
25474     },
25475
25476     // default connection failure
25477     failure : function(response){
25478         
25479         this.response = response;
25480         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25481         this.form.afterAction(this, false);
25482     },
25483
25484     processResponse : function(response){
25485         this.response = response;
25486         if(!response.responseText){
25487             return true;
25488         }
25489         this.result = this.handleResponse(response);
25490         return this.result;
25491     },
25492
25493     // utility functions used internally
25494     getUrl : function(appendParams){
25495         var url = this.options.url || this.form.url || this.form.el.dom.action;
25496         if(appendParams){
25497             var p = this.getParams();
25498             if(p){
25499                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25500             }
25501         }
25502         return url;
25503     },
25504
25505     getMethod : function(){
25506         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25507     },
25508
25509     getParams : function(){
25510         var bp = this.form.baseParams;
25511         var p = this.options.params;
25512         if(p){
25513             if(typeof p == "object"){
25514                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25515             }else if(typeof p == 'string' && bp){
25516                 p += '&' + Roo.urlEncode(bp);
25517             }
25518         }else if(bp){
25519             p = Roo.urlEncode(bp);
25520         }
25521         return p;
25522     },
25523
25524     createCallback : function(){
25525         return {
25526             success: this.success,
25527             failure: this.failure,
25528             scope: this,
25529             timeout: (this.form.timeout*1000),
25530             upload: this.form.fileUpload ? this.success : undefined
25531         };
25532     }
25533 };
25534
25535 Roo.form.Action.Submit = function(form, options){
25536     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25537 };
25538
25539 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25540     type : 'submit',
25541
25542     haveProgress : false,
25543     uploadComplete : false,
25544     
25545     // uploadProgress indicator.
25546     uploadProgress : function()
25547     {
25548         if (!this.form.progressUrl) {
25549             return;
25550         }
25551         
25552         if (!this.haveProgress) {
25553             Roo.MessageBox.progress("Uploading", "Uploading");
25554         }
25555         if (this.uploadComplete) {
25556            Roo.MessageBox.hide();
25557            return;
25558         }
25559         
25560         this.haveProgress = true;
25561    
25562         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25563         
25564         var c = new Roo.data.Connection();
25565         c.request({
25566             url : this.form.progressUrl,
25567             params: {
25568                 id : uid
25569             },
25570             method: 'GET',
25571             success : function(req){
25572                //console.log(data);
25573                 var rdata = false;
25574                 var edata;
25575                 try  {
25576                    rdata = Roo.decode(req.responseText)
25577                 } catch (e) {
25578                     Roo.log("Invalid data from server..");
25579                     Roo.log(edata);
25580                     return;
25581                 }
25582                 if (!rdata || !rdata.success) {
25583                     Roo.log(rdata);
25584                     Roo.MessageBox.alert(Roo.encode(rdata));
25585                     return;
25586                 }
25587                 var data = rdata.data;
25588                 
25589                 if (this.uploadComplete) {
25590                    Roo.MessageBox.hide();
25591                    return;
25592                 }
25593                    
25594                 if (data){
25595                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25596                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25597                     );
25598                 }
25599                 this.uploadProgress.defer(2000,this);
25600             },
25601        
25602             failure: function(data) {
25603                 Roo.log('progress url failed ');
25604                 Roo.log(data);
25605             },
25606             scope : this
25607         });
25608            
25609     },
25610     
25611     
25612     run : function()
25613     {
25614         // run get Values on the form, so it syncs any secondary forms.
25615         this.form.getValues();
25616         
25617         var o = this.options;
25618         var method = this.getMethod();
25619         var isPost = method == 'POST';
25620         if(o.clientValidation === false || this.form.isValid()){
25621             
25622             if (this.form.progressUrl) {
25623                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25624                     (new Date() * 1) + '' + Math.random());
25625                     
25626             } 
25627             
25628             
25629             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25630                 form:this.form.el.dom,
25631                 url:this.getUrl(!isPost),
25632                 method: method,
25633                 params:isPost ? this.getParams() : null,
25634                 isUpload: this.form.fileUpload,
25635                 formData : this.form.formData
25636             }));
25637             
25638             this.uploadProgress();
25639
25640         }else if (o.clientValidation !== false){ // client validation failed
25641             this.failureType = Roo.form.Action.CLIENT_INVALID;
25642             this.form.afterAction(this, false);
25643         }
25644     },
25645
25646     success : function(response)
25647     {
25648         this.uploadComplete= true;
25649         if (this.haveProgress) {
25650             Roo.MessageBox.hide();
25651         }
25652         
25653         
25654         var result = this.processResponse(response);
25655         if(result === true || result.success){
25656             this.form.afterAction(this, true);
25657             return;
25658         }
25659         if(result.errors){
25660             this.form.markInvalid(result.errors);
25661             this.failureType = Roo.form.Action.SERVER_INVALID;
25662         }
25663         this.form.afterAction(this, false);
25664     },
25665     failure : function(response)
25666     {
25667         this.uploadComplete= true;
25668         if (this.haveProgress) {
25669             Roo.MessageBox.hide();
25670         }
25671         
25672         this.response = response;
25673         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25674         this.form.afterAction(this, false);
25675     },
25676     
25677     handleResponse : function(response){
25678         if(this.form.errorReader){
25679             var rs = this.form.errorReader.read(response);
25680             var errors = [];
25681             if(rs.records){
25682                 for(var i = 0, len = rs.records.length; i < len; i++) {
25683                     var r = rs.records[i];
25684                     errors[i] = r.data;
25685                 }
25686             }
25687             if(errors.length < 1){
25688                 errors = null;
25689             }
25690             return {
25691                 success : rs.success,
25692                 errors : errors
25693             };
25694         }
25695         var ret = false;
25696         try {
25697             ret = Roo.decode(response.responseText);
25698         } catch (e) {
25699             ret = {
25700                 success: false,
25701                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25702                 errors : []
25703             };
25704         }
25705         return ret;
25706         
25707     }
25708 });
25709
25710
25711 Roo.form.Action.Load = function(form, options){
25712     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25713     this.reader = this.form.reader;
25714 };
25715
25716 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
25717     type : 'load',
25718
25719     run : function(){
25720         
25721         Roo.Ajax.request(Roo.apply(
25722                 this.createCallback(), {
25723                     method:this.getMethod(),
25724                     url:this.getUrl(false),
25725                     params:this.getParams()
25726         }));
25727     },
25728
25729     success : function(response){
25730         
25731         var result = this.processResponse(response);
25732         if(result === true || !result.success || !result.data){
25733             this.failureType = Roo.form.Action.LOAD_FAILURE;
25734             this.form.afterAction(this, false);
25735             return;
25736         }
25737         this.form.clearInvalid();
25738         this.form.setValues(result.data);
25739         this.form.afterAction(this, true);
25740     },
25741
25742     handleResponse : function(response){
25743         if(this.form.reader){
25744             var rs = this.form.reader.read(response);
25745             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
25746             return {
25747                 success : rs.success,
25748                 data : data
25749             };
25750         }
25751         return Roo.decode(response.responseText);
25752     }
25753 });
25754
25755 Roo.form.Action.ACTION_TYPES = {
25756     'load' : Roo.form.Action.Load,
25757     'submit' : Roo.form.Action.Submit
25758 };/*
25759  * Based on:
25760  * Ext JS Library 1.1.1
25761  * Copyright(c) 2006-2007, Ext JS, LLC.
25762  *
25763  * Originally Released Under LGPL - original licence link has changed is not relivant.
25764  *
25765  * Fork - LGPL
25766  * <script type="text/javascript">
25767  */
25768  
25769 /**
25770  * @class Roo.form.Layout
25771  * @extends Roo.Component
25772  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
25773  * @constructor
25774  * @param {Object} config Configuration options
25775  */
25776 Roo.form.Layout = function(config){
25777     var xitems = [];
25778     if (config.items) {
25779         xitems = config.items;
25780         delete config.items;
25781     }
25782     Roo.form.Layout.superclass.constructor.call(this, config);
25783     this.stack = [];
25784     Roo.each(xitems, this.addxtype, this);
25785      
25786 };
25787
25788 Roo.extend(Roo.form.Layout, Roo.Component, {
25789     /**
25790      * @cfg {String/Object} autoCreate
25791      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
25792      */
25793     /**
25794      * @cfg {String/Object/Function} style
25795      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
25796      * a function which returns such a specification.
25797      */
25798     /**
25799      * @cfg {String} labelAlign
25800      * Valid values are "left," "top" and "right" (defaults to "left")
25801      */
25802     /**
25803      * @cfg {Number} labelWidth
25804      * Fixed width in pixels of all field labels (defaults to undefined)
25805      */
25806     /**
25807      * @cfg {Boolean} clear
25808      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
25809      */
25810     clear : true,
25811     /**
25812      * @cfg {String} labelSeparator
25813      * The separator to use after field labels (defaults to ':')
25814      */
25815     labelSeparator : ':',
25816     /**
25817      * @cfg {Boolean} hideLabels
25818      * True to suppress the display of field labels in this layout (defaults to false)
25819      */
25820     hideLabels : false,
25821
25822     // private
25823     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
25824     
25825     isLayout : true,
25826     
25827     // private
25828     onRender : function(ct, position){
25829         if(this.el){ // from markup
25830             this.el = Roo.get(this.el);
25831         }else {  // generate
25832             var cfg = this.getAutoCreate();
25833             this.el = ct.createChild(cfg, position);
25834         }
25835         if(this.style){
25836             this.el.applyStyles(this.style);
25837         }
25838         if(this.labelAlign){
25839             this.el.addClass('x-form-label-'+this.labelAlign);
25840         }
25841         if(this.hideLabels){
25842             this.labelStyle = "display:none";
25843             this.elementStyle = "padding-left:0;";
25844         }else{
25845             if(typeof this.labelWidth == 'number'){
25846                 this.labelStyle = "width:"+this.labelWidth+"px;";
25847                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
25848             }
25849             if(this.labelAlign == 'top'){
25850                 this.labelStyle = "width:auto;";
25851                 this.elementStyle = "padding-left:0;";
25852             }
25853         }
25854         var stack = this.stack;
25855         var slen = stack.length;
25856         if(slen > 0){
25857             if(!this.fieldTpl){
25858                 var t = new Roo.Template(
25859                     '<div class="x-form-item {5}">',
25860                         '<label for="{0}" style="{2}">{1}{4}</label>',
25861                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25862                         '</div>',
25863                     '</div><div class="x-form-clear-left"></div>'
25864                 );
25865                 t.disableFormats = true;
25866                 t.compile();
25867                 Roo.form.Layout.prototype.fieldTpl = t;
25868             }
25869             for(var i = 0; i < slen; i++) {
25870                 if(stack[i].isFormField){
25871                     this.renderField(stack[i]);
25872                 }else{
25873                     this.renderComponent(stack[i]);
25874                 }
25875             }
25876         }
25877         if(this.clear){
25878             this.el.createChild({cls:'x-form-clear'});
25879         }
25880     },
25881
25882     // private
25883     renderField : function(f){
25884         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
25885                f.id, //0
25886                f.fieldLabel, //1
25887                f.labelStyle||this.labelStyle||'', //2
25888                this.elementStyle||'', //3
25889                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
25890                f.itemCls||this.itemCls||''  //5
25891        ], true).getPrevSibling());
25892     },
25893
25894     // private
25895     renderComponent : function(c){
25896         c.render(c.isLayout ? this.el : this.el.createChild());    
25897     },
25898     /**
25899      * Adds a object form elements (using the xtype property as the factory method.)
25900      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
25901      * @param {Object} config 
25902      */
25903     addxtype : function(o)
25904     {
25905         // create the lement.
25906         o.form = this.form;
25907         var fe = Roo.factory(o, Roo.form);
25908         this.form.allItems.push(fe);
25909         this.stack.push(fe);
25910         
25911         if (fe.isFormField) {
25912             this.form.items.add(fe);
25913         }
25914          
25915         return fe;
25916     }
25917 });
25918
25919 /**
25920  * @class Roo.form.Column
25921  * @extends Roo.form.Layout
25922  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
25923  * @constructor
25924  * @param {Object} config Configuration options
25925  */
25926 Roo.form.Column = function(config){
25927     Roo.form.Column.superclass.constructor.call(this, config);
25928 };
25929
25930 Roo.extend(Roo.form.Column, Roo.form.Layout, {
25931     /**
25932      * @cfg {Number/String} width
25933      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25934      */
25935     /**
25936      * @cfg {String/Object} autoCreate
25937      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
25938      */
25939
25940     // private
25941     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
25942
25943     // private
25944     onRender : function(ct, position){
25945         Roo.form.Column.superclass.onRender.call(this, ct, position);
25946         if(this.width){
25947             this.el.setWidth(this.width);
25948         }
25949     }
25950 });
25951
25952
25953 /**
25954  * @class Roo.form.Row
25955  * @extends Roo.form.Layout
25956  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
25957  * @constructor
25958  * @param {Object} config Configuration options
25959  */
25960
25961  
25962 Roo.form.Row = function(config){
25963     Roo.form.Row.superclass.constructor.call(this, config);
25964 };
25965  
25966 Roo.extend(Roo.form.Row, Roo.form.Layout, {
25967       /**
25968      * @cfg {Number/String} width
25969      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25970      */
25971     /**
25972      * @cfg {Number/String} height
25973      * The fixed height of the column in pixels or CSS value (defaults to "auto")
25974      */
25975     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
25976     
25977     padWidth : 20,
25978     // private
25979     onRender : function(ct, position){
25980         //console.log('row render');
25981         if(!this.rowTpl){
25982             var t = new Roo.Template(
25983                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
25984                     '<label for="{0}" style="{2}">{1}{4}</label>',
25985                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25986                     '</div>',
25987                 '</div>'
25988             );
25989             t.disableFormats = true;
25990             t.compile();
25991             Roo.form.Layout.prototype.rowTpl = t;
25992         }
25993         this.fieldTpl = this.rowTpl;
25994         
25995         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
25996         var labelWidth = 100;
25997         
25998         if ((this.labelAlign != 'top')) {
25999             if (typeof this.labelWidth == 'number') {
26000                 labelWidth = this.labelWidth
26001             }
26002             this.padWidth =  20 + labelWidth;
26003             
26004         }
26005         
26006         Roo.form.Column.superclass.onRender.call(this, ct, position);
26007         if(this.width){
26008             this.el.setWidth(this.width);
26009         }
26010         if(this.height){
26011             this.el.setHeight(this.height);
26012         }
26013     },
26014     
26015     // private
26016     renderField : function(f){
26017         f.fieldEl = this.fieldTpl.append(this.el, [
26018                f.id, f.fieldLabel,
26019                f.labelStyle||this.labelStyle||'',
26020                this.elementStyle||'',
26021                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26022                f.itemCls||this.itemCls||'',
26023                f.width ? f.width + this.padWidth : 160 + this.padWidth
26024        ],true);
26025     }
26026 });
26027  
26028
26029 /**
26030  * @class Roo.form.FieldSet
26031  * @extends Roo.form.Layout
26032  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26033  * @constructor
26034  * @param {Object} config Configuration options
26035  */
26036 Roo.form.FieldSet = function(config){
26037     Roo.form.FieldSet.superclass.constructor.call(this, config);
26038 };
26039
26040 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26041     /**
26042      * @cfg {String} legend
26043      * The text to display as the legend for the FieldSet (defaults to '')
26044      */
26045     /**
26046      * @cfg {String/Object} autoCreate
26047      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26048      */
26049
26050     // private
26051     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26052
26053     // private
26054     onRender : function(ct, position){
26055         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26056         if(this.legend){
26057             this.setLegend(this.legend);
26058         }
26059     },
26060
26061     // private
26062     setLegend : function(text){
26063         if(this.rendered){
26064             this.el.child('legend').update(text);
26065         }
26066     }
26067 });/*
26068  * Based on:
26069  * Ext JS Library 1.1.1
26070  * Copyright(c) 2006-2007, Ext JS, LLC.
26071  *
26072  * Originally Released Under LGPL - original licence link has changed is not relivant.
26073  *
26074  * Fork - LGPL
26075  * <script type="text/javascript">
26076  */
26077 /**
26078  * @class Roo.form.VTypes
26079  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26080  * @singleton
26081  */
26082 Roo.form.VTypes = function(){
26083     // closure these in so they are only created once.
26084     var alpha = /^[a-zA-Z_]+$/;
26085     var alphanum = /^[a-zA-Z0-9_]+$/;
26086     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26087     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26088
26089     // All these messages and functions are configurable
26090     return {
26091         /**
26092          * The function used to validate email addresses
26093          * @param {String} value The email address
26094          */
26095         'email' : function(v){
26096             return email.test(v);
26097         },
26098         /**
26099          * The error text to display when the email validation function returns false
26100          * @type String
26101          */
26102         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26103         /**
26104          * The keystroke filter mask to be applied on email input
26105          * @type RegExp
26106          */
26107         'emailMask' : /[a-z0-9_\.\-@]/i,
26108
26109         /**
26110          * The function used to validate URLs
26111          * @param {String} value The URL
26112          */
26113         'url' : function(v){
26114             return url.test(v);
26115         },
26116         /**
26117          * The error text to display when the url validation function returns false
26118          * @type String
26119          */
26120         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26121         
26122         /**
26123          * The function used to validate alpha values
26124          * @param {String} value The value
26125          */
26126         'alpha' : function(v){
26127             return alpha.test(v);
26128         },
26129         /**
26130          * The error text to display when the alpha validation function returns false
26131          * @type String
26132          */
26133         'alphaText' : 'This field should only contain letters and _',
26134         /**
26135          * The keystroke filter mask to be applied on alpha input
26136          * @type RegExp
26137          */
26138         'alphaMask' : /[a-z_]/i,
26139
26140         /**
26141          * The function used to validate alphanumeric values
26142          * @param {String} value The value
26143          */
26144         'alphanum' : function(v){
26145             return alphanum.test(v);
26146         },
26147         /**
26148          * The error text to display when the alphanumeric validation function returns false
26149          * @type String
26150          */
26151         'alphanumText' : 'This field should only contain letters, numbers and _',
26152         /**
26153          * The keystroke filter mask to be applied on alphanumeric input
26154          * @type RegExp
26155          */
26156         'alphanumMask' : /[a-z0-9_]/i
26157     };
26158 }();//<script type="text/javascript">
26159
26160 /**
26161  * @class Roo.form.FCKeditor
26162  * @extends Roo.form.TextArea
26163  * Wrapper around the FCKEditor http://www.fckeditor.net
26164  * @constructor
26165  * Creates a new FCKeditor
26166  * @param {Object} config Configuration options
26167  */
26168 Roo.form.FCKeditor = function(config){
26169     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26170     this.addEvents({
26171          /**
26172          * @event editorinit
26173          * Fired when the editor is initialized - you can add extra handlers here..
26174          * @param {FCKeditor} this
26175          * @param {Object} the FCK object.
26176          */
26177         editorinit : true
26178     });
26179     
26180     
26181 };
26182 Roo.form.FCKeditor.editors = { };
26183 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26184 {
26185     //defaultAutoCreate : {
26186     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26187     //},
26188     // private
26189     /**
26190      * @cfg {Object} fck options - see fck manual for details.
26191      */
26192     fckconfig : false,
26193     
26194     /**
26195      * @cfg {Object} fck toolbar set (Basic or Default)
26196      */
26197     toolbarSet : 'Basic',
26198     /**
26199      * @cfg {Object} fck BasePath
26200      */ 
26201     basePath : '/fckeditor/',
26202     
26203     
26204     frame : false,
26205     
26206     value : '',
26207     
26208    
26209     onRender : function(ct, position)
26210     {
26211         if(!this.el){
26212             this.defaultAutoCreate = {
26213                 tag: "textarea",
26214                 style:"width:300px;height:60px;",
26215                 autocomplete: "new-password"
26216             };
26217         }
26218         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26219         /*
26220         if(this.grow){
26221             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26222             if(this.preventScrollbars){
26223                 this.el.setStyle("overflow", "hidden");
26224             }
26225             this.el.setHeight(this.growMin);
26226         }
26227         */
26228         //console.log('onrender' + this.getId() );
26229         Roo.form.FCKeditor.editors[this.getId()] = this;
26230          
26231
26232         this.replaceTextarea() ;
26233         
26234     },
26235     
26236     getEditor : function() {
26237         return this.fckEditor;
26238     },
26239     /**
26240      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26241      * @param {Mixed} value The value to set
26242      */
26243     
26244     
26245     setValue : function(value)
26246     {
26247         //console.log('setValue: ' + value);
26248         
26249         if(typeof(value) == 'undefined') { // not sure why this is happending...
26250             return;
26251         }
26252         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26253         
26254         //if(!this.el || !this.getEditor()) {
26255         //    this.value = value;
26256             //this.setValue.defer(100,this,[value]);    
26257         //    return;
26258         //} 
26259         
26260         if(!this.getEditor()) {
26261             return;
26262         }
26263         
26264         this.getEditor().SetData(value);
26265         
26266         //
26267
26268     },
26269
26270     /**
26271      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26272      * @return {Mixed} value The field value
26273      */
26274     getValue : function()
26275     {
26276         
26277         if (this.frame && this.frame.dom.style.display == 'none') {
26278             return Roo.form.FCKeditor.superclass.getValue.call(this);
26279         }
26280         
26281         if(!this.el || !this.getEditor()) {
26282            
26283            // this.getValue.defer(100,this); 
26284             return this.value;
26285         }
26286        
26287         
26288         var value=this.getEditor().GetData();
26289         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26290         return Roo.form.FCKeditor.superclass.getValue.call(this);
26291         
26292
26293     },
26294
26295     /**
26296      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26297      * @return {Mixed} value The field value
26298      */
26299     getRawValue : function()
26300     {
26301         if (this.frame && this.frame.dom.style.display == 'none') {
26302             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26303         }
26304         
26305         if(!this.el || !this.getEditor()) {
26306             //this.getRawValue.defer(100,this); 
26307             return this.value;
26308             return;
26309         }
26310         
26311         
26312         
26313         var value=this.getEditor().GetData();
26314         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26315         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26316          
26317     },
26318     
26319     setSize : function(w,h) {
26320         
26321         
26322         
26323         //if (this.frame && this.frame.dom.style.display == 'none') {
26324         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26325         //    return;
26326         //}
26327         //if(!this.el || !this.getEditor()) {
26328         //    this.setSize.defer(100,this, [w,h]); 
26329         //    return;
26330         //}
26331         
26332         
26333         
26334         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26335         
26336         this.frame.dom.setAttribute('width', w);
26337         this.frame.dom.setAttribute('height', h);
26338         this.frame.setSize(w,h);
26339         
26340     },
26341     
26342     toggleSourceEdit : function(value) {
26343         
26344       
26345          
26346         this.el.dom.style.display = value ? '' : 'none';
26347         this.frame.dom.style.display = value ?  'none' : '';
26348         
26349     },
26350     
26351     
26352     focus: function(tag)
26353     {
26354         if (this.frame.dom.style.display == 'none') {
26355             return Roo.form.FCKeditor.superclass.focus.call(this);
26356         }
26357         if(!this.el || !this.getEditor()) {
26358             this.focus.defer(100,this, [tag]); 
26359             return;
26360         }
26361         
26362         
26363         
26364         
26365         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26366         this.getEditor().Focus();
26367         if (tgs.length) {
26368             if (!this.getEditor().Selection.GetSelection()) {
26369                 this.focus.defer(100,this, [tag]); 
26370                 return;
26371             }
26372             
26373             
26374             var r = this.getEditor().EditorDocument.createRange();
26375             r.setStart(tgs[0],0);
26376             r.setEnd(tgs[0],0);
26377             this.getEditor().Selection.GetSelection().removeAllRanges();
26378             this.getEditor().Selection.GetSelection().addRange(r);
26379             this.getEditor().Focus();
26380         }
26381         
26382     },
26383     
26384     
26385     
26386     replaceTextarea : function()
26387     {
26388         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26389             return ;
26390         }
26391         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26392         //{
26393             // We must check the elements firstly using the Id and then the name.
26394         var oTextarea = document.getElementById( this.getId() );
26395         
26396         var colElementsByName = document.getElementsByName( this.getId() ) ;
26397          
26398         oTextarea.style.display = 'none' ;
26399
26400         if ( oTextarea.tabIndex ) {            
26401             this.TabIndex = oTextarea.tabIndex ;
26402         }
26403         
26404         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26405         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26406         this.frame = Roo.get(this.getId() + '___Frame')
26407     },
26408     
26409     _getConfigHtml : function()
26410     {
26411         var sConfig = '' ;
26412
26413         for ( var o in this.fckconfig ) {
26414             sConfig += sConfig.length > 0  ? '&amp;' : '';
26415             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26416         }
26417
26418         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26419     },
26420     
26421     
26422     _getIFrameHtml : function()
26423     {
26424         var sFile = 'fckeditor.html' ;
26425         /* no idea what this is about..
26426         try
26427         {
26428             if ( (/fcksource=true/i).test( window.top.location.search ) )
26429                 sFile = 'fckeditor.original.html' ;
26430         }
26431         catch (e) { 
26432         */
26433
26434         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26435         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26436         
26437         
26438         var html = '<iframe id="' + this.getId() +
26439             '___Frame" src="' + sLink +
26440             '" width="' + this.width +
26441             '" height="' + this.height + '"' +
26442             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26443             ' frameborder="0" scrolling="no"></iframe>' ;
26444
26445         return html ;
26446     },
26447     
26448     _insertHtmlBefore : function( html, element )
26449     {
26450         if ( element.insertAdjacentHTML )       {
26451             // IE
26452             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26453         } else { // Gecko
26454             var oRange = document.createRange() ;
26455             oRange.setStartBefore( element ) ;
26456             var oFragment = oRange.createContextualFragment( html );
26457             element.parentNode.insertBefore( oFragment, element ) ;
26458         }
26459     }
26460     
26461     
26462   
26463     
26464     
26465     
26466     
26467
26468 });
26469
26470 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26471
26472 function FCKeditor_OnComplete(editorInstance){
26473     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26474     f.fckEditor = editorInstance;
26475     //console.log("loaded");
26476     f.fireEvent('editorinit', f, editorInstance);
26477
26478   
26479
26480  
26481
26482
26483
26484
26485
26486
26487
26488
26489
26490
26491
26492
26493
26494
26495
26496 //<script type="text/javascript">
26497 /**
26498  * @class Roo.form.GridField
26499  * @extends Roo.form.Field
26500  * Embed a grid (or editable grid into a form)
26501  * STATUS ALPHA
26502  * 
26503  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26504  * it needs 
26505  * xgrid.store = Roo.data.Store
26506  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26507  * xgrid.store.reader = Roo.data.JsonReader 
26508  * 
26509  * 
26510  * @constructor
26511  * Creates a new GridField
26512  * @param {Object} config Configuration options
26513  */
26514 Roo.form.GridField = function(config){
26515     Roo.form.GridField.superclass.constructor.call(this, config);
26516      
26517 };
26518
26519 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26520     /**
26521      * @cfg {Number} width  - used to restrict width of grid..
26522      */
26523     width : 100,
26524     /**
26525      * @cfg {Number} height - used to restrict height of grid..
26526      */
26527     height : 50,
26528      /**
26529      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26530          * 
26531          *}
26532      */
26533     xgrid : false, 
26534     /**
26535      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26536      * {tag: "input", type: "checkbox", autocomplete: "off"})
26537      */
26538    // defaultAutoCreate : { tag: 'div' },
26539     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26540     /**
26541      * @cfg {String} addTitle Text to include for adding a title.
26542      */
26543     addTitle : false,
26544     //
26545     onResize : function(){
26546         Roo.form.Field.superclass.onResize.apply(this, arguments);
26547     },
26548
26549     initEvents : function(){
26550         // Roo.form.Checkbox.superclass.initEvents.call(this);
26551         // has no events...
26552        
26553     },
26554
26555
26556     getResizeEl : function(){
26557         return this.wrap;
26558     },
26559
26560     getPositionEl : function(){
26561         return this.wrap;
26562     },
26563
26564     // private
26565     onRender : function(ct, position){
26566         
26567         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26568         var style = this.style;
26569         delete this.style;
26570         
26571         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26572         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26573         this.viewEl = this.wrap.createChild({ tag: 'div' });
26574         if (style) {
26575             this.viewEl.applyStyles(style);
26576         }
26577         if (this.width) {
26578             this.viewEl.setWidth(this.width);
26579         }
26580         if (this.height) {
26581             this.viewEl.setHeight(this.height);
26582         }
26583         //if(this.inputValue !== undefined){
26584         //this.setValue(this.value);
26585         
26586         
26587         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26588         
26589         
26590         this.grid.render();
26591         this.grid.getDataSource().on('remove', this.refreshValue, this);
26592         this.grid.getDataSource().on('update', this.refreshValue, this);
26593         this.grid.on('afteredit', this.refreshValue, this);
26594  
26595     },
26596      
26597     
26598     /**
26599      * Sets the value of the item. 
26600      * @param {String} either an object  or a string..
26601      */
26602     setValue : function(v){
26603         //this.value = v;
26604         v = v || []; // empty set..
26605         // this does not seem smart - it really only affects memoryproxy grids..
26606         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26607             var ds = this.grid.getDataSource();
26608             // assumes a json reader..
26609             var data = {}
26610             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26611             ds.loadData( data);
26612         }
26613         // clear selection so it does not get stale.
26614         if (this.grid.sm) { 
26615             this.grid.sm.clearSelections();
26616         }
26617         
26618         Roo.form.GridField.superclass.setValue.call(this, v);
26619         this.refreshValue();
26620         // should load data in the grid really....
26621     },
26622     
26623     // private
26624     refreshValue: function() {
26625          var val = [];
26626         this.grid.getDataSource().each(function(r) {
26627             val.push(r.data);
26628         });
26629         this.el.dom.value = Roo.encode(val);
26630     }
26631     
26632      
26633     
26634     
26635 });/*
26636  * Based on:
26637  * Ext JS Library 1.1.1
26638  * Copyright(c) 2006-2007, Ext JS, LLC.
26639  *
26640  * Originally Released Under LGPL - original licence link has changed is not relivant.
26641  *
26642  * Fork - LGPL
26643  * <script type="text/javascript">
26644  */
26645 /**
26646  * @class Roo.form.DisplayField
26647  * @extends Roo.form.Field
26648  * A generic Field to display non-editable data.
26649  * @cfg {Boolean} closable (true|false) default false
26650  * @constructor
26651  * Creates a new Display Field item.
26652  * @param {Object} config Configuration options
26653  */
26654 Roo.form.DisplayField = function(config){
26655     Roo.form.DisplayField.superclass.constructor.call(this, config);
26656     
26657     this.addEvents({
26658         /**
26659          * @event close
26660          * Fires after the click the close btn
26661              * @param {Roo.form.DisplayField} this
26662              */
26663         close : true
26664     });
26665 };
26666
26667 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26668     inputType:      'hidden',
26669     allowBlank:     true,
26670     readOnly:         true,
26671     
26672  
26673     /**
26674      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26675      */
26676     focusClass : undefined,
26677     /**
26678      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26679      */
26680     fieldClass: 'x-form-field',
26681     
26682      /**
26683      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26684      */
26685     valueRenderer: undefined,
26686     
26687     width: 100,
26688     /**
26689      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26690      * {tag: "input", type: "checkbox", autocomplete: "off"})
26691      */
26692      
26693  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26694  
26695     closable : false,
26696     
26697     onResize : function(){
26698         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26699         
26700     },
26701
26702     initEvents : function(){
26703         // Roo.form.Checkbox.superclass.initEvents.call(this);
26704         // has no events...
26705         
26706         if(this.closable){
26707             this.closeEl.on('click', this.onClose, this);
26708         }
26709        
26710     },
26711
26712
26713     getResizeEl : function(){
26714         return this.wrap;
26715     },
26716
26717     getPositionEl : function(){
26718         return this.wrap;
26719     },
26720
26721     // private
26722     onRender : function(ct, position){
26723         
26724         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
26725         //if(this.inputValue !== undefined){
26726         this.wrap = this.el.wrap();
26727         
26728         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
26729         
26730         if(this.closable){
26731             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
26732         }
26733         
26734         if (this.bodyStyle) {
26735             this.viewEl.applyStyles(this.bodyStyle);
26736         }
26737         //this.viewEl.setStyle('padding', '2px');
26738         
26739         this.setValue(this.value);
26740         
26741     },
26742 /*
26743     // private
26744     initValue : Roo.emptyFn,
26745
26746   */
26747
26748         // private
26749     onClick : function(){
26750         
26751     },
26752
26753     /**
26754      * Sets the checked state of the checkbox.
26755      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
26756      */
26757     setValue : function(v){
26758         this.value = v;
26759         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
26760         // this might be called before we have a dom element..
26761         if (!this.viewEl) {
26762             return;
26763         }
26764         this.viewEl.dom.innerHTML = html;
26765         Roo.form.DisplayField.superclass.setValue.call(this, v);
26766
26767     },
26768     
26769     onClose : function(e)
26770     {
26771         e.preventDefault();
26772         
26773         this.fireEvent('close', this);
26774     }
26775 });/*
26776  * 
26777  * Licence- LGPL
26778  * 
26779  */
26780
26781 /**
26782  * @class Roo.form.DayPicker
26783  * @extends Roo.form.Field
26784  * A Day picker show [M] [T] [W] ....
26785  * @constructor
26786  * Creates a new Day Picker
26787  * @param {Object} config Configuration options
26788  */
26789 Roo.form.DayPicker= function(config){
26790     Roo.form.DayPicker.superclass.constructor.call(this, config);
26791      
26792 };
26793
26794 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
26795     /**
26796      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26797      */
26798     focusClass : undefined,
26799     /**
26800      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26801      */
26802     fieldClass: "x-form-field",
26803    
26804     /**
26805      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26806      * {tag: "input", type: "checkbox", autocomplete: "off"})
26807      */
26808     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
26809     
26810    
26811     actionMode : 'viewEl', 
26812     //
26813     // private
26814  
26815     inputType : 'hidden',
26816     
26817      
26818     inputElement: false, // real input element?
26819     basedOn: false, // ????
26820     
26821     isFormField: true, // not sure where this is needed!!!!
26822
26823     onResize : function(){
26824         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
26825         if(!this.boxLabel){
26826             this.el.alignTo(this.wrap, 'c-c');
26827         }
26828     },
26829
26830     initEvents : function(){
26831         Roo.form.Checkbox.superclass.initEvents.call(this);
26832         this.el.on("click", this.onClick,  this);
26833         this.el.on("change", this.onClick,  this);
26834     },
26835
26836
26837     getResizeEl : function(){
26838         return this.wrap;
26839     },
26840
26841     getPositionEl : function(){
26842         return this.wrap;
26843     },
26844
26845     
26846     // private
26847     onRender : function(ct, position){
26848         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
26849        
26850         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
26851         
26852         var r1 = '<table><tr>';
26853         var r2 = '<tr class="x-form-daypick-icons">';
26854         for (var i=0; i < 7; i++) {
26855             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
26856             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
26857         }
26858         
26859         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
26860         viewEl.select('img').on('click', this.onClick, this);
26861         this.viewEl = viewEl;   
26862         
26863         
26864         // this will not work on Chrome!!!
26865         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
26866         this.el.on('propertychange', this.setFromHidden,  this);  //ie
26867         
26868         
26869           
26870
26871     },
26872
26873     // private
26874     initValue : Roo.emptyFn,
26875
26876     /**
26877      * Returns the checked state of the checkbox.
26878      * @return {Boolean} True if checked, else false
26879      */
26880     getValue : function(){
26881         return this.el.dom.value;
26882         
26883     },
26884
26885         // private
26886     onClick : function(e){ 
26887         //this.setChecked(!this.checked);
26888         Roo.get(e.target).toggleClass('x-menu-item-checked');
26889         this.refreshValue();
26890         //if(this.el.dom.checked != this.checked){
26891         //    this.setValue(this.el.dom.checked);
26892        // }
26893     },
26894     
26895     // private
26896     refreshValue : function()
26897     {
26898         var val = '';
26899         this.viewEl.select('img',true).each(function(e,i,n)  {
26900             val += e.is(".x-menu-item-checked") ? String(n) : '';
26901         });
26902         this.setValue(val, true);
26903     },
26904
26905     /**
26906      * Sets the checked state of the checkbox.
26907      * On is always based on a string comparison between inputValue and the param.
26908      * @param {Boolean/String} value - the value to set 
26909      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
26910      */
26911     setValue : function(v,suppressEvent){
26912         if (!this.el.dom) {
26913             return;
26914         }
26915         var old = this.el.dom.value ;
26916         this.el.dom.value = v;
26917         if (suppressEvent) {
26918             return ;
26919         }
26920          
26921         // update display..
26922         this.viewEl.select('img',true).each(function(e,i,n)  {
26923             
26924             var on = e.is(".x-menu-item-checked");
26925             var newv = v.indexOf(String(n)) > -1;
26926             if (on != newv) {
26927                 e.toggleClass('x-menu-item-checked');
26928             }
26929             
26930         });
26931         
26932         
26933         this.fireEvent('change', this, v, old);
26934         
26935         
26936     },
26937    
26938     // handle setting of hidden value by some other method!!?!?
26939     setFromHidden: function()
26940     {
26941         if(!this.el){
26942             return;
26943         }
26944         //console.log("SET FROM HIDDEN");
26945         //alert('setFrom hidden');
26946         this.setValue(this.el.dom.value);
26947     },
26948     
26949     onDestroy : function()
26950     {
26951         if(this.viewEl){
26952             Roo.get(this.viewEl).remove();
26953         }
26954          
26955         Roo.form.DayPicker.superclass.onDestroy.call(this);
26956     }
26957
26958 });/*
26959  * RooJS Library 1.1.1
26960  * Copyright(c) 2008-2011  Alan Knowles
26961  *
26962  * License - LGPL
26963  */
26964  
26965
26966 /**
26967  * @class Roo.form.ComboCheck
26968  * @extends Roo.form.ComboBox
26969  * A combobox for multiple select items.
26970  *
26971  * FIXME - could do with a reset button..
26972  * 
26973  * @constructor
26974  * Create a new ComboCheck
26975  * @param {Object} config Configuration options
26976  */
26977 Roo.form.ComboCheck = function(config){
26978     Roo.form.ComboCheck.superclass.constructor.call(this, config);
26979     // should verify some data...
26980     // like
26981     // hiddenName = required..
26982     // displayField = required
26983     // valudField == required
26984     var req= [ 'hiddenName', 'displayField', 'valueField' ];
26985     var _t = this;
26986     Roo.each(req, function(e) {
26987         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
26988             throw "Roo.form.ComboCheck : missing value for: " + e;
26989         }
26990     });
26991     
26992     
26993 };
26994
26995 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
26996      
26997      
26998     editable : false,
26999      
27000     selectedClass: 'x-menu-item-checked', 
27001     
27002     // private
27003     onRender : function(ct, position){
27004         var _t = this;
27005         
27006         
27007         
27008         if(!this.tpl){
27009             var cls = 'x-combo-list';
27010
27011             
27012             this.tpl =  new Roo.Template({
27013                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27014                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27015                    '<span>{' + this.displayField + '}</span>' +
27016                     '</div>' 
27017                 
27018             });
27019         }
27020  
27021         
27022         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27023         this.view.singleSelect = false;
27024         this.view.multiSelect = true;
27025         this.view.toggleSelect = true;
27026         this.pageTb.add(new Roo.Toolbar.Fill(), {
27027             
27028             text: 'Done',
27029             handler: function()
27030             {
27031                 _t.collapse();
27032             }
27033         });
27034     },
27035     
27036     onViewOver : function(e, t){
27037         // do nothing...
27038         return;
27039         
27040     },
27041     
27042     onViewClick : function(doFocus,index){
27043         return;
27044         
27045     },
27046     select: function () {
27047         //Roo.log("SELECT CALLED");
27048     },
27049      
27050     selectByValue : function(xv, scrollIntoView){
27051         var ar = this.getValueArray();
27052         var sels = [];
27053         
27054         Roo.each(ar, function(v) {
27055             if(v === undefined || v === null){
27056                 return;
27057             }
27058             var r = this.findRecord(this.valueField, v);
27059             if(r){
27060                 sels.push(this.store.indexOf(r))
27061                 
27062             }
27063         },this);
27064         this.view.select(sels);
27065         return false;
27066     },
27067     
27068     
27069     
27070     onSelect : function(record, index){
27071        // Roo.log("onselect Called");
27072        // this is only called by the clear button now..
27073         this.view.clearSelections();
27074         this.setValue('[]');
27075         if (this.value != this.valueBefore) {
27076             this.fireEvent('change', this, this.value, this.valueBefore);
27077             this.valueBefore = this.value;
27078         }
27079     },
27080     getValueArray : function()
27081     {
27082         var ar = [] ;
27083         
27084         try {
27085             //Roo.log(this.value);
27086             if (typeof(this.value) == 'undefined') {
27087                 return [];
27088             }
27089             var ar = Roo.decode(this.value);
27090             return  ar instanceof Array ? ar : []; //?? valid?
27091             
27092         } catch(e) {
27093             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27094             return [];
27095         }
27096          
27097     },
27098     expand : function ()
27099     {
27100         
27101         Roo.form.ComboCheck.superclass.expand.call(this);
27102         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27103         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27104         
27105
27106     },
27107     
27108     collapse : function(){
27109         Roo.form.ComboCheck.superclass.collapse.call(this);
27110         var sl = this.view.getSelectedIndexes();
27111         var st = this.store;
27112         var nv = [];
27113         var tv = [];
27114         var r;
27115         Roo.each(sl, function(i) {
27116             r = st.getAt(i);
27117             nv.push(r.get(this.valueField));
27118         },this);
27119         this.setValue(Roo.encode(nv));
27120         if (this.value != this.valueBefore) {
27121
27122             this.fireEvent('change', this, this.value, this.valueBefore);
27123             this.valueBefore = this.value;
27124         }
27125         
27126     },
27127     
27128     setValue : function(v){
27129         // Roo.log(v);
27130         this.value = v;
27131         
27132         var vals = this.getValueArray();
27133         var tv = [];
27134         Roo.each(vals, function(k) {
27135             var r = this.findRecord(this.valueField, k);
27136             if(r){
27137                 tv.push(r.data[this.displayField]);
27138             }else if(this.valueNotFoundText !== undefined){
27139                 tv.push( this.valueNotFoundText );
27140             }
27141         },this);
27142        // Roo.log(tv);
27143         
27144         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27145         this.hiddenField.value = v;
27146         this.value = v;
27147     }
27148     
27149 });/*
27150  * Based on:
27151  * Ext JS Library 1.1.1
27152  * Copyright(c) 2006-2007, Ext JS, LLC.
27153  *
27154  * Originally Released Under LGPL - original licence link has changed is not relivant.
27155  *
27156  * Fork - LGPL
27157  * <script type="text/javascript">
27158  */
27159  
27160 /**
27161  * @class Roo.form.Signature
27162  * @extends Roo.form.Field
27163  * Signature field.  
27164  * @constructor
27165  * 
27166  * @param {Object} config Configuration options
27167  */
27168
27169 Roo.form.Signature = function(config){
27170     Roo.form.Signature.superclass.constructor.call(this, config);
27171     
27172     this.addEvents({// not in used??
27173          /**
27174          * @event confirm
27175          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27176              * @param {Roo.form.Signature} combo This combo box
27177              */
27178         'confirm' : true,
27179         /**
27180          * @event reset
27181          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27182              * @param {Roo.form.ComboBox} combo This combo box
27183              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27184              */
27185         'reset' : true
27186     });
27187 };
27188
27189 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27190     /**
27191      * @cfg {Object} labels Label to use when rendering a form.
27192      * defaults to 
27193      * labels : { 
27194      *      clear : "Clear",
27195      *      confirm : "Confirm"
27196      *  }
27197      */
27198     labels : { 
27199         clear : "Clear",
27200         confirm : "Confirm"
27201     },
27202     /**
27203      * @cfg {Number} width The signature panel width (defaults to 300)
27204      */
27205     width: 300,
27206     /**
27207      * @cfg {Number} height The signature panel height (defaults to 100)
27208      */
27209     height : 100,
27210     /**
27211      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27212      */
27213     allowBlank : false,
27214     
27215     //private
27216     // {Object} signPanel The signature SVG panel element (defaults to {})
27217     signPanel : {},
27218     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27219     isMouseDown : false,
27220     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27221     isConfirmed : false,
27222     // {String} signatureTmp SVG mapping string (defaults to empty string)
27223     signatureTmp : '',
27224     
27225     
27226     defaultAutoCreate : { // modified by initCompnoent..
27227         tag: "input",
27228         type:"hidden"
27229     },
27230
27231     // private
27232     onRender : function(ct, position){
27233         
27234         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27235         
27236         this.wrap = this.el.wrap({
27237             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27238         });
27239         
27240         this.createToolbar(this);
27241         this.signPanel = this.wrap.createChild({
27242                 tag: 'div',
27243                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27244             }, this.el
27245         );
27246             
27247         this.svgID = Roo.id();
27248         this.svgEl = this.signPanel.createChild({
27249               xmlns : 'http://www.w3.org/2000/svg',
27250               tag : 'svg',
27251               id : this.svgID + "-svg",
27252               width: this.width,
27253               height: this.height,
27254               viewBox: '0 0 '+this.width+' '+this.height,
27255               cn : [
27256                 {
27257                     tag: "rect",
27258                     id: this.svgID + "-svg-r",
27259                     width: this.width,
27260                     height: this.height,
27261                     fill: "#ffa"
27262                 },
27263                 {
27264                     tag: "line",
27265                     id: this.svgID + "-svg-l",
27266                     x1: "0", // start
27267                     y1: (this.height*0.8), // start set the line in 80% of height
27268                     x2: this.width, // end
27269                     y2: (this.height*0.8), // end set the line in 80% of height
27270                     'stroke': "#666",
27271                     'stroke-width': "1",
27272                     'stroke-dasharray': "3",
27273                     'shape-rendering': "crispEdges",
27274                     'pointer-events': "none"
27275                 },
27276                 {
27277                     tag: "path",
27278                     id: this.svgID + "-svg-p",
27279                     'stroke': "navy",
27280                     'stroke-width': "3",
27281                     'fill': "none",
27282                     'pointer-events': 'none'
27283                 }
27284               ]
27285         });
27286         this.createSVG();
27287         this.svgBox = this.svgEl.dom.getScreenCTM();
27288     },
27289     createSVG : function(){ 
27290         var svg = this.signPanel;
27291         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27292         var t = this;
27293
27294         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27295         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27296         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27297         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27298         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27299         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27300         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27301         
27302     },
27303     isTouchEvent : function(e){
27304         return e.type.match(/^touch/);
27305     },
27306     getCoords : function (e) {
27307         var pt    = this.svgEl.dom.createSVGPoint();
27308         pt.x = e.clientX; 
27309         pt.y = e.clientY;
27310         if (this.isTouchEvent(e)) {
27311             pt.x =  e.targetTouches[0].clientX;
27312             pt.y = e.targetTouches[0].clientY;
27313         }
27314         var a = this.svgEl.dom.getScreenCTM();
27315         var b = a.inverse();
27316         var mx = pt.matrixTransform(b);
27317         return mx.x + ',' + mx.y;
27318     },
27319     //mouse event headler 
27320     down : function (e) {
27321         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27322         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27323         
27324         this.isMouseDown = true;
27325         
27326         e.preventDefault();
27327     },
27328     move : function (e) {
27329         if (this.isMouseDown) {
27330             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27331             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27332         }
27333         
27334         e.preventDefault();
27335     },
27336     up : function (e) {
27337         this.isMouseDown = false;
27338         var sp = this.signatureTmp.split(' ');
27339         
27340         if(sp.length > 1){
27341             if(!sp[sp.length-2].match(/^L/)){
27342                 sp.pop();
27343                 sp.pop();
27344                 sp.push("");
27345                 this.signatureTmp = sp.join(" ");
27346             }
27347         }
27348         if(this.getValue() != this.signatureTmp){
27349             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27350             this.isConfirmed = false;
27351         }
27352         e.preventDefault();
27353     },
27354     
27355     /**
27356      * Protected method that will not generally be called directly. It
27357      * is called when the editor creates its toolbar. Override this method if you need to
27358      * add custom toolbar buttons.
27359      * @param {HtmlEditor} editor
27360      */
27361     createToolbar : function(editor){
27362          function btn(id, toggle, handler){
27363             var xid = fid + '-'+ id ;
27364             return {
27365                 id : xid,
27366                 cmd : id,
27367                 cls : 'x-btn-icon x-edit-'+id,
27368                 enableToggle:toggle !== false,
27369                 scope: editor, // was editor...
27370                 handler:handler||editor.relayBtnCmd,
27371                 clickEvent:'mousedown',
27372                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27373                 tabIndex:-1
27374             };
27375         }
27376         
27377         
27378         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27379         this.tb = tb;
27380         this.tb.add(
27381            {
27382                 cls : ' x-signature-btn x-signature-'+id,
27383                 scope: editor, // was editor...
27384                 handler: this.reset,
27385                 clickEvent:'mousedown',
27386                 text: this.labels.clear
27387             },
27388             {
27389                  xtype : 'Fill',
27390                  xns: Roo.Toolbar
27391             }, 
27392             {
27393                 cls : '  x-signature-btn x-signature-'+id,
27394                 scope: editor, // was editor...
27395                 handler: this.confirmHandler,
27396                 clickEvent:'mousedown',
27397                 text: this.labels.confirm
27398             }
27399         );
27400     
27401     },
27402     //public
27403     /**
27404      * when user is clicked confirm then show this image.....
27405      * 
27406      * @return {String} Image Data URI
27407      */
27408     getImageDataURI : function(){
27409         var svg = this.svgEl.dom.parentNode.innerHTML;
27410         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27411         return src; 
27412     },
27413     /**
27414      * 
27415      * @return {Boolean} this.isConfirmed
27416      */
27417     getConfirmed : function(){
27418         return this.isConfirmed;
27419     },
27420     /**
27421      * 
27422      * @return {Number} this.width
27423      */
27424     getWidth : function(){
27425         return this.width;
27426     },
27427     /**
27428      * 
27429      * @return {Number} this.height
27430      */
27431     getHeight : function(){
27432         return this.height;
27433     },
27434     // private
27435     getSignature : function(){
27436         return this.signatureTmp;
27437     },
27438     // private
27439     reset : function(){
27440         this.signatureTmp = '';
27441         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27442         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27443         this.isConfirmed = false;
27444         Roo.form.Signature.superclass.reset.call(this);
27445     },
27446     setSignature : function(s){
27447         this.signatureTmp = s;
27448         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27449         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27450         this.setValue(s);
27451         this.isConfirmed = false;
27452         Roo.form.Signature.superclass.reset.call(this);
27453     }, 
27454     test : function(){
27455 //        Roo.log(this.signPanel.dom.contentWindow.up())
27456     },
27457     //private
27458     setConfirmed : function(){
27459         
27460         
27461         
27462 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27463     },
27464     // private
27465     confirmHandler : function(){
27466         if(!this.getSignature()){
27467             return;
27468         }
27469         
27470         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27471         this.setValue(this.getSignature());
27472         this.isConfirmed = true;
27473         
27474         this.fireEvent('confirm', this);
27475     },
27476     // private
27477     // Subclasses should provide the validation implementation by overriding this
27478     validateValue : function(value){
27479         if(this.allowBlank){
27480             return true;
27481         }
27482         
27483         if(this.isConfirmed){
27484             return true;
27485         }
27486         return false;
27487     }
27488 });/*
27489  * Based on:
27490  * Ext JS Library 1.1.1
27491  * Copyright(c) 2006-2007, Ext JS, LLC.
27492  *
27493  * Originally Released Under LGPL - original licence link has changed is not relivant.
27494  *
27495  * Fork - LGPL
27496  * <script type="text/javascript">
27497  */
27498  
27499
27500 /**
27501  * @class Roo.form.ComboBox
27502  * @extends Roo.form.TriggerField
27503  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27504  * @constructor
27505  * Create a new ComboBox.
27506  * @param {Object} config Configuration options
27507  */
27508 Roo.form.Select = function(config){
27509     Roo.form.Select.superclass.constructor.call(this, config);
27510      
27511 };
27512
27513 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27514     /**
27515      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27516      */
27517     /**
27518      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27519      * rendering into an Roo.Editor, defaults to false)
27520      */
27521     /**
27522      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27523      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27524      */
27525     /**
27526      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27527      */
27528     /**
27529      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27530      * the dropdown list (defaults to undefined, with no header element)
27531      */
27532
27533      /**
27534      * @cfg {String/Roo.Template} tpl The template to use to render the output
27535      */
27536      
27537     // private
27538     defaultAutoCreate : {tag: "select"  },
27539     /**
27540      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27541      */
27542     listWidth: undefined,
27543     /**
27544      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27545      * mode = 'remote' or 'text' if mode = 'local')
27546      */
27547     displayField: undefined,
27548     /**
27549      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27550      * mode = 'remote' or 'value' if mode = 'local'). 
27551      * Note: use of a valueField requires the user make a selection
27552      * in order for a value to be mapped.
27553      */
27554     valueField: undefined,
27555     
27556     
27557     /**
27558      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27559      * field's data value (defaults to the underlying DOM element's name)
27560      */
27561     hiddenName: undefined,
27562     /**
27563      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27564      */
27565     listClass: '',
27566     /**
27567      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27568      */
27569     selectedClass: 'x-combo-selected',
27570     /**
27571      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27572      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27573      * which displays a downward arrow icon).
27574      */
27575     triggerClass : 'x-form-arrow-trigger',
27576     /**
27577      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27578      */
27579     shadow:'sides',
27580     /**
27581      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27582      * anchor positions (defaults to 'tl-bl')
27583      */
27584     listAlign: 'tl-bl?',
27585     /**
27586      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27587      */
27588     maxHeight: 300,
27589     /**
27590      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27591      * query specified by the allQuery config option (defaults to 'query')
27592      */
27593     triggerAction: 'query',
27594     /**
27595      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27596      * (defaults to 4, does not apply if editable = false)
27597      */
27598     minChars : 4,
27599     /**
27600      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27601      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27602      */
27603     typeAhead: false,
27604     /**
27605      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27606      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27607      */
27608     queryDelay: 500,
27609     /**
27610      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27611      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27612      */
27613     pageSize: 0,
27614     /**
27615      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27616      * when editable = true (defaults to false)
27617      */
27618     selectOnFocus:false,
27619     /**
27620      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27621      */
27622     queryParam: 'query',
27623     /**
27624      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27625      * when mode = 'remote' (defaults to 'Loading...')
27626      */
27627     loadingText: 'Loading...',
27628     /**
27629      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27630      */
27631     resizable: false,
27632     /**
27633      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27634      */
27635     handleHeight : 8,
27636     /**
27637      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27638      * traditional select (defaults to true)
27639      */
27640     editable: true,
27641     /**
27642      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27643      */
27644     allQuery: '',
27645     /**
27646      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27647      */
27648     mode: 'remote',
27649     /**
27650      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27651      * listWidth has a higher value)
27652      */
27653     minListWidth : 70,
27654     /**
27655      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27656      * allow the user to set arbitrary text into the field (defaults to false)
27657      */
27658     forceSelection:false,
27659     /**
27660      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27661      * if typeAhead = true (defaults to 250)
27662      */
27663     typeAheadDelay : 250,
27664     /**
27665      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27666      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27667      */
27668     valueNotFoundText : undefined,
27669     
27670     /**
27671      * @cfg {String} defaultValue The value displayed after loading the store.
27672      */
27673     defaultValue: '',
27674     
27675     /**
27676      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27677      */
27678     blockFocus : false,
27679     
27680     /**
27681      * @cfg {Boolean} disableClear Disable showing of clear button.
27682      */
27683     disableClear : false,
27684     /**
27685      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
27686      */
27687     alwaysQuery : false,
27688     
27689     //private
27690     addicon : false,
27691     editicon: false,
27692     
27693     // element that contains real text value.. (when hidden is used..)
27694      
27695     // private
27696     onRender : function(ct, position){
27697         Roo.form.Field.prototype.onRender.call(this, ct, position);
27698         
27699         if(this.store){
27700             this.store.on('beforeload', this.onBeforeLoad, this);
27701             this.store.on('load', this.onLoad, this);
27702             this.store.on('loadexception', this.onLoadException, this);
27703             this.store.load({});
27704         }
27705         
27706         
27707         
27708     },
27709
27710     // private
27711     initEvents : function(){
27712         //Roo.form.ComboBox.superclass.initEvents.call(this);
27713  
27714     },
27715
27716     onDestroy : function(){
27717        
27718         if(this.store){
27719             this.store.un('beforeload', this.onBeforeLoad, this);
27720             this.store.un('load', this.onLoad, this);
27721             this.store.un('loadexception', this.onLoadException, this);
27722         }
27723         //Roo.form.ComboBox.superclass.onDestroy.call(this);
27724     },
27725
27726     // private
27727     fireKey : function(e){
27728         if(e.isNavKeyPress() && !this.list.isVisible()){
27729             this.fireEvent("specialkey", this, e);
27730         }
27731     },
27732
27733     // private
27734     onResize: function(w, h){
27735         
27736         return; 
27737     
27738         
27739     },
27740
27741     /**
27742      * Allow or prevent the user from directly editing the field text.  If false is passed,
27743      * the user will only be able to select from the items defined in the dropdown list.  This method
27744      * is the runtime equivalent of setting the 'editable' config option at config time.
27745      * @param {Boolean} value True to allow the user to directly edit the field text
27746      */
27747     setEditable : function(value){
27748          
27749     },
27750
27751     // private
27752     onBeforeLoad : function(){
27753         
27754         Roo.log("Select before load");
27755         return;
27756     
27757         this.innerList.update(this.loadingText ?
27758                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
27759         //this.restrictHeight();
27760         this.selectedIndex = -1;
27761     },
27762
27763     // private
27764     onLoad : function(){
27765
27766     
27767         var dom = this.el.dom;
27768         dom.innerHTML = '';
27769          var od = dom.ownerDocument;
27770          
27771         if (this.emptyText) {
27772             var op = od.createElement('option');
27773             op.setAttribute('value', '');
27774             op.innerHTML = String.format('{0}', this.emptyText);
27775             dom.appendChild(op);
27776         }
27777         if(this.store.getCount() > 0){
27778            
27779             var vf = this.valueField;
27780             var df = this.displayField;
27781             this.store.data.each(function(r) {
27782                 // which colmsn to use... testing - cdoe / title..
27783                 var op = od.createElement('option');
27784                 op.setAttribute('value', r.data[vf]);
27785                 op.innerHTML = String.format('{0}', r.data[df]);
27786                 dom.appendChild(op);
27787             });
27788             if (typeof(this.defaultValue != 'undefined')) {
27789                 this.setValue(this.defaultValue);
27790             }
27791             
27792              
27793         }else{
27794             //this.onEmptyResults();
27795         }
27796         //this.el.focus();
27797     },
27798     // private
27799     onLoadException : function()
27800     {
27801         dom.innerHTML = '';
27802             
27803         Roo.log("Select on load exception");
27804         return;
27805     
27806         this.collapse();
27807         Roo.log(this.store.reader.jsonData);
27808         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
27809             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
27810         }
27811         
27812         
27813     },
27814     // private
27815     onTypeAhead : function(){
27816          
27817     },
27818
27819     // private
27820     onSelect : function(record, index){
27821         Roo.log('on select?');
27822         return;
27823         if(this.fireEvent('beforeselect', this, record, index) !== false){
27824             this.setFromData(index > -1 ? record.data : false);
27825             this.collapse();
27826             this.fireEvent('select', this, record, index);
27827         }
27828     },
27829
27830     /**
27831      * Returns the currently selected field value or empty string if no value is set.
27832      * @return {String} value The selected value
27833      */
27834     getValue : function(){
27835         var dom = this.el.dom;
27836         this.value = dom.options[dom.selectedIndex].value;
27837         return this.value;
27838         
27839     },
27840
27841     /**
27842      * Clears any text/value currently set in the field
27843      */
27844     clearValue : function(){
27845         this.value = '';
27846         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
27847         
27848     },
27849
27850     /**
27851      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
27852      * will be displayed in the field.  If the value does not match the data value of an existing item,
27853      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
27854      * Otherwise the field will be blank (although the value will still be set).
27855      * @param {String} value The value to match
27856      */
27857     setValue : function(v){
27858         var d = this.el.dom;
27859         for (var i =0; i < d.options.length;i++) {
27860             if (v == d.options[i].value) {
27861                 d.selectedIndex = i;
27862                 this.value = v;
27863                 return;
27864             }
27865         }
27866         this.clearValue();
27867     },
27868     /**
27869      * @property {Object} the last set data for the element
27870      */
27871     
27872     lastData : false,
27873     /**
27874      * Sets the value of the field based on a object which is related to the record format for the store.
27875      * @param {Object} value the value to set as. or false on reset?
27876      */
27877     setFromData : function(o){
27878         Roo.log('setfrom data?');
27879          
27880         
27881         
27882     },
27883     // private
27884     reset : function(){
27885         this.clearValue();
27886     },
27887     // private
27888     findRecord : function(prop, value){
27889         
27890         return false;
27891     
27892         var record;
27893         if(this.store.getCount() > 0){
27894             this.store.each(function(r){
27895                 if(r.data[prop] == value){
27896                     record = r;
27897                     return false;
27898                 }
27899                 return true;
27900             });
27901         }
27902         return record;
27903     },
27904     
27905     getName: function()
27906     {
27907         // returns hidden if it's set..
27908         if (!this.rendered) {return ''};
27909         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
27910         
27911     },
27912      
27913
27914     
27915
27916     // private
27917     onEmptyResults : function(){
27918         Roo.log('empty results');
27919         //this.collapse();
27920     },
27921
27922     /**
27923      * Returns true if the dropdown list is expanded, else false.
27924      */
27925     isExpanded : function(){
27926         return false;
27927     },
27928
27929     /**
27930      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
27931      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27932      * @param {String} value The data value of the item to select
27933      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27934      * selected item if it is not currently in view (defaults to true)
27935      * @return {Boolean} True if the value matched an item in the list, else false
27936      */
27937     selectByValue : function(v, scrollIntoView){
27938         Roo.log('select By Value');
27939         return false;
27940     
27941         if(v !== undefined && v !== null){
27942             var r = this.findRecord(this.valueField || this.displayField, v);
27943             if(r){
27944                 this.select(this.store.indexOf(r), scrollIntoView);
27945                 return true;
27946             }
27947         }
27948         return false;
27949     },
27950
27951     /**
27952      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
27953      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27954      * @param {Number} index The zero-based index of the list item to select
27955      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27956      * selected item if it is not currently in view (defaults to true)
27957      */
27958     select : function(index, scrollIntoView){
27959         Roo.log('select ');
27960         return  ;
27961         
27962         this.selectedIndex = index;
27963         this.view.select(index);
27964         if(scrollIntoView !== false){
27965             var el = this.view.getNode(index);
27966             if(el){
27967                 this.innerList.scrollChildIntoView(el, false);
27968             }
27969         }
27970     },
27971
27972       
27973
27974     // private
27975     validateBlur : function(){
27976         
27977         return;
27978         
27979     },
27980
27981     // private
27982     initQuery : function(){
27983         this.doQuery(this.getRawValue());
27984     },
27985
27986     // private
27987     doForce : function(){
27988         if(this.el.dom.value.length > 0){
27989             this.el.dom.value =
27990                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
27991              
27992         }
27993     },
27994
27995     /**
27996      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
27997      * query allowing the query action to be canceled if needed.
27998      * @param {String} query The SQL query to execute
27999      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28000      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28001      * saved in the current store (defaults to false)
28002      */
28003     doQuery : function(q, forceAll){
28004         
28005         Roo.log('doQuery?');
28006         if(q === undefined || q === null){
28007             q = '';
28008         }
28009         var qe = {
28010             query: q,
28011             forceAll: forceAll,
28012             combo: this,
28013             cancel:false
28014         };
28015         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28016             return false;
28017         }
28018         q = qe.query;
28019         forceAll = qe.forceAll;
28020         if(forceAll === true || (q.length >= this.minChars)){
28021             if(this.lastQuery != q || this.alwaysQuery){
28022                 this.lastQuery = q;
28023                 if(this.mode == 'local'){
28024                     this.selectedIndex = -1;
28025                     if(forceAll){
28026                         this.store.clearFilter();
28027                     }else{
28028                         this.store.filter(this.displayField, q);
28029                     }
28030                     this.onLoad();
28031                 }else{
28032                     this.store.baseParams[this.queryParam] = q;
28033                     this.store.load({
28034                         params: this.getParams(q)
28035                     });
28036                     this.expand();
28037                 }
28038             }else{
28039                 this.selectedIndex = -1;
28040                 this.onLoad();   
28041             }
28042         }
28043     },
28044
28045     // private
28046     getParams : function(q){
28047         var p = {};
28048         //p[this.queryParam] = q;
28049         if(this.pageSize){
28050             p.start = 0;
28051             p.limit = this.pageSize;
28052         }
28053         return p;
28054     },
28055
28056     /**
28057      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28058      */
28059     collapse : function(){
28060         
28061     },
28062
28063     // private
28064     collapseIf : function(e){
28065         
28066     },
28067
28068     /**
28069      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28070      */
28071     expand : function(){
28072         
28073     } ,
28074
28075     // private
28076      
28077
28078     /** 
28079     * @cfg {Boolean} grow 
28080     * @hide 
28081     */
28082     /** 
28083     * @cfg {Number} growMin 
28084     * @hide 
28085     */
28086     /** 
28087     * @cfg {Number} growMax 
28088     * @hide 
28089     */
28090     /**
28091      * @hide
28092      * @method autoSize
28093      */
28094     
28095     setWidth : function()
28096     {
28097         
28098     },
28099     getResizeEl : function(){
28100         return this.el;
28101     }
28102 });//<script type="text/javasscript">
28103  
28104
28105 /**
28106  * @class Roo.DDView
28107  * A DnD enabled version of Roo.View.
28108  * @param {Element/String} container The Element in which to create the View.
28109  * @param {String} tpl The template string used to create the markup for each element of the View
28110  * @param {Object} config The configuration properties. These include all the config options of
28111  * {@link Roo.View} plus some specific to this class.<br>
28112  * <p>
28113  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28114  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28115  * <p>
28116  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28117 .x-view-drag-insert-above {
28118         border-top:1px dotted #3366cc;
28119 }
28120 .x-view-drag-insert-below {
28121         border-bottom:1px dotted #3366cc;
28122 }
28123 </code></pre>
28124  * 
28125  */
28126  
28127 Roo.DDView = function(container, tpl, config) {
28128     Roo.DDView.superclass.constructor.apply(this, arguments);
28129     this.getEl().setStyle("outline", "0px none");
28130     this.getEl().unselectable();
28131     if (this.dragGroup) {
28132                 this.setDraggable(this.dragGroup.split(","));
28133     }
28134     if (this.dropGroup) {
28135                 this.setDroppable(this.dropGroup.split(","));
28136     }
28137     if (this.deletable) {
28138         this.setDeletable();
28139     }
28140     this.isDirtyFlag = false;
28141         this.addEvents({
28142                 "drop" : true
28143         });
28144 };
28145
28146 Roo.extend(Roo.DDView, Roo.View, {
28147 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28148 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28149 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28150 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28151
28152         isFormField: true,
28153
28154         reset: Roo.emptyFn,
28155         
28156         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28157
28158         validate: function() {
28159                 return true;
28160         },
28161         
28162         destroy: function() {
28163                 this.purgeListeners();
28164                 this.getEl.removeAllListeners();
28165                 this.getEl().remove();
28166                 if (this.dragZone) {
28167                         if (this.dragZone.destroy) {
28168                                 this.dragZone.destroy();
28169                         }
28170                 }
28171                 if (this.dropZone) {
28172                         if (this.dropZone.destroy) {
28173                                 this.dropZone.destroy();
28174                         }
28175                 }
28176         },
28177
28178 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28179         getName: function() {
28180                 return this.name;
28181         },
28182
28183 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28184         setValue: function(v) {
28185                 if (!this.store) {
28186                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28187                 }
28188                 var data = {};
28189                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28190                 this.store.proxy = new Roo.data.MemoryProxy(data);
28191                 this.store.load();
28192         },
28193
28194 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28195         getValue: function() {
28196                 var result = '(';
28197                 this.store.each(function(rec) {
28198                         result += rec.id + ',';
28199                 });
28200                 return result.substr(0, result.length - 1) + ')';
28201         },
28202         
28203         getIds: function() {
28204                 var i = 0, result = new Array(this.store.getCount());
28205                 this.store.each(function(rec) {
28206                         result[i++] = rec.id;
28207                 });
28208                 return result;
28209         },
28210         
28211         isDirty: function() {
28212                 return this.isDirtyFlag;
28213         },
28214
28215 /**
28216  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28217  *      whole Element becomes the target, and this causes the drop gesture to append.
28218  */
28219     getTargetFromEvent : function(e) {
28220                 var target = e.getTarget();
28221                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28222                 target = target.parentNode;
28223                 }
28224                 if (!target) {
28225                         target = this.el.dom.lastChild || this.el.dom;
28226                 }
28227                 return target;
28228     },
28229
28230 /**
28231  *      Create the drag data which consists of an object which has the property "ddel" as
28232  *      the drag proxy element. 
28233  */
28234     getDragData : function(e) {
28235         var target = this.findItemFromChild(e.getTarget());
28236                 if(target) {
28237                         this.handleSelection(e);
28238                         var selNodes = this.getSelectedNodes();
28239             var dragData = {
28240                 source: this,
28241                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28242                 nodes: selNodes,
28243                 records: []
28244                         };
28245                         var selectedIndices = this.getSelectedIndexes();
28246                         for (var i = 0; i < selectedIndices.length; i++) {
28247                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28248                         }
28249                         if (selNodes.length == 1) {
28250                                 dragData.ddel = target.cloneNode(true); // the div element
28251                         } else {
28252                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28253                                 div.className = 'multi-proxy';
28254                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28255                                         div.appendChild(selNodes[i].cloneNode(true));
28256                                 }
28257                                 dragData.ddel = div;
28258                         }
28259             //console.log(dragData)
28260             //console.log(dragData.ddel.innerHTML)
28261                         return dragData;
28262                 }
28263         //console.log('nodragData')
28264                 return false;
28265     },
28266     
28267 /**     Specify to which ddGroup items in this DDView may be dragged. */
28268     setDraggable: function(ddGroup) {
28269         if (ddGroup instanceof Array) {
28270                 Roo.each(ddGroup, this.setDraggable, this);
28271                 return;
28272         }
28273         if (this.dragZone) {
28274                 this.dragZone.addToGroup(ddGroup);
28275         } else {
28276                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28277                                 containerScroll: true,
28278                                 ddGroup: ddGroup 
28279
28280                         });
28281 //                      Draggability implies selection. DragZone's mousedown selects the element.
28282                         if (!this.multiSelect) { this.singleSelect = true; }
28283
28284 //                      Wire the DragZone's handlers up to methods in *this*
28285                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28286                 }
28287     },
28288
28289 /**     Specify from which ddGroup this DDView accepts drops. */
28290     setDroppable: function(ddGroup) {
28291         if (ddGroup instanceof Array) {
28292                 Roo.each(ddGroup, this.setDroppable, this);
28293                 return;
28294         }
28295         if (this.dropZone) {
28296                 this.dropZone.addToGroup(ddGroup);
28297         } else {
28298                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28299                                 containerScroll: true,
28300                                 ddGroup: ddGroup
28301                         });
28302
28303 //                      Wire the DropZone's handlers up to methods in *this*
28304                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28305                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28306                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28307                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28308                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28309                 }
28310     },
28311
28312 /**     Decide whether to drop above or below a View node. */
28313     getDropPoint : function(e, n, dd){
28314         if (n == this.el.dom) { return "above"; }
28315                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28316                 var c = t + (b - t) / 2;
28317                 var y = Roo.lib.Event.getPageY(e);
28318                 if(y <= c) {
28319                         return "above";
28320                 }else{
28321                         return "below";
28322                 }
28323     },
28324
28325     onNodeEnter : function(n, dd, e, data){
28326                 return false;
28327     },
28328     
28329     onNodeOver : function(n, dd, e, data){
28330                 var pt = this.getDropPoint(e, n, dd);
28331                 // set the insert point style on the target node
28332                 var dragElClass = this.dropNotAllowed;
28333                 if (pt) {
28334                         var targetElClass;
28335                         if (pt == "above"){
28336                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28337                                 targetElClass = "x-view-drag-insert-above";
28338                         } else {
28339                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28340                                 targetElClass = "x-view-drag-insert-below";
28341                         }
28342                         if (this.lastInsertClass != targetElClass){
28343                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28344                                 this.lastInsertClass = targetElClass;
28345                         }
28346                 }
28347                 return dragElClass;
28348         },
28349
28350     onNodeOut : function(n, dd, e, data){
28351                 this.removeDropIndicators(n);
28352     },
28353
28354     onNodeDrop : function(n, dd, e, data){
28355         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28356                 return false;
28357         }
28358         var pt = this.getDropPoint(e, n, dd);
28359                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28360                 if (pt == "below") { insertAt++; }
28361                 for (var i = 0; i < data.records.length; i++) {
28362                         var r = data.records[i];
28363                         var dup = this.store.getById(r.id);
28364                         if (dup && (dd != this.dragZone)) {
28365                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28366                         } else {
28367                                 if (data.copy) {
28368                                         this.store.insert(insertAt++, r.copy());
28369                                 } else {
28370                                         data.source.isDirtyFlag = true;
28371                                         r.store.remove(r);
28372                                         this.store.insert(insertAt++, r);
28373                                 }
28374                                 this.isDirtyFlag = true;
28375                         }
28376                 }
28377                 this.dragZone.cachedTarget = null;
28378                 return true;
28379     },
28380
28381     removeDropIndicators : function(n){
28382                 if(n){
28383                         Roo.fly(n).removeClass([
28384                                 "x-view-drag-insert-above",
28385                                 "x-view-drag-insert-below"]);
28386                         this.lastInsertClass = "_noclass";
28387                 }
28388     },
28389
28390 /**
28391  *      Utility method. Add a delete option to the DDView's context menu.
28392  *      @param {String} imageUrl The URL of the "delete" icon image.
28393  */
28394         setDeletable: function(imageUrl) {
28395                 if (!this.singleSelect && !this.multiSelect) {
28396                         this.singleSelect = true;
28397                 }
28398                 var c = this.getContextMenu();
28399                 this.contextMenu.on("itemclick", function(item) {
28400                         switch (item.id) {
28401                                 case "delete":
28402                                         this.remove(this.getSelectedIndexes());
28403                                         break;
28404                         }
28405                 }, this);
28406                 this.contextMenu.add({
28407                         icon: imageUrl,
28408                         id: "delete",
28409                         text: 'Delete'
28410                 });
28411         },
28412         
28413 /**     Return the context menu for this DDView. */
28414         getContextMenu: function() {
28415                 if (!this.contextMenu) {
28416 //                      Create the View's context menu
28417                         this.contextMenu = new Roo.menu.Menu({
28418                                 id: this.id + "-contextmenu"
28419                         });
28420                         this.el.on("contextmenu", this.showContextMenu, this);
28421                 }
28422                 return this.contextMenu;
28423         },
28424         
28425         disableContextMenu: function() {
28426                 if (this.contextMenu) {
28427                         this.el.un("contextmenu", this.showContextMenu, this);
28428                 }
28429         },
28430
28431         showContextMenu: function(e, item) {
28432         item = this.findItemFromChild(e.getTarget());
28433                 if (item) {
28434                         e.stopEvent();
28435                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28436                         this.contextMenu.showAt(e.getXY());
28437             }
28438     },
28439
28440 /**
28441  *      Remove {@link Roo.data.Record}s at the specified indices.
28442  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28443  */
28444     remove: function(selectedIndices) {
28445                 selectedIndices = [].concat(selectedIndices);
28446                 for (var i = 0; i < selectedIndices.length; i++) {
28447                         var rec = this.store.getAt(selectedIndices[i]);
28448                         this.store.remove(rec);
28449                 }
28450     },
28451
28452 /**
28453  *      Double click fires the event, but also, if this is draggable, and there is only one other
28454  *      related DropZone, it transfers the selected node.
28455  */
28456     onDblClick : function(e){
28457         var item = this.findItemFromChild(e.getTarget());
28458         if(item){
28459             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28460                 return false;
28461             }
28462             if (this.dragGroup) {
28463                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28464                     while (targets.indexOf(this.dropZone) > -1) {
28465                             targets.remove(this.dropZone);
28466                                 }
28467                     if (targets.length == 1) {
28468                                         this.dragZone.cachedTarget = null;
28469                         var el = Roo.get(targets[0].getEl());
28470                         var box = el.getBox(true);
28471                         targets[0].onNodeDrop(el.dom, {
28472                                 target: el.dom,
28473                                 xy: [box.x, box.y + box.height - 1]
28474                         }, null, this.getDragData(e));
28475                     }
28476                 }
28477         }
28478     },
28479     
28480     handleSelection: function(e) {
28481                 this.dragZone.cachedTarget = null;
28482         var item = this.findItemFromChild(e.getTarget());
28483         if (!item) {
28484                 this.clearSelections(true);
28485                 return;
28486         }
28487                 if (item && (this.multiSelect || this.singleSelect)){
28488                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28489                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28490                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28491                                 this.unselect(item);
28492                         } else {
28493                                 this.select(item, this.multiSelect && e.ctrlKey);
28494                                 this.lastSelection = item;
28495                         }
28496                 }
28497     },
28498
28499     onItemClick : function(item, index, e){
28500                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28501                         return false;
28502                 }
28503                 return true;
28504     },
28505
28506     unselect : function(nodeInfo, suppressEvent){
28507                 var node = this.getNode(nodeInfo);
28508                 if(node && this.isSelected(node)){
28509                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28510                                 Roo.fly(node).removeClass(this.selectedClass);
28511                                 this.selections.remove(node);
28512                                 if(!suppressEvent){
28513                                         this.fireEvent("selectionchange", this, this.selections);
28514                                 }
28515                         }
28516                 }
28517     }
28518 });
28519 /*
28520  * Based on:
28521  * Ext JS Library 1.1.1
28522  * Copyright(c) 2006-2007, Ext JS, LLC.
28523  *
28524  * Originally Released Under LGPL - original licence link has changed is not relivant.
28525  *
28526  * Fork - LGPL
28527  * <script type="text/javascript">
28528  */
28529  
28530 /**
28531  * @class Roo.LayoutManager
28532  * @extends Roo.util.Observable
28533  * Base class for layout managers.
28534  */
28535 Roo.LayoutManager = function(container, config){
28536     Roo.LayoutManager.superclass.constructor.call(this);
28537     this.el = Roo.get(container);
28538     // ie scrollbar fix
28539     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28540         document.body.scroll = "no";
28541     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28542         this.el.position('relative');
28543     }
28544     this.id = this.el.id;
28545     this.el.addClass("x-layout-container");
28546     /** false to disable window resize monitoring @type Boolean */
28547     this.monitorWindowResize = true;
28548     this.regions = {};
28549     this.addEvents({
28550         /**
28551          * @event layout
28552          * Fires when a layout is performed. 
28553          * @param {Roo.LayoutManager} this
28554          */
28555         "layout" : true,
28556         /**
28557          * @event regionresized
28558          * Fires when the user resizes a region. 
28559          * @param {Roo.LayoutRegion} region The resized region
28560          * @param {Number} newSize The new size (width for east/west, height for north/south)
28561          */
28562         "regionresized" : true,
28563         /**
28564          * @event regioncollapsed
28565          * Fires when a region is collapsed. 
28566          * @param {Roo.LayoutRegion} region The collapsed region
28567          */
28568         "regioncollapsed" : true,
28569         /**
28570          * @event regionexpanded
28571          * Fires when a region is expanded.  
28572          * @param {Roo.LayoutRegion} region The expanded region
28573          */
28574         "regionexpanded" : true
28575     });
28576     this.updating = false;
28577     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28578 };
28579
28580 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28581     /**
28582      * Returns true if this layout is currently being updated
28583      * @return {Boolean}
28584      */
28585     isUpdating : function(){
28586         return this.updating; 
28587     },
28588     
28589     /**
28590      * Suspend the LayoutManager from doing auto-layouts while
28591      * making multiple add or remove calls
28592      */
28593     beginUpdate : function(){
28594         this.updating = true;    
28595     },
28596     
28597     /**
28598      * Restore auto-layouts and optionally disable the manager from performing a layout
28599      * @param {Boolean} noLayout true to disable a layout update 
28600      */
28601     endUpdate : function(noLayout){
28602         this.updating = false;
28603         if(!noLayout){
28604             this.layout();
28605         }    
28606     },
28607     
28608     layout: function(){
28609         
28610     },
28611     
28612     onRegionResized : function(region, newSize){
28613         this.fireEvent("regionresized", region, newSize);
28614         this.layout();
28615     },
28616     
28617     onRegionCollapsed : function(region){
28618         this.fireEvent("regioncollapsed", region);
28619     },
28620     
28621     onRegionExpanded : function(region){
28622         this.fireEvent("regionexpanded", region);
28623     },
28624         
28625     /**
28626      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28627      * performs box-model adjustments.
28628      * @return {Object} The size as an object {width: (the width), height: (the height)}
28629      */
28630     getViewSize : function(){
28631         var size;
28632         if(this.el.dom != document.body){
28633             size = this.el.getSize();
28634         }else{
28635             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28636         }
28637         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28638         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28639         return size;
28640     },
28641     
28642     /**
28643      * Returns the Element this layout is bound to.
28644      * @return {Roo.Element}
28645      */
28646     getEl : function(){
28647         return this.el;
28648     },
28649     
28650     /**
28651      * Returns the specified region.
28652      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28653      * @return {Roo.LayoutRegion}
28654      */
28655     getRegion : function(target){
28656         return this.regions[target.toLowerCase()];
28657     },
28658     
28659     onWindowResize : function(){
28660         if(this.monitorWindowResize){
28661             this.layout();
28662         }
28663     }
28664 });/*
28665  * Based on:
28666  * Ext JS Library 1.1.1
28667  * Copyright(c) 2006-2007, Ext JS, LLC.
28668  *
28669  * Originally Released Under LGPL - original licence link has changed is not relivant.
28670  *
28671  * Fork - LGPL
28672  * <script type="text/javascript">
28673  */
28674 /**
28675  * @class Roo.BorderLayout
28676  * @extends Roo.LayoutManager
28677  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28678  * please see: <br><br>
28679  * <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>
28680  * <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>
28681  * Example:
28682  <pre><code>
28683  var layout = new Roo.BorderLayout(document.body, {
28684     north: {
28685         initialSize: 25,
28686         titlebar: false
28687     },
28688     west: {
28689         split:true,
28690         initialSize: 200,
28691         minSize: 175,
28692         maxSize: 400,
28693         titlebar: true,
28694         collapsible: true
28695     },
28696     east: {
28697         split:true,
28698         initialSize: 202,
28699         minSize: 175,
28700         maxSize: 400,
28701         titlebar: true,
28702         collapsible: true
28703     },
28704     south: {
28705         split:true,
28706         initialSize: 100,
28707         minSize: 100,
28708         maxSize: 200,
28709         titlebar: true,
28710         collapsible: true
28711     },
28712     center: {
28713         titlebar: true,
28714         autoScroll:true,
28715         resizeTabs: true,
28716         minTabWidth: 50,
28717         preferredTabWidth: 150
28718     }
28719 });
28720
28721 // shorthand
28722 var CP = Roo.ContentPanel;
28723
28724 layout.beginUpdate();
28725 layout.add("north", new CP("north", "North"));
28726 layout.add("south", new CP("south", {title: "South", closable: true}));
28727 layout.add("west", new CP("west", {title: "West"}));
28728 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28729 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28730 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28731 layout.getRegion("center").showPanel("center1");
28732 layout.endUpdate();
28733 </code></pre>
28734
28735 <b>The container the layout is rendered into can be either the body element or any other element.
28736 If it is not the body element, the container needs to either be an absolute positioned element,
28737 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28738 the container size if it is not the body element.</b>
28739
28740 * @constructor
28741 * Create a new BorderLayout
28742 * @param {String/HTMLElement/Element} container The container this layout is bound to
28743 * @param {Object} config Configuration options
28744  */
28745 Roo.BorderLayout = function(container, config){
28746     config = config || {};
28747     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28748     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28749     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28750         var target = this.factory.validRegions[i];
28751         if(config[target]){
28752             this.addRegion(target, config[target]);
28753         }
28754     }
28755 };
28756
28757 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28758     /**
28759      * Creates and adds a new region if it doesn't already exist.
28760      * @param {String} target The target region key (north, south, east, west or center).
28761      * @param {Object} config The regions config object
28762      * @return {BorderLayoutRegion} The new region
28763      */
28764     addRegion : function(target, config){
28765         if(!this.regions[target]){
28766             var r = this.factory.create(target, this, config);
28767             this.bindRegion(target, r);
28768         }
28769         return this.regions[target];
28770     },
28771
28772     // private (kinda)
28773     bindRegion : function(name, r){
28774         this.regions[name] = r;
28775         r.on("visibilitychange", this.layout, this);
28776         r.on("paneladded", this.layout, this);
28777         r.on("panelremoved", this.layout, this);
28778         r.on("invalidated", this.layout, this);
28779         r.on("resized", this.onRegionResized, this);
28780         r.on("collapsed", this.onRegionCollapsed, this);
28781         r.on("expanded", this.onRegionExpanded, this);
28782     },
28783
28784     /**
28785      * Performs a layout update.
28786      */
28787     layout : function(){
28788         if(this.updating) {
28789             return;
28790         }
28791         var size = this.getViewSize();
28792         var w = size.width;
28793         var h = size.height;
28794         var centerW = w;
28795         var centerH = h;
28796         var centerY = 0;
28797         var centerX = 0;
28798         //var x = 0, y = 0;
28799
28800         var rs = this.regions;
28801         var north = rs["north"];
28802         var south = rs["south"]; 
28803         var west = rs["west"];
28804         var east = rs["east"];
28805         var center = rs["center"];
28806         //if(this.hideOnLayout){ // not supported anymore
28807             //c.el.setStyle("display", "none");
28808         //}
28809         if(north && north.isVisible()){
28810             var b = north.getBox();
28811             var m = north.getMargins();
28812             b.width = w - (m.left+m.right);
28813             b.x = m.left;
28814             b.y = m.top;
28815             centerY = b.height + b.y + m.bottom;
28816             centerH -= centerY;
28817             north.updateBox(this.safeBox(b));
28818         }
28819         if(south && south.isVisible()){
28820             var b = south.getBox();
28821             var m = south.getMargins();
28822             b.width = w - (m.left+m.right);
28823             b.x = m.left;
28824             var totalHeight = (b.height + m.top + m.bottom);
28825             b.y = h - totalHeight + m.top;
28826             centerH -= totalHeight;
28827             south.updateBox(this.safeBox(b));
28828         }
28829         if(west && west.isVisible()){
28830             var b = west.getBox();
28831             var m = west.getMargins();
28832             b.height = centerH - (m.top+m.bottom);
28833             b.x = m.left;
28834             b.y = centerY + m.top;
28835             var totalWidth = (b.width + m.left + m.right);
28836             centerX += totalWidth;
28837             centerW -= totalWidth;
28838             west.updateBox(this.safeBox(b));
28839         }
28840         if(east && east.isVisible()){
28841             var b = east.getBox();
28842             var m = east.getMargins();
28843             b.height = centerH - (m.top+m.bottom);
28844             var totalWidth = (b.width + m.left + m.right);
28845             b.x = w - totalWidth + m.left;
28846             b.y = centerY + m.top;
28847             centerW -= totalWidth;
28848             east.updateBox(this.safeBox(b));
28849         }
28850         if(center){
28851             var m = center.getMargins();
28852             var centerBox = {
28853                 x: centerX + m.left,
28854                 y: centerY + m.top,
28855                 width: centerW - (m.left+m.right),
28856                 height: centerH - (m.top+m.bottom)
28857             };
28858             //if(this.hideOnLayout){
28859                 //center.el.setStyle("display", "block");
28860             //}
28861             center.updateBox(this.safeBox(centerBox));
28862         }
28863         this.el.repaint();
28864         this.fireEvent("layout", this);
28865     },
28866
28867     // private
28868     safeBox : function(box){
28869         box.width = Math.max(0, box.width);
28870         box.height = Math.max(0, box.height);
28871         return box;
28872     },
28873
28874     /**
28875      * Adds a ContentPanel (or subclass) to this layout.
28876      * @param {String} target The target region key (north, south, east, west or center).
28877      * @param {Roo.ContentPanel} panel The panel to add
28878      * @return {Roo.ContentPanel} The added panel
28879      */
28880     add : function(target, panel){
28881          
28882         target = target.toLowerCase();
28883         return this.regions[target].add(panel);
28884     },
28885
28886     /**
28887      * Remove a ContentPanel (or subclass) to this layout.
28888      * @param {String} target The target region key (north, south, east, west or center).
28889      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28890      * @return {Roo.ContentPanel} The removed panel
28891      */
28892     remove : function(target, panel){
28893         target = target.toLowerCase();
28894         return this.regions[target].remove(panel);
28895     },
28896
28897     /**
28898      * Searches all regions for a panel with the specified id
28899      * @param {String} panelId
28900      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28901      */
28902     findPanel : function(panelId){
28903         var rs = this.regions;
28904         for(var target in rs){
28905             if(typeof rs[target] != "function"){
28906                 var p = rs[target].getPanel(panelId);
28907                 if(p){
28908                     return p;
28909                 }
28910             }
28911         }
28912         return null;
28913     },
28914
28915     /**
28916      * Searches all regions for a panel with the specified id and activates (shows) it.
28917      * @param {String/ContentPanel} panelId The panels id or the panel itself
28918      * @return {Roo.ContentPanel} The shown panel or null
28919      */
28920     showPanel : function(panelId) {
28921       var rs = this.regions;
28922       for(var target in rs){
28923          var r = rs[target];
28924          if(typeof r != "function"){
28925             if(r.hasPanel(panelId)){
28926                return r.showPanel(panelId);
28927             }
28928          }
28929       }
28930       return null;
28931    },
28932
28933    /**
28934      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28935      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28936      */
28937     restoreState : function(provider){
28938         if(!provider){
28939             provider = Roo.state.Manager;
28940         }
28941         var sm = new Roo.LayoutStateManager();
28942         sm.init(this, provider);
28943     },
28944
28945     /**
28946      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28947      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28948      * a valid ContentPanel config object.  Example:
28949      * <pre><code>
28950 // Create the main layout
28951 var layout = new Roo.BorderLayout('main-ct', {
28952     west: {
28953         split:true,
28954         minSize: 175,
28955         titlebar: true
28956     },
28957     center: {
28958         title:'Components'
28959     }
28960 }, 'main-ct');
28961
28962 // Create and add multiple ContentPanels at once via configs
28963 layout.batchAdd({
28964    west: {
28965        id: 'source-files',
28966        autoCreate:true,
28967        title:'Ext Source Files',
28968        autoScroll:true,
28969        fitToFrame:true
28970    },
28971    center : {
28972        el: cview,
28973        autoScroll:true,
28974        fitToFrame:true,
28975        toolbar: tb,
28976        resizeEl:'cbody'
28977    }
28978 });
28979 </code></pre>
28980      * @param {Object} regions An object containing ContentPanel configs by region name
28981      */
28982     batchAdd : function(regions){
28983         this.beginUpdate();
28984         for(var rname in regions){
28985             var lr = this.regions[rname];
28986             if(lr){
28987                 this.addTypedPanels(lr, regions[rname]);
28988             }
28989         }
28990         this.endUpdate();
28991     },
28992
28993     // private
28994     addTypedPanels : function(lr, ps){
28995         if(typeof ps == 'string'){
28996             lr.add(new Roo.ContentPanel(ps));
28997         }
28998         else if(ps instanceof Array){
28999             for(var i =0, len = ps.length; i < len; i++){
29000                 this.addTypedPanels(lr, ps[i]);
29001             }
29002         }
29003         else if(!ps.events){ // raw config?
29004             var el = ps.el;
29005             delete ps.el; // prevent conflict
29006             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29007         }
29008         else {  // panel object assumed!
29009             lr.add(ps);
29010         }
29011     },
29012     /**
29013      * Adds a xtype elements to the layout.
29014      * <pre><code>
29015
29016 layout.addxtype({
29017        xtype : 'ContentPanel',
29018        region: 'west',
29019        items: [ .... ]
29020    }
29021 );
29022
29023 layout.addxtype({
29024         xtype : 'NestedLayoutPanel',
29025         region: 'west',
29026         layout: {
29027            center: { },
29028            west: { }   
29029         },
29030         items : [ ... list of content panels or nested layout panels.. ]
29031    }
29032 );
29033 </code></pre>
29034      * @param {Object} cfg Xtype definition of item to add.
29035      */
29036     addxtype : function(cfg)
29037     {
29038         // basically accepts a pannel...
29039         // can accept a layout region..!?!?
29040         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29041         
29042         if (!cfg.xtype.match(/Panel$/)) {
29043             return false;
29044         }
29045         var ret = false;
29046         
29047         if (typeof(cfg.region) == 'undefined') {
29048             Roo.log("Failed to add Panel, region was not set");
29049             Roo.log(cfg);
29050             return false;
29051         }
29052         var region = cfg.region;
29053         delete cfg.region;
29054         
29055           
29056         var xitems = [];
29057         if (cfg.items) {
29058             xitems = cfg.items;
29059             delete cfg.items;
29060         }
29061         var nb = false;
29062         
29063         switch(cfg.xtype) 
29064         {
29065             case 'ContentPanel':  // ContentPanel (el, cfg)
29066             case 'ScrollPanel':  // ContentPanel (el, cfg)
29067             case 'ViewPanel': 
29068                 if(cfg.autoCreate) {
29069                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29070                 } else {
29071                     var el = this.el.createChild();
29072                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29073                 }
29074                 
29075                 this.add(region, ret);
29076                 break;
29077             
29078             
29079             case 'TreePanel': // our new panel!
29080                 cfg.el = this.el.createChild();
29081                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29082                 this.add(region, ret);
29083                 break;
29084             
29085             case 'NestedLayoutPanel': 
29086                 // create a new Layout (which is  a Border Layout...
29087                 var el = this.el.createChild();
29088                 var clayout = cfg.layout;
29089                 delete cfg.layout;
29090                 clayout.items   = clayout.items  || [];
29091                 // replace this exitems with the clayout ones..
29092                 xitems = clayout.items;
29093                  
29094                 
29095                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29096                     cfg.background = false;
29097                 }
29098                 var layout = new Roo.BorderLayout(el, clayout);
29099                 
29100                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29101                 //console.log('adding nested layout panel '  + cfg.toSource());
29102                 this.add(region, ret);
29103                 nb = {}; /// find first...
29104                 break;
29105                 
29106             case 'GridPanel': 
29107             
29108                 // needs grid and region
29109                 
29110                 //var el = this.getRegion(region).el.createChild();
29111                 var el = this.el.createChild();
29112                 // create the grid first...
29113                 
29114                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29115                 delete cfg.grid;
29116                 if (region == 'center' && this.active ) {
29117                     cfg.background = false;
29118                 }
29119                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29120                 
29121                 this.add(region, ret);
29122                 if (cfg.background) {
29123                     ret.on('activate', function(gp) {
29124                         if (!gp.grid.rendered) {
29125                             gp.grid.render();
29126                         }
29127                     });
29128                 } else {
29129                     grid.render();
29130                 }
29131                 break;
29132            
29133            
29134            
29135                 
29136                 
29137                 
29138             default:
29139                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29140                     
29141                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29142                     this.add(region, ret);
29143                 } else {
29144                 
29145                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29146                     return null;
29147                 }
29148                 
29149              // GridPanel (grid, cfg)
29150             
29151         }
29152         this.beginUpdate();
29153         // add children..
29154         var region = '';
29155         var abn = {};
29156         Roo.each(xitems, function(i)  {
29157             region = nb && i.region ? i.region : false;
29158             
29159             var add = ret.addxtype(i);
29160            
29161             if (region) {
29162                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29163                 if (!i.background) {
29164                     abn[region] = nb[region] ;
29165                 }
29166             }
29167             
29168         });
29169         this.endUpdate();
29170
29171         // make the last non-background panel active..
29172         //if (nb) { Roo.log(abn); }
29173         if (nb) {
29174             
29175             for(var r in abn) {
29176                 region = this.getRegion(r);
29177                 if (region) {
29178                     // tried using nb[r], but it does not work..
29179                      
29180                     region.showPanel(abn[r]);
29181                    
29182                 }
29183             }
29184         }
29185         return ret;
29186         
29187     }
29188 });
29189
29190 /**
29191  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29192  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29193  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29194  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29195  * <pre><code>
29196 // shorthand
29197 var CP = Roo.ContentPanel;
29198
29199 var layout = Roo.BorderLayout.create({
29200     north: {
29201         initialSize: 25,
29202         titlebar: false,
29203         panels: [new CP("north", "North")]
29204     },
29205     west: {
29206         split:true,
29207         initialSize: 200,
29208         minSize: 175,
29209         maxSize: 400,
29210         titlebar: true,
29211         collapsible: true,
29212         panels: [new CP("west", {title: "West"})]
29213     },
29214     east: {
29215         split:true,
29216         initialSize: 202,
29217         minSize: 175,
29218         maxSize: 400,
29219         titlebar: true,
29220         collapsible: true,
29221         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29222     },
29223     south: {
29224         split:true,
29225         initialSize: 100,
29226         minSize: 100,
29227         maxSize: 200,
29228         titlebar: true,
29229         collapsible: true,
29230         panels: [new CP("south", {title: "South", closable: true})]
29231     },
29232     center: {
29233         titlebar: true,
29234         autoScroll:true,
29235         resizeTabs: true,
29236         minTabWidth: 50,
29237         preferredTabWidth: 150,
29238         panels: [
29239             new CP("center1", {title: "Close Me", closable: true}),
29240             new CP("center2", {title: "Center Panel", closable: false})
29241         ]
29242     }
29243 }, document.body);
29244
29245 layout.getRegion("center").showPanel("center1");
29246 </code></pre>
29247  * @param config
29248  * @param targetEl
29249  */
29250 Roo.BorderLayout.create = function(config, targetEl){
29251     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29252     layout.beginUpdate();
29253     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29254     for(var j = 0, jlen = regions.length; j < jlen; j++){
29255         var lr = regions[j];
29256         if(layout.regions[lr] && config[lr].panels){
29257             var r = layout.regions[lr];
29258             var ps = config[lr].panels;
29259             layout.addTypedPanels(r, ps);
29260         }
29261     }
29262     layout.endUpdate();
29263     return layout;
29264 };
29265
29266 // private
29267 Roo.BorderLayout.RegionFactory = {
29268     // private
29269     validRegions : ["north","south","east","west","center"],
29270
29271     // private
29272     create : function(target, mgr, config){
29273         target = target.toLowerCase();
29274         if(config.lightweight || config.basic){
29275             return new Roo.BasicLayoutRegion(mgr, config, target);
29276         }
29277         switch(target){
29278             case "north":
29279                 return new Roo.NorthLayoutRegion(mgr, config);
29280             case "south":
29281                 return new Roo.SouthLayoutRegion(mgr, config);
29282             case "east":
29283                 return new Roo.EastLayoutRegion(mgr, config);
29284             case "west":
29285                 return new Roo.WestLayoutRegion(mgr, config);
29286             case "center":
29287                 return new Roo.CenterLayoutRegion(mgr, config);
29288         }
29289         throw 'Layout region "'+target+'" not supported.';
29290     }
29291 };/*
29292  * Based on:
29293  * Ext JS Library 1.1.1
29294  * Copyright(c) 2006-2007, Ext JS, LLC.
29295  *
29296  * Originally Released Under LGPL - original licence link has changed is not relivant.
29297  *
29298  * Fork - LGPL
29299  * <script type="text/javascript">
29300  */
29301  
29302 /**
29303  * @class Roo.BasicLayoutRegion
29304  * @extends Roo.util.Observable
29305  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29306  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29307  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29308  */
29309 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29310     this.mgr = mgr;
29311     this.position  = pos;
29312     this.events = {
29313         /**
29314          * @scope Roo.BasicLayoutRegion
29315          */
29316         
29317         /**
29318          * @event beforeremove
29319          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29320          * @param {Roo.LayoutRegion} this
29321          * @param {Roo.ContentPanel} panel The panel
29322          * @param {Object} e The cancel event object
29323          */
29324         "beforeremove" : true,
29325         /**
29326          * @event invalidated
29327          * Fires when the layout for this region is changed.
29328          * @param {Roo.LayoutRegion} this
29329          */
29330         "invalidated" : true,
29331         /**
29332          * @event visibilitychange
29333          * Fires when this region is shown or hidden 
29334          * @param {Roo.LayoutRegion} this
29335          * @param {Boolean} visibility true or false
29336          */
29337         "visibilitychange" : true,
29338         /**
29339          * @event paneladded
29340          * Fires when a panel is added. 
29341          * @param {Roo.LayoutRegion} this
29342          * @param {Roo.ContentPanel} panel The panel
29343          */
29344         "paneladded" : true,
29345         /**
29346          * @event panelremoved
29347          * Fires when a panel is removed. 
29348          * @param {Roo.LayoutRegion} this
29349          * @param {Roo.ContentPanel} panel The panel
29350          */
29351         "panelremoved" : true,
29352         /**
29353          * @event beforecollapse
29354          * Fires when this region before collapse.
29355          * @param {Roo.LayoutRegion} this
29356          */
29357         "beforecollapse" : true,
29358         /**
29359          * @event collapsed
29360          * Fires when this region is collapsed.
29361          * @param {Roo.LayoutRegion} this
29362          */
29363         "collapsed" : true,
29364         /**
29365          * @event expanded
29366          * Fires when this region is expanded.
29367          * @param {Roo.LayoutRegion} this
29368          */
29369         "expanded" : true,
29370         /**
29371          * @event slideshow
29372          * Fires when this region is slid into view.
29373          * @param {Roo.LayoutRegion} this
29374          */
29375         "slideshow" : true,
29376         /**
29377          * @event slidehide
29378          * Fires when this region slides out of view. 
29379          * @param {Roo.LayoutRegion} this
29380          */
29381         "slidehide" : true,
29382         /**
29383          * @event panelactivated
29384          * Fires when a panel is activated. 
29385          * @param {Roo.LayoutRegion} this
29386          * @param {Roo.ContentPanel} panel The activated panel
29387          */
29388         "panelactivated" : true,
29389         /**
29390          * @event resized
29391          * Fires when the user resizes this region. 
29392          * @param {Roo.LayoutRegion} this
29393          * @param {Number} newSize The new size (width for east/west, height for north/south)
29394          */
29395         "resized" : true
29396     };
29397     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29398     this.panels = new Roo.util.MixedCollection();
29399     this.panels.getKey = this.getPanelId.createDelegate(this);
29400     this.box = null;
29401     this.activePanel = null;
29402     // ensure listeners are added...
29403     
29404     if (config.listeners || config.events) {
29405         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29406             listeners : config.listeners || {},
29407             events : config.events || {}
29408         });
29409     }
29410     
29411     if(skipConfig !== true){
29412         this.applyConfig(config);
29413     }
29414 };
29415
29416 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29417     getPanelId : function(p){
29418         return p.getId();
29419     },
29420     
29421     applyConfig : function(config){
29422         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29423         this.config = config;
29424         
29425     },
29426     
29427     /**
29428      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29429      * the width, for horizontal (north, south) the height.
29430      * @param {Number} newSize The new width or height
29431      */
29432     resizeTo : function(newSize){
29433         var el = this.el ? this.el :
29434                  (this.activePanel ? this.activePanel.getEl() : null);
29435         if(el){
29436             switch(this.position){
29437                 case "east":
29438                 case "west":
29439                     el.setWidth(newSize);
29440                     this.fireEvent("resized", this, newSize);
29441                 break;
29442                 case "north":
29443                 case "south":
29444                     el.setHeight(newSize);
29445                     this.fireEvent("resized", this, newSize);
29446                 break;                
29447             }
29448         }
29449     },
29450     
29451     getBox : function(){
29452         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29453     },
29454     
29455     getMargins : function(){
29456         return this.margins;
29457     },
29458     
29459     updateBox : function(box){
29460         this.box = box;
29461         var el = this.activePanel.getEl();
29462         el.dom.style.left = box.x + "px";
29463         el.dom.style.top = box.y + "px";
29464         this.activePanel.setSize(box.width, box.height);
29465     },
29466     
29467     /**
29468      * Returns the container element for this region.
29469      * @return {Roo.Element}
29470      */
29471     getEl : function(){
29472         return this.activePanel;
29473     },
29474     
29475     /**
29476      * Returns true if this region is currently visible.
29477      * @return {Boolean}
29478      */
29479     isVisible : function(){
29480         return this.activePanel ? true : false;
29481     },
29482     
29483     setActivePanel : function(panel){
29484         panel = this.getPanel(panel);
29485         if(this.activePanel && this.activePanel != panel){
29486             this.activePanel.setActiveState(false);
29487             this.activePanel.getEl().setLeftTop(-10000,-10000);
29488         }
29489         this.activePanel = panel;
29490         panel.setActiveState(true);
29491         if(this.box){
29492             panel.setSize(this.box.width, this.box.height);
29493         }
29494         this.fireEvent("panelactivated", this, panel);
29495         this.fireEvent("invalidated");
29496     },
29497     
29498     /**
29499      * Show the specified panel.
29500      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29501      * @return {Roo.ContentPanel} The shown panel or null
29502      */
29503     showPanel : function(panel){
29504         if(panel = this.getPanel(panel)){
29505             this.setActivePanel(panel);
29506         }
29507         return panel;
29508     },
29509     
29510     /**
29511      * Get the active panel for this region.
29512      * @return {Roo.ContentPanel} The active panel or null
29513      */
29514     getActivePanel : function(){
29515         return this.activePanel;
29516     },
29517     
29518     /**
29519      * Add the passed ContentPanel(s)
29520      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29521      * @return {Roo.ContentPanel} The panel added (if only one was added)
29522      */
29523     add : function(panel){
29524         if(arguments.length > 1){
29525             for(var i = 0, len = arguments.length; i < len; i++) {
29526                 this.add(arguments[i]);
29527             }
29528             return null;
29529         }
29530         if(this.hasPanel(panel)){
29531             this.showPanel(panel);
29532             return panel;
29533         }
29534         var el = panel.getEl();
29535         if(el.dom.parentNode != this.mgr.el.dom){
29536             this.mgr.el.dom.appendChild(el.dom);
29537         }
29538         if(panel.setRegion){
29539             panel.setRegion(this);
29540         }
29541         this.panels.add(panel);
29542         el.setStyle("position", "absolute");
29543         if(!panel.background){
29544             this.setActivePanel(panel);
29545             if(this.config.initialSize && this.panels.getCount()==1){
29546                 this.resizeTo(this.config.initialSize);
29547             }
29548         }
29549         this.fireEvent("paneladded", this, panel);
29550         return panel;
29551     },
29552     
29553     /**
29554      * Returns true if the panel is in this region.
29555      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29556      * @return {Boolean}
29557      */
29558     hasPanel : function(panel){
29559         if(typeof panel == "object"){ // must be panel obj
29560             panel = panel.getId();
29561         }
29562         return this.getPanel(panel) ? true : false;
29563     },
29564     
29565     /**
29566      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29567      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29568      * @param {Boolean} preservePanel Overrides the config preservePanel option
29569      * @return {Roo.ContentPanel} The panel that was removed
29570      */
29571     remove : function(panel, preservePanel){
29572         panel = this.getPanel(panel);
29573         if(!panel){
29574             return null;
29575         }
29576         var e = {};
29577         this.fireEvent("beforeremove", this, panel, e);
29578         if(e.cancel === true){
29579             return null;
29580         }
29581         var panelId = panel.getId();
29582         this.panels.removeKey(panelId);
29583         return panel;
29584     },
29585     
29586     /**
29587      * Returns the panel specified or null if it's not in this region.
29588      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29589      * @return {Roo.ContentPanel}
29590      */
29591     getPanel : function(id){
29592         if(typeof id == "object"){ // must be panel obj
29593             return id;
29594         }
29595         return this.panels.get(id);
29596     },
29597     
29598     /**
29599      * Returns this regions position (north/south/east/west/center).
29600      * @return {String} 
29601      */
29602     getPosition: function(){
29603         return this.position;    
29604     }
29605 });/*
29606  * Based on:
29607  * Ext JS Library 1.1.1
29608  * Copyright(c) 2006-2007, Ext JS, LLC.
29609  *
29610  * Originally Released Under LGPL - original licence link has changed is not relivant.
29611  *
29612  * Fork - LGPL
29613  * <script type="text/javascript">
29614  */
29615  
29616 /**
29617  * @class Roo.LayoutRegion
29618  * @extends Roo.BasicLayoutRegion
29619  * This class represents a region in a layout manager.
29620  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29621  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29622  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29623  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29624  * @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})
29625  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29626  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29627  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29628  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29629  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29630  * @cfg {String}    title           The title for the region (overrides panel titles)
29631  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29632  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29633  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29634  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29635  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29636  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29637  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29638  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29639  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29640  * @cfg {Boolean}   showPin         True to show a pin button
29641  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29642  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29643  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29644  * @cfg {Number}    width           For East/West panels
29645  * @cfg {Number}    height          For North/South panels
29646  * @cfg {Boolean}   split           To show the splitter
29647  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29648  */
29649 Roo.LayoutRegion = function(mgr, config, pos){
29650     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29651     var dh = Roo.DomHelper;
29652     /** This region's container element 
29653     * @type Roo.Element */
29654     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29655     /** This region's title element 
29656     * @type Roo.Element */
29657
29658     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29659         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29660         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29661     ]}, true);
29662     this.titleEl.enableDisplayMode();
29663     /** This region's title text element 
29664     * @type HTMLElement */
29665     this.titleTextEl = this.titleEl.dom.firstChild;
29666     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29667     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29668     this.closeBtn.enableDisplayMode();
29669     this.closeBtn.on("click", this.closeClicked, this);
29670     this.closeBtn.hide();
29671
29672     this.createBody(config);
29673     this.visible = true;
29674     this.collapsed = false;
29675
29676     if(config.hideWhenEmpty){
29677         this.hide();
29678         this.on("paneladded", this.validateVisibility, this);
29679         this.on("panelremoved", this.validateVisibility, this);
29680     }
29681     this.applyConfig(config);
29682 };
29683
29684 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29685
29686     createBody : function(){
29687         /** This region's body element 
29688         * @type Roo.Element */
29689         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29690     },
29691
29692     applyConfig : function(c){
29693         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29694             var dh = Roo.DomHelper;
29695             if(c.titlebar !== false){
29696                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29697                 this.collapseBtn.on("click", this.collapse, this);
29698                 this.collapseBtn.enableDisplayMode();
29699
29700                 if(c.showPin === true || this.showPin){
29701                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29702                     this.stickBtn.enableDisplayMode();
29703                     this.stickBtn.on("click", this.expand, this);
29704                     this.stickBtn.hide();
29705                 }
29706             }
29707             /** This region's collapsed element
29708             * @type Roo.Element */
29709             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29710                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29711             ]}, true);
29712             if(c.floatable !== false){
29713                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29714                this.collapsedEl.on("click", this.collapseClick, this);
29715             }
29716
29717             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29718                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29719                    id: "message", unselectable: "on", style:{"float":"left"}});
29720                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29721              }
29722             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29723             this.expandBtn.on("click", this.expand, this);
29724         }
29725         if(this.collapseBtn){
29726             this.collapseBtn.setVisible(c.collapsible == true);
29727         }
29728         this.cmargins = c.cmargins || this.cmargins ||
29729                          (this.position == "west" || this.position == "east" ?
29730                              {top: 0, left: 2, right:2, bottom: 0} :
29731                              {top: 2, left: 0, right:0, bottom: 2});
29732         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29733         this.bottomTabs = c.tabPosition != "top";
29734         this.autoScroll = c.autoScroll || false;
29735         if(this.autoScroll){
29736             this.bodyEl.setStyle("overflow", "auto");
29737         }else{
29738             this.bodyEl.setStyle("overflow", "hidden");
29739         }
29740         //if(c.titlebar !== false){
29741             if((!c.titlebar && !c.title) || c.titlebar === false){
29742                 this.titleEl.hide();
29743             }else{
29744                 this.titleEl.show();
29745                 if(c.title){
29746                     this.titleTextEl.innerHTML = c.title;
29747                 }
29748             }
29749         //}
29750         this.duration = c.duration || .30;
29751         this.slideDuration = c.slideDuration || .45;
29752         this.config = c;
29753         if(c.collapsed){
29754             this.collapse(true);
29755         }
29756         if(c.hidden){
29757             this.hide();
29758         }
29759     },
29760     /**
29761      * Returns true if this region is currently visible.
29762      * @return {Boolean}
29763      */
29764     isVisible : function(){
29765         return this.visible;
29766     },
29767
29768     /**
29769      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29770      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29771      */
29772     setCollapsedTitle : function(title){
29773         title = title || "&#160;";
29774         if(this.collapsedTitleTextEl){
29775             this.collapsedTitleTextEl.innerHTML = title;
29776         }
29777     },
29778
29779     getBox : function(){
29780         var b;
29781         if(!this.collapsed){
29782             b = this.el.getBox(false, true);
29783         }else{
29784             b = this.collapsedEl.getBox(false, true);
29785         }
29786         return b;
29787     },
29788
29789     getMargins : function(){
29790         return this.collapsed ? this.cmargins : this.margins;
29791     },
29792
29793     highlight : function(){
29794         this.el.addClass("x-layout-panel-dragover");
29795     },
29796
29797     unhighlight : function(){
29798         this.el.removeClass("x-layout-panel-dragover");
29799     },
29800
29801     updateBox : function(box){
29802         this.box = box;
29803         if(!this.collapsed){
29804             this.el.dom.style.left = box.x + "px";
29805             this.el.dom.style.top = box.y + "px";
29806             this.updateBody(box.width, box.height);
29807         }else{
29808             this.collapsedEl.dom.style.left = box.x + "px";
29809             this.collapsedEl.dom.style.top = box.y + "px";
29810             this.collapsedEl.setSize(box.width, box.height);
29811         }
29812         if(this.tabs){
29813             this.tabs.autoSizeTabs();
29814         }
29815     },
29816
29817     updateBody : function(w, h){
29818         if(w !== null){
29819             this.el.setWidth(w);
29820             w -= this.el.getBorderWidth("rl");
29821             if(this.config.adjustments){
29822                 w += this.config.adjustments[0];
29823             }
29824         }
29825         if(h !== null){
29826             this.el.setHeight(h);
29827             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29828             h -= this.el.getBorderWidth("tb");
29829             if(this.config.adjustments){
29830                 h += this.config.adjustments[1];
29831             }
29832             this.bodyEl.setHeight(h);
29833             if(this.tabs){
29834                 h = this.tabs.syncHeight(h);
29835             }
29836         }
29837         if(this.panelSize){
29838             w = w !== null ? w : this.panelSize.width;
29839             h = h !== null ? h : this.panelSize.height;
29840         }
29841         if(this.activePanel){
29842             var el = this.activePanel.getEl();
29843             w = w !== null ? w : el.getWidth();
29844             h = h !== null ? h : el.getHeight();
29845             this.panelSize = {width: w, height: h};
29846             this.activePanel.setSize(w, h);
29847         }
29848         if(Roo.isIE && this.tabs){
29849             this.tabs.el.repaint();
29850         }
29851     },
29852
29853     /**
29854      * Returns the container element for this region.
29855      * @return {Roo.Element}
29856      */
29857     getEl : function(){
29858         return this.el;
29859     },
29860
29861     /**
29862      * Hides this region.
29863      */
29864     hide : function(){
29865         if(!this.collapsed){
29866             this.el.dom.style.left = "-2000px";
29867             this.el.hide();
29868         }else{
29869             this.collapsedEl.dom.style.left = "-2000px";
29870             this.collapsedEl.hide();
29871         }
29872         this.visible = false;
29873         this.fireEvent("visibilitychange", this, false);
29874     },
29875
29876     /**
29877      * Shows this region if it was previously hidden.
29878      */
29879     show : function(){
29880         if(!this.collapsed){
29881             this.el.show();
29882         }else{
29883             this.collapsedEl.show();
29884         }
29885         this.visible = true;
29886         this.fireEvent("visibilitychange", this, true);
29887     },
29888
29889     closeClicked : function(){
29890         if(this.activePanel){
29891             this.remove(this.activePanel);
29892         }
29893     },
29894
29895     collapseClick : function(e){
29896         if(this.isSlid){
29897            e.stopPropagation();
29898            this.slideIn();
29899         }else{
29900            e.stopPropagation();
29901            this.slideOut();
29902         }
29903     },
29904
29905     /**
29906      * Collapses this region.
29907      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29908      */
29909     collapse : function(skipAnim, skipCheck = false){
29910         if(this.collapsed) {
29911             return;
29912         }
29913         
29914         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
29915             
29916             this.collapsed = true;
29917             if(this.split){
29918                 this.split.el.hide();
29919             }
29920             if(this.config.animate && skipAnim !== true){
29921                 this.fireEvent("invalidated", this);
29922                 this.animateCollapse();
29923             }else{
29924                 this.el.setLocation(-20000,-20000);
29925                 this.el.hide();
29926                 this.collapsedEl.show();
29927                 this.fireEvent("collapsed", this);
29928                 this.fireEvent("invalidated", this);
29929             }
29930         }
29931         
29932     },
29933
29934     animateCollapse : function(){
29935         // overridden
29936     },
29937
29938     /**
29939      * Expands this region if it was previously collapsed.
29940      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29941      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29942      */
29943     expand : function(e, skipAnim){
29944         if(e) {
29945             e.stopPropagation();
29946         }
29947         if(!this.collapsed || this.el.hasActiveFx()) {
29948             return;
29949         }
29950         if(this.isSlid){
29951             this.afterSlideIn();
29952             skipAnim = true;
29953         }
29954         this.collapsed = false;
29955         if(this.config.animate && skipAnim !== true){
29956             this.animateExpand();
29957         }else{
29958             this.el.show();
29959             if(this.split){
29960                 this.split.el.show();
29961             }
29962             this.collapsedEl.setLocation(-2000,-2000);
29963             this.collapsedEl.hide();
29964             this.fireEvent("invalidated", this);
29965             this.fireEvent("expanded", this);
29966         }
29967     },
29968
29969     animateExpand : function(){
29970         // overridden
29971     },
29972
29973     initTabs : function()
29974     {
29975         this.bodyEl.setStyle("overflow", "hidden");
29976         var ts = new Roo.TabPanel(
29977                 this.bodyEl.dom,
29978                 {
29979                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
29980                     disableTooltips: this.config.disableTabTips,
29981                     toolbar : this.config.toolbar
29982                 }
29983         );
29984         if(this.config.hideTabs){
29985             ts.stripWrap.setDisplayed(false);
29986         }
29987         this.tabs = ts;
29988         ts.resizeTabs = this.config.resizeTabs === true;
29989         ts.minTabWidth = this.config.minTabWidth || 40;
29990         ts.maxTabWidth = this.config.maxTabWidth || 250;
29991         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29992         ts.monitorResize = false;
29993         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29994         ts.bodyEl.addClass('x-layout-tabs-body');
29995         this.panels.each(this.initPanelAsTab, this);
29996     },
29997
29998     initPanelAsTab : function(panel){
29999         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30000                     this.config.closeOnTab && panel.isClosable());
30001         if(panel.tabTip !== undefined){
30002             ti.setTooltip(panel.tabTip);
30003         }
30004         ti.on("activate", function(){
30005               this.setActivePanel(panel);
30006         }, this);
30007         if(this.config.closeOnTab){
30008             ti.on("beforeclose", function(t, e){
30009                 e.cancel = true;
30010                 this.remove(panel);
30011             }, this);
30012         }
30013         return ti;
30014     },
30015
30016     updatePanelTitle : function(panel, title){
30017         if(this.activePanel == panel){
30018             this.updateTitle(title);
30019         }
30020         if(this.tabs){
30021             var ti = this.tabs.getTab(panel.getEl().id);
30022             ti.setText(title);
30023             if(panel.tabTip !== undefined){
30024                 ti.setTooltip(panel.tabTip);
30025             }
30026         }
30027     },
30028
30029     updateTitle : function(title){
30030         if(this.titleTextEl && !this.config.title){
30031             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30032         }
30033     },
30034
30035     setActivePanel : function(panel){
30036         panel = this.getPanel(panel);
30037         if(this.activePanel && this.activePanel != panel){
30038             this.activePanel.setActiveState(false);
30039         }
30040         this.activePanel = panel;
30041         panel.setActiveState(true);
30042         if(this.panelSize){
30043             panel.setSize(this.panelSize.width, this.panelSize.height);
30044         }
30045         if(this.closeBtn){
30046             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30047         }
30048         this.updateTitle(panel.getTitle());
30049         if(this.tabs){
30050             this.fireEvent("invalidated", this);
30051         }
30052         this.fireEvent("panelactivated", this, panel);
30053     },
30054
30055     /**
30056      * Shows the specified panel.
30057      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30058      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30059      */
30060     showPanel : function(panel)
30061     {
30062         panel = this.getPanel(panel);
30063         if(panel){
30064             if(this.tabs){
30065                 var tab = this.tabs.getTab(panel.getEl().id);
30066                 if(tab.isHidden()){
30067                     this.tabs.unhideTab(tab.id);
30068                 }
30069                 tab.activate();
30070             }else{
30071                 this.setActivePanel(panel);
30072             }
30073         }
30074         return panel;
30075     },
30076
30077     /**
30078      * Get the active panel for this region.
30079      * @return {Roo.ContentPanel} The active panel or null
30080      */
30081     getActivePanel : function(){
30082         return this.activePanel;
30083     },
30084
30085     validateVisibility : function(){
30086         if(this.panels.getCount() < 1){
30087             this.updateTitle("&#160;");
30088             this.closeBtn.hide();
30089             this.hide();
30090         }else{
30091             if(!this.isVisible()){
30092                 this.show();
30093             }
30094         }
30095     },
30096
30097     /**
30098      * Adds the passed ContentPanel(s) to this region.
30099      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30100      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30101      */
30102     add : function(panel){
30103         if(arguments.length > 1){
30104             for(var i = 0, len = arguments.length; i < len; i++) {
30105                 this.add(arguments[i]);
30106             }
30107             return null;
30108         }
30109         if(this.hasPanel(panel)){
30110             this.showPanel(panel);
30111             return panel;
30112         }
30113         panel.setRegion(this);
30114         this.panels.add(panel);
30115         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30116             this.bodyEl.dom.appendChild(panel.getEl().dom);
30117             if(panel.background !== true){
30118                 this.setActivePanel(panel);
30119             }
30120             this.fireEvent("paneladded", this, panel);
30121             return panel;
30122         }
30123         if(!this.tabs){
30124             this.initTabs();
30125         }else{
30126             this.initPanelAsTab(panel);
30127         }
30128         if(panel.background !== true){
30129             this.tabs.activate(panel.getEl().id);
30130         }
30131         this.fireEvent("paneladded", this, panel);
30132         return panel;
30133     },
30134
30135     /**
30136      * Hides the tab for the specified panel.
30137      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30138      */
30139     hidePanel : function(panel){
30140         if(this.tabs && (panel = this.getPanel(panel))){
30141             this.tabs.hideTab(panel.getEl().id);
30142         }
30143     },
30144
30145     /**
30146      * Unhides the tab for a previously hidden panel.
30147      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30148      */
30149     unhidePanel : function(panel){
30150         if(this.tabs && (panel = this.getPanel(panel))){
30151             this.tabs.unhideTab(panel.getEl().id);
30152         }
30153     },
30154
30155     clearPanels : function(){
30156         while(this.panels.getCount() > 0){
30157              this.remove(this.panels.first());
30158         }
30159     },
30160
30161     /**
30162      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30163      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30164      * @param {Boolean} preservePanel Overrides the config preservePanel option
30165      * @return {Roo.ContentPanel} The panel that was removed
30166      */
30167     remove : function(panel, preservePanel){
30168         panel = this.getPanel(panel);
30169         if(!panel){
30170             return null;
30171         }
30172         var e = {};
30173         this.fireEvent("beforeremove", this, panel, e);
30174         if(e.cancel === true){
30175             return null;
30176         }
30177         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30178         var panelId = panel.getId();
30179         this.panels.removeKey(panelId);
30180         if(preservePanel){
30181             document.body.appendChild(panel.getEl().dom);
30182         }
30183         if(this.tabs){
30184             this.tabs.removeTab(panel.getEl().id);
30185         }else if (!preservePanel){
30186             this.bodyEl.dom.removeChild(panel.getEl().dom);
30187         }
30188         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30189             var p = this.panels.first();
30190             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30191             tempEl.appendChild(p.getEl().dom);
30192             this.bodyEl.update("");
30193             this.bodyEl.dom.appendChild(p.getEl().dom);
30194             tempEl = null;
30195             this.updateTitle(p.getTitle());
30196             this.tabs = null;
30197             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30198             this.setActivePanel(p);
30199         }
30200         panel.setRegion(null);
30201         if(this.activePanel == panel){
30202             this.activePanel = null;
30203         }
30204         if(this.config.autoDestroy !== false && preservePanel !== true){
30205             try{panel.destroy();}catch(e){}
30206         }
30207         this.fireEvent("panelremoved", this, panel);
30208         return panel;
30209     },
30210
30211     /**
30212      * Returns the TabPanel component used by this region
30213      * @return {Roo.TabPanel}
30214      */
30215     getTabs : function(){
30216         return this.tabs;
30217     },
30218
30219     createTool : function(parentEl, className){
30220         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30221             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30222         btn.addClassOnOver("x-layout-tools-button-over");
30223         return btn;
30224     }
30225 });/*
30226  * Based on:
30227  * Ext JS Library 1.1.1
30228  * Copyright(c) 2006-2007, Ext JS, LLC.
30229  *
30230  * Originally Released Under LGPL - original licence link has changed is not relivant.
30231  *
30232  * Fork - LGPL
30233  * <script type="text/javascript">
30234  */
30235  
30236
30237
30238 /**
30239  * @class Roo.SplitLayoutRegion
30240  * @extends Roo.LayoutRegion
30241  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30242  */
30243 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30244     this.cursor = cursor;
30245     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30246 };
30247
30248 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30249     splitTip : "Drag to resize.",
30250     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30251     useSplitTips : false,
30252
30253     applyConfig : function(config){
30254         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30255         if(config.split){
30256             if(!this.split){
30257                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30258                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30259                 /** The SplitBar for this region 
30260                 * @type Roo.SplitBar */
30261                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30262                 this.split.on("moved", this.onSplitMove, this);
30263                 this.split.useShim = config.useShim === true;
30264                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30265                 if(this.useSplitTips){
30266                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30267                 }
30268                 if(config.collapsible){
30269                     this.split.el.on("dblclick", this.collapse,  this);
30270                 }
30271             }
30272             if(typeof config.minSize != "undefined"){
30273                 this.split.minSize = config.minSize;
30274             }
30275             if(typeof config.maxSize != "undefined"){
30276                 this.split.maxSize = config.maxSize;
30277             }
30278             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30279                 this.hideSplitter();
30280             }
30281         }
30282     },
30283
30284     getHMaxSize : function(){
30285          var cmax = this.config.maxSize || 10000;
30286          var center = this.mgr.getRegion("center");
30287          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30288     },
30289
30290     getVMaxSize : function(){
30291          var cmax = this.config.maxSize || 10000;
30292          var center = this.mgr.getRegion("center");
30293          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30294     },
30295
30296     onSplitMove : function(split, newSize){
30297         this.fireEvent("resized", this, newSize);
30298     },
30299     
30300     /** 
30301      * Returns the {@link Roo.SplitBar} for this region.
30302      * @return {Roo.SplitBar}
30303      */
30304     getSplitBar : function(){
30305         return this.split;
30306     },
30307     
30308     hide : function(){
30309         this.hideSplitter();
30310         Roo.SplitLayoutRegion.superclass.hide.call(this);
30311     },
30312
30313     hideSplitter : function(){
30314         if(this.split){
30315             this.split.el.setLocation(-2000,-2000);
30316             this.split.el.hide();
30317         }
30318     },
30319
30320     show : function(){
30321         if(this.split){
30322             this.split.el.show();
30323         }
30324         Roo.SplitLayoutRegion.superclass.show.call(this);
30325     },
30326     
30327     beforeSlide: function(){
30328         if(Roo.isGecko){// firefox overflow auto bug workaround
30329             this.bodyEl.clip();
30330             if(this.tabs) {
30331                 this.tabs.bodyEl.clip();
30332             }
30333             if(this.activePanel){
30334                 this.activePanel.getEl().clip();
30335                 
30336                 if(this.activePanel.beforeSlide){
30337                     this.activePanel.beforeSlide();
30338                 }
30339             }
30340         }
30341     },
30342     
30343     afterSlide : function(){
30344         if(Roo.isGecko){// firefox overflow auto bug workaround
30345             this.bodyEl.unclip();
30346             if(this.tabs) {
30347                 this.tabs.bodyEl.unclip();
30348             }
30349             if(this.activePanel){
30350                 this.activePanel.getEl().unclip();
30351                 if(this.activePanel.afterSlide){
30352                     this.activePanel.afterSlide();
30353                 }
30354             }
30355         }
30356     },
30357
30358     initAutoHide : function(){
30359         if(this.autoHide !== false){
30360             if(!this.autoHideHd){
30361                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30362                 this.autoHideHd = {
30363                     "mouseout": function(e){
30364                         if(!e.within(this.el, true)){
30365                             st.delay(500);
30366                         }
30367                     },
30368                     "mouseover" : function(e){
30369                         st.cancel();
30370                     },
30371                     scope : this
30372                 };
30373             }
30374             this.el.on(this.autoHideHd);
30375         }
30376     },
30377
30378     clearAutoHide : function(){
30379         if(this.autoHide !== false){
30380             this.el.un("mouseout", this.autoHideHd.mouseout);
30381             this.el.un("mouseover", this.autoHideHd.mouseover);
30382         }
30383     },
30384
30385     clearMonitor : function(){
30386         Roo.get(document).un("click", this.slideInIf, this);
30387     },
30388
30389     // these names are backwards but not changed for compat
30390     slideOut : function(){
30391         if(this.isSlid || this.el.hasActiveFx()){
30392             return;
30393         }
30394         this.isSlid = true;
30395         if(this.collapseBtn){
30396             this.collapseBtn.hide();
30397         }
30398         this.closeBtnState = this.closeBtn.getStyle('display');
30399         this.closeBtn.hide();
30400         if(this.stickBtn){
30401             this.stickBtn.show();
30402         }
30403         this.el.show();
30404         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30405         this.beforeSlide();
30406         this.el.setStyle("z-index", 10001);
30407         this.el.slideIn(this.getSlideAnchor(), {
30408             callback: function(){
30409                 this.afterSlide();
30410                 this.initAutoHide();
30411                 Roo.get(document).on("click", this.slideInIf, this);
30412                 this.fireEvent("slideshow", this);
30413             },
30414             scope: this,
30415             block: true
30416         });
30417     },
30418
30419     afterSlideIn : function(){
30420         this.clearAutoHide();
30421         this.isSlid = false;
30422         this.clearMonitor();
30423         this.el.setStyle("z-index", "");
30424         if(this.collapseBtn){
30425             this.collapseBtn.show();
30426         }
30427         this.closeBtn.setStyle('display', this.closeBtnState);
30428         if(this.stickBtn){
30429             this.stickBtn.hide();
30430         }
30431         this.fireEvent("slidehide", this);
30432     },
30433
30434     slideIn : function(cb){
30435         if(!this.isSlid || this.el.hasActiveFx()){
30436             Roo.callback(cb);
30437             return;
30438         }
30439         this.isSlid = false;
30440         this.beforeSlide();
30441         this.el.slideOut(this.getSlideAnchor(), {
30442             callback: function(){
30443                 this.el.setLeftTop(-10000, -10000);
30444                 this.afterSlide();
30445                 this.afterSlideIn();
30446                 Roo.callback(cb);
30447             },
30448             scope: this,
30449             block: true
30450         });
30451     },
30452     
30453     slideInIf : function(e){
30454         if(!e.within(this.el)){
30455             this.slideIn();
30456         }
30457     },
30458
30459     animateCollapse : function(){
30460         this.beforeSlide();
30461         this.el.setStyle("z-index", 20000);
30462         var anchor = this.getSlideAnchor();
30463         this.el.slideOut(anchor, {
30464             callback : function(){
30465                 this.el.setStyle("z-index", "");
30466                 this.collapsedEl.slideIn(anchor, {duration:.3});
30467                 this.afterSlide();
30468                 this.el.setLocation(-10000,-10000);
30469                 this.el.hide();
30470                 this.fireEvent("collapsed", this);
30471             },
30472             scope: this,
30473             block: true
30474         });
30475     },
30476
30477     animateExpand : function(){
30478         this.beforeSlide();
30479         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30480         this.el.setStyle("z-index", 20000);
30481         this.collapsedEl.hide({
30482             duration:.1
30483         });
30484         this.el.slideIn(this.getSlideAnchor(), {
30485             callback : function(){
30486                 this.el.setStyle("z-index", "");
30487                 this.afterSlide();
30488                 if(this.split){
30489                     this.split.el.show();
30490                 }
30491                 this.fireEvent("invalidated", this);
30492                 this.fireEvent("expanded", this);
30493             },
30494             scope: this,
30495             block: true
30496         });
30497     },
30498
30499     anchors : {
30500         "west" : "left",
30501         "east" : "right",
30502         "north" : "top",
30503         "south" : "bottom"
30504     },
30505
30506     sanchors : {
30507         "west" : "l",
30508         "east" : "r",
30509         "north" : "t",
30510         "south" : "b"
30511     },
30512
30513     canchors : {
30514         "west" : "tl-tr",
30515         "east" : "tr-tl",
30516         "north" : "tl-bl",
30517         "south" : "bl-tl"
30518     },
30519
30520     getAnchor : function(){
30521         return this.anchors[this.position];
30522     },
30523
30524     getCollapseAnchor : function(){
30525         return this.canchors[this.position];
30526     },
30527
30528     getSlideAnchor : function(){
30529         return this.sanchors[this.position];
30530     },
30531
30532     getAlignAdj : function(){
30533         var cm = this.cmargins;
30534         switch(this.position){
30535             case "west":
30536                 return [0, 0];
30537             break;
30538             case "east":
30539                 return [0, 0];
30540             break;
30541             case "north":
30542                 return [0, 0];
30543             break;
30544             case "south":
30545                 return [0, 0];
30546             break;
30547         }
30548     },
30549
30550     getExpandAdj : function(){
30551         var c = this.collapsedEl, cm = this.cmargins;
30552         switch(this.position){
30553             case "west":
30554                 return [-(cm.right+c.getWidth()+cm.left), 0];
30555             break;
30556             case "east":
30557                 return [cm.right+c.getWidth()+cm.left, 0];
30558             break;
30559             case "north":
30560                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30561             break;
30562             case "south":
30563                 return [0, cm.top+cm.bottom+c.getHeight()];
30564             break;
30565         }
30566     }
30567 });/*
30568  * Based on:
30569  * Ext JS Library 1.1.1
30570  * Copyright(c) 2006-2007, Ext JS, LLC.
30571  *
30572  * Originally Released Under LGPL - original licence link has changed is not relivant.
30573  *
30574  * Fork - LGPL
30575  * <script type="text/javascript">
30576  */
30577 /*
30578  * These classes are private internal classes
30579  */
30580 Roo.CenterLayoutRegion = function(mgr, config){
30581     Roo.LayoutRegion.call(this, mgr, config, "center");
30582     this.visible = true;
30583     this.minWidth = config.minWidth || 20;
30584     this.minHeight = config.minHeight || 20;
30585 };
30586
30587 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30588     hide : function(){
30589         // center panel can't be hidden
30590     },
30591     
30592     show : function(){
30593         // center panel can't be hidden
30594     },
30595     
30596     getMinWidth: function(){
30597         return this.minWidth;
30598     },
30599     
30600     getMinHeight: function(){
30601         return this.minHeight;
30602     }
30603 });
30604
30605
30606 Roo.NorthLayoutRegion = function(mgr, config){
30607     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30608     if(this.split){
30609         this.split.placement = Roo.SplitBar.TOP;
30610         this.split.orientation = Roo.SplitBar.VERTICAL;
30611         this.split.el.addClass("x-layout-split-v");
30612     }
30613     var size = config.initialSize || config.height;
30614     if(typeof size != "undefined"){
30615         this.el.setHeight(size);
30616     }
30617 };
30618 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30619     orientation: Roo.SplitBar.VERTICAL,
30620     getBox : function(){
30621         if(this.collapsed){
30622             return this.collapsedEl.getBox();
30623         }
30624         var box = this.el.getBox();
30625         if(this.split){
30626             box.height += this.split.el.getHeight();
30627         }
30628         return box;
30629     },
30630     
30631     updateBox : function(box){
30632         if(this.split && !this.collapsed){
30633             box.height -= this.split.el.getHeight();
30634             this.split.el.setLeft(box.x);
30635             this.split.el.setTop(box.y+box.height);
30636             this.split.el.setWidth(box.width);
30637         }
30638         if(this.collapsed){
30639             this.updateBody(box.width, null);
30640         }
30641         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30642     }
30643 });
30644
30645 Roo.SouthLayoutRegion = function(mgr, config){
30646     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30647     if(this.split){
30648         this.split.placement = Roo.SplitBar.BOTTOM;
30649         this.split.orientation = Roo.SplitBar.VERTICAL;
30650         this.split.el.addClass("x-layout-split-v");
30651     }
30652     var size = config.initialSize || config.height;
30653     if(typeof size != "undefined"){
30654         this.el.setHeight(size);
30655     }
30656 };
30657 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30658     orientation: Roo.SplitBar.VERTICAL,
30659     getBox : function(){
30660         if(this.collapsed){
30661             return this.collapsedEl.getBox();
30662         }
30663         var box = this.el.getBox();
30664         if(this.split){
30665             var sh = this.split.el.getHeight();
30666             box.height += sh;
30667             box.y -= sh;
30668         }
30669         return box;
30670     },
30671     
30672     updateBox : function(box){
30673         if(this.split && !this.collapsed){
30674             var sh = this.split.el.getHeight();
30675             box.height -= sh;
30676             box.y += sh;
30677             this.split.el.setLeft(box.x);
30678             this.split.el.setTop(box.y-sh);
30679             this.split.el.setWidth(box.width);
30680         }
30681         if(this.collapsed){
30682             this.updateBody(box.width, null);
30683         }
30684         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30685     }
30686 });
30687
30688 Roo.EastLayoutRegion = function(mgr, config){
30689     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30690     if(this.split){
30691         this.split.placement = Roo.SplitBar.RIGHT;
30692         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30693         this.split.el.addClass("x-layout-split-h");
30694     }
30695     var size = config.initialSize || config.width;
30696     if(typeof size != "undefined"){
30697         this.el.setWidth(size);
30698     }
30699 };
30700 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30701     orientation: Roo.SplitBar.HORIZONTAL,
30702     getBox : function(){
30703         if(this.collapsed){
30704             return this.collapsedEl.getBox();
30705         }
30706         var box = this.el.getBox();
30707         if(this.split){
30708             var sw = this.split.el.getWidth();
30709             box.width += sw;
30710             box.x -= sw;
30711         }
30712         return box;
30713     },
30714
30715     updateBox : function(box){
30716         if(this.split && !this.collapsed){
30717             var sw = this.split.el.getWidth();
30718             box.width -= sw;
30719             this.split.el.setLeft(box.x);
30720             this.split.el.setTop(box.y);
30721             this.split.el.setHeight(box.height);
30722             box.x += sw;
30723         }
30724         if(this.collapsed){
30725             this.updateBody(null, box.height);
30726         }
30727         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30728     }
30729 });
30730
30731 Roo.WestLayoutRegion = function(mgr, config){
30732     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30733     if(this.split){
30734         this.split.placement = Roo.SplitBar.LEFT;
30735         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30736         this.split.el.addClass("x-layout-split-h");
30737     }
30738     var size = config.initialSize || config.width;
30739     if(typeof size != "undefined"){
30740         this.el.setWidth(size);
30741     }
30742 };
30743 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30744     orientation: Roo.SplitBar.HORIZONTAL,
30745     getBox : function(){
30746         if(this.collapsed){
30747             return this.collapsedEl.getBox();
30748         }
30749         var box = this.el.getBox();
30750         if(this.split){
30751             box.width += this.split.el.getWidth();
30752         }
30753         return box;
30754     },
30755     
30756     updateBox : function(box){
30757         if(this.split && !this.collapsed){
30758             var sw = this.split.el.getWidth();
30759             box.width -= sw;
30760             this.split.el.setLeft(box.x+box.width);
30761             this.split.el.setTop(box.y);
30762             this.split.el.setHeight(box.height);
30763         }
30764         if(this.collapsed){
30765             this.updateBody(null, box.height);
30766         }
30767         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30768     }
30769 });
30770 /*
30771  * Based on:
30772  * Ext JS Library 1.1.1
30773  * Copyright(c) 2006-2007, Ext JS, LLC.
30774  *
30775  * Originally Released Under LGPL - original licence link has changed is not relivant.
30776  *
30777  * Fork - LGPL
30778  * <script type="text/javascript">
30779  */
30780  
30781  
30782 /*
30783  * Private internal class for reading and applying state
30784  */
30785 Roo.LayoutStateManager = function(layout){
30786      // default empty state
30787      this.state = {
30788         north: {},
30789         south: {},
30790         east: {},
30791         west: {}       
30792     };
30793 };
30794
30795 Roo.LayoutStateManager.prototype = {
30796     init : function(layout, provider){
30797         this.provider = provider;
30798         var state = provider.get(layout.id+"-layout-state");
30799         if(state){
30800             var wasUpdating = layout.isUpdating();
30801             if(!wasUpdating){
30802                 layout.beginUpdate();
30803             }
30804             for(var key in state){
30805                 if(typeof state[key] != "function"){
30806                     var rstate = state[key];
30807                     var r = layout.getRegion(key);
30808                     if(r && rstate){
30809                         if(rstate.size){
30810                             r.resizeTo(rstate.size);
30811                         }
30812                         if(rstate.collapsed == true){
30813                             r.collapse(true);
30814                         }else{
30815                             r.expand(null, true);
30816                         }
30817                     }
30818                 }
30819             }
30820             if(!wasUpdating){
30821                 layout.endUpdate();
30822             }
30823             this.state = state; 
30824         }
30825         this.layout = layout;
30826         layout.on("regionresized", this.onRegionResized, this);
30827         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30828         layout.on("regionexpanded", this.onRegionExpanded, this);
30829     },
30830     
30831     storeState : function(){
30832         this.provider.set(this.layout.id+"-layout-state", this.state);
30833     },
30834     
30835     onRegionResized : function(region, newSize){
30836         this.state[region.getPosition()].size = newSize;
30837         this.storeState();
30838     },
30839     
30840     onRegionCollapsed : function(region){
30841         this.state[region.getPosition()].collapsed = true;
30842         this.storeState();
30843     },
30844     
30845     onRegionExpanded : function(region){
30846         this.state[region.getPosition()].collapsed = false;
30847         this.storeState();
30848     }
30849 };/*
30850  * Based on:
30851  * Ext JS Library 1.1.1
30852  * Copyright(c) 2006-2007, Ext JS, LLC.
30853  *
30854  * Originally Released Under LGPL - original licence link has changed is not relivant.
30855  *
30856  * Fork - LGPL
30857  * <script type="text/javascript">
30858  */
30859 /**
30860  * @class Roo.ContentPanel
30861  * @extends Roo.util.Observable
30862  * A basic ContentPanel element.
30863  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30864  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30865  * @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
30866  * @cfg {Boolean}   closable      True if the panel can be closed/removed
30867  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
30868  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30869  * @cfg {Toolbar}   toolbar       A toolbar for this panel
30870  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
30871  * @cfg {String} title          The title for this panel
30872  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30873  * @cfg {String} url            Calls {@link #setUrl} with this value
30874  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30875  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
30876  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
30877  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
30878
30879  * @constructor
30880  * Create a new ContentPanel.
30881  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30882  * @param {String/Object} config A string to set only the title or a config object
30883  * @param {String} content (optional) Set the HTML content for this panel
30884  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30885  */
30886 Roo.ContentPanel = function(el, config, content){
30887     
30888      
30889     /*
30890     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30891         config = el;
30892         el = Roo.id();
30893     }
30894     if (config && config.parentLayout) { 
30895         el = config.parentLayout.el.createChild(); 
30896     }
30897     */
30898     if(el.autoCreate){ // xtype is available if this is called from factory
30899         config = el;
30900         el = Roo.id();
30901     }
30902     this.el = Roo.get(el);
30903     if(!this.el && config && config.autoCreate){
30904         if(typeof config.autoCreate == "object"){
30905             if(!config.autoCreate.id){
30906                 config.autoCreate.id = config.id||el;
30907             }
30908             this.el = Roo.DomHelper.append(document.body,
30909                         config.autoCreate, true);
30910         }else{
30911             this.el = Roo.DomHelper.append(document.body,
30912                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30913         }
30914     }
30915     this.closable = false;
30916     this.loaded = false;
30917     this.active = false;
30918     if(typeof config == "string"){
30919         this.title = config;
30920     }else{
30921         Roo.apply(this, config);
30922     }
30923     
30924     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30925         this.wrapEl = this.el.wrap();
30926         this.toolbar.container = this.el.insertSibling(false, 'before');
30927         this.toolbar = new Roo.Toolbar(this.toolbar);
30928     }
30929     
30930     // xtype created footer. - not sure if will work as we normally have to render first..
30931     if (this.footer && !this.footer.el && this.footer.xtype) {
30932         if (!this.wrapEl) {
30933             this.wrapEl = this.el.wrap();
30934         }
30935     
30936         this.footer.container = this.wrapEl.createChild();
30937          
30938         this.footer = Roo.factory(this.footer, Roo);
30939         
30940     }
30941     
30942     if(this.resizeEl){
30943         this.resizeEl = Roo.get(this.resizeEl, true);
30944     }else{
30945         this.resizeEl = this.el;
30946     }
30947     // handle view.xtype
30948     
30949  
30950     
30951     
30952     this.addEvents({
30953         /**
30954          * @event activate
30955          * Fires when this panel is activated. 
30956          * @param {Roo.ContentPanel} this
30957          */
30958         "activate" : true,
30959         /**
30960          * @event deactivate
30961          * Fires when this panel is activated. 
30962          * @param {Roo.ContentPanel} this
30963          */
30964         "deactivate" : true,
30965
30966         /**
30967          * @event resize
30968          * Fires when this panel is resized if fitToFrame is true.
30969          * @param {Roo.ContentPanel} this
30970          * @param {Number} width The width after any component adjustments
30971          * @param {Number} height The height after any component adjustments
30972          */
30973         "resize" : true,
30974         
30975          /**
30976          * @event render
30977          * Fires when this tab is created
30978          * @param {Roo.ContentPanel} this
30979          */
30980         "render" : true
30981          
30982         
30983     });
30984     
30985
30986     
30987     
30988     if(this.autoScroll){
30989         this.resizeEl.setStyle("overflow", "auto");
30990     } else {
30991         // fix randome scrolling
30992         this.el.on('scroll', function() {
30993             Roo.log('fix random scolling');
30994             this.scrollTo('top',0); 
30995         });
30996     }
30997     content = content || this.content;
30998     if(content){
30999         this.setContent(content);
31000     }
31001     if(config && config.url){
31002         this.setUrl(this.url, this.params, this.loadOnce);
31003     }
31004     
31005     
31006     
31007     Roo.ContentPanel.superclass.constructor.call(this);
31008     
31009     if (this.view && typeof(this.view.xtype) != 'undefined') {
31010         this.view.el = this.el.appendChild(document.createElement("div"));
31011         this.view = Roo.factory(this.view); 
31012         this.view.render  &&  this.view.render(false, '');  
31013     }
31014     
31015     
31016     this.fireEvent('render', this);
31017 };
31018
31019 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31020     tabTip:'',
31021     setRegion : function(region){
31022         this.region = region;
31023         if(region){
31024            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31025         }else{
31026            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31027         } 
31028     },
31029     
31030     /**
31031      * Returns the toolbar for this Panel if one was configured. 
31032      * @return {Roo.Toolbar} 
31033      */
31034     getToolbar : function(){
31035         return this.toolbar;
31036     },
31037     
31038     setActiveState : function(active){
31039         this.active = active;
31040         if(!active){
31041             this.fireEvent("deactivate", this);
31042         }else{
31043             this.fireEvent("activate", this);
31044         }
31045     },
31046     /**
31047      * Updates this panel's element
31048      * @param {String} content The new content
31049      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31050     */
31051     setContent : function(content, loadScripts){
31052         this.el.update(content, loadScripts);
31053     },
31054
31055     ignoreResize : function(w, h){
31056         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31057             return true;
31058         }else{
31059             this.lastSize = {width: w, height: h};
31060             return false;
31061         }
31062     },
31063     /**
31064      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31065      * @return {Roo.UpdateManager} The UpdateManager
31066      */
31067     getUpdateManager : function(){
31068         return this.el.getUpdateManager();
31069     },
31070      /**
31071      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31072      * @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:
31073 <pre><code>
31074 panel.load({
31075     url: "your-url.php",
31076     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31077     callback: yourFunction,
31078     scope: yourObject, //(optional scope)
31079     discardUrl: false,
31080     nocache: false,
31081     text: "Loading...",
31082     timeout: 30,
31083     scripts: false
31084 });
31085 </code></pre>
31086      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31087      * 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.
31088      * @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}
31089      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31090      * @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.
31091      * @return {Roo.ContentPanel} this
31092      */
31093     load : function(){
31094         var um = this.el.getUpdateManager();
31095         um.update.apply(um, arguments);
31096         return this;
31097     },
31098
31099
31100     /**
31101      * 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.
31102      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31103      * @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)
31104      * @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)
31105      * @return {Roo.UpdateManager} The UpdateManager
31106      */
31107     setUrl : function(url, params, loadOnce){
31108         if(this.refreshDelegate){
31109             this.removeListener("activate", this.refreshDelegate);
31110         }
31111         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31112         this.on("activate", this.refreshDelegate);
31113         return this.el.getUpdateManager();
31114     },
31115     
31116     _handleRefresh : function(url, params, loadOnce){
31117         if(!loadOnce || !this.loaded){
31118             var updater = this.el.getUpdateManager();
31119             updater.update(url, params, this._setLoaded.createDelegate(this));
31120         }
31121     },
31122     
31123     _setLoaded : function(){
31124         this.loaded = true;
31125     }, 
31126     
31127     /**
31128      * Returns this panel's id
31129      * @return {String} 
31130      */
31131     getId : function(){
31132         return this.el.id;
31133     },
31134     
31135     /** 
31136      * Returns this panel's element - used by regiosn to add.
31137      * @return {Roo.Element} 
31138      */
31139     getEl : function(){
31140         return this.wrapEl || this.el;
31141     },
31142     
31143     adjustForComponents : function(width, height)
31144     {
31145         //Roo.log('adjustForComponents ');
31146         if(this.resizeEl != this.el){
31147             width -= this.el.getFrameWidth('lr');
31148             height -= this.el.getFrameWidth('tb');
31149         }
31150         if(this.toolbar){
31151             var te = this.toolbar.getEl();
31152             height -= te.getHeight();
31153             te.setWidth(width);
31154         }
31155         if(this.footer){
31156             var te = this.footer.getEl();
31157             //Roo.log("footer:" + te.getHeight());
31158             
31159             height -= te.getHeight();
31160             te.setWidth(width);
31161         }
31162         
31163         
31164         if(this.adjustments){
31165             width += this.adjustments[0];
31166             height += this.adjustments[1];
31167         }
31168         return {"width": width, "height": height};
31169     },
31170     
31171     setSize : function(width, height){
31172         if(this.fitToFrame && !this.ignoreResize(width, height)){
31173             if(this.fitContainer && this.resizeEl != this.el){
31174                 this.el.setSize(width, height);
31175             }
31176             var size = this.adjustForComponents(width, height);
31177             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31178             this.fireEvent('resize', this, size.width, size.height);
31179         }
31180     },
31181     
31182     /**
31183      * Returns this panel's title
31184      * @return {String} 
31185      */
31186     getTitle : function(){
31187         return this.title;
31188     },
31189     
31190     /**
31191      * Set this panel's title
31192      * @param {String} title
31193      */
31194     setTitle : function(title){
31195         this.title = title;
31196         if(this.region){
31197             this.region.updatePanelTitle(this, title);
31198         }
31199     },
31200     
31201     /**
31202      * Returns true is this panel was configured to be closable
31203      * @return {Boolean} 
31204      */
31205     isClosable : function(){
31206         return this.closable;
31207     },
31208     
31209     beforeSlide : function(){
31210         this.el.clip();
31211         this.resizeEl.clip();
31212     },
31213     
31214     afterSlide : function(){
31215         this.el.unclip();
31216         this.resizeEl.unclip();
31217     },
31218     
31219     /**
31220      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31221      *   Will fail silently if the {@link #setUrl} method has not been called.
31222      *   This does not activate the panel, just updates its content.
31223      */
31224     refresh : function(){
31225         if(this.refreshDelegate){
31226            this.loaded = false;
31227            this.refreshDelegate();
31228         }
31229     },
31230     
31231     /**
31232      * Destroys this panel
31233      */
31234     destroy : function(){
31235         this.el.removeAllListeners();
31236         var tempEl = document.createElement("span");
31237         tempEl.appendChild(this.el.dom);
31238         tempEl.innerHTML = "";
31239         this.el.remove();
31240         this.el = null;
31241     },
31242     
31243     /**
31244      * form - if the content panel contains a form - this is a reference to it.
31245      * @type {Roo.form.Form}
31246      */
31247     form : false,
31248     /**
31249      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31250      *    This contains a reference to it.
31251      * @type {Roo.View}
31252      */
31253     view : false,
31254     
31255       /**
31256      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31257      * <pre><code>
31258
31259 layout.addxtype({
31260        xtype : 'Form',
31261        items: [ .... ]
31262    }
31263 );
31264
31265 </code></pre>
31266      * @param {Object} cfg Xtype definition of item to add.
31267      */
31268     
31269     addxtype : function(cfg) {
31270         // add form..
31271         if (cfg.xtype.match(/^Form$/)) {
31272             
31273             var el;
31274             //if (this.footer) {
31275             //    el = this.footer.container.insertSibling(false, 'before');
31276             //} else {
31277                 el = this.el.createChild();
31278             //}
31279
31280             this.form = new  Roo.form.Form(cfg);
31281             
31282             
31283             if ( this.form.allItems.length) {
31284                 this.form.render(el.dom);
31285             }
31286             return this.form;
31287         }
31288         // should only have one of theses..
31289         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31290             // views.. should not be just added - used named prop 'view''
31291             
31292             cfg.el = this.el.appendChild(document.createElement("div"));
31293             // factory?
31294             
31295             var ret = new Roo.factory(cfg);
31296              
31297              ret.render && ret.render(false, ''); // render blank..
31298             this.view = ret;
31299             return ret;
31300         }
31301         return false;
31302     }
31303 });
31304
31305 /**
31306  * @class Roo.GridPanel
31307  * @extends Roo.ContentPanel
31308  * @constructor
31309  * Create a new GridPanel.
31310  * @param {Roo.grid.Grid} grid The grid for this panel
31311  * @param {String/Object} config A string to set only the panel's title, or a config object
31312  */
31313 Roo.GridPanel = function(grid, config){
31314     
31315   
31316     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31317         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31318         
31319     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31320     
31321     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31322     
31323     if(this.toolbar){
31324         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31325     }
31326     // xtype created footer. - not sure if will work as we normally have to render first..
31327     if (this.footer && !this.footer.el && this.footer.xtype) {
31328         
31329         this.footer.container = this.grid.getView().getFooterPanel(true);
31330         this.footer.dataSource = this.grid.dataSource;
31331         this.footer = Roo.factory(this.footer, Roo);
31332         
31333     }
31334     
31335     grid.monitorWindowResize = false; // turn off autosizing
31336     grid.autoHeight = false;
31337     grid.autoWidth = false;
31338     this.grid = grid;
31339     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31340 };
31341
31342 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31343     getId : function(){
31344         return this.grid.id;
31345     },
31346     
31347     /**
31348      * Returns the grid for this panel
31349      * @return {Roo.grid.Grid} 
31350      */
31351     getGrid : function(){
31352         return this.grid;    
31353     },
31354     
31355     setSize : function(width, height){
31356         if(!this.ignoreResize(width, height)){
31357             var grid = this.grid;
31358             var size = this.adjustForComponents(width, height);
31359             grid.getGridEl().setSize(size.width, size.height);
31360             grid.autoSize();
31361         }
31362     },
31363     
31364     beforeSlide : function(){
31365         this.grid.getView().scroller.clip();
31366     },
31367     
31368     afterSlide : function(){
31369         this.grid.getView().scroller.unclip();
31370     },
31371     
31372     destroy : function(){
31373         this.grid.destroy();
31374         delete this.grid;
31375         Roo.GridPanel.superclass.destroy.call(this); 
31376     }
31377 });
31378
31379
31380 /**
31381  * @class Roo.NestedLayoutPanel
31382  * @extends Roo.ContentPanel
31383  * @constructor
31384  * Create a new NestedLayoutPanel.
31385  * 
31386  * 
31387  * @param {Roo.BorderLayout} layout The layout for this panel
31388  * @param {String/Object} config A string to set only the title or a config object
31389  */
31390 Roo.NestedLayoutPanel = function(layout, config)
31391 {
31392     // construct with only one argument..
31393     /* FIXME - implement nicer consturctors
31394     if (layout.layout) {
31395         config = layout;
31396         layout = config.layout;
31397         delete config.layout;
31398     }
31399     if (layout.xtype && !layout.getEl) {
31400         // then layout needs constructing..
31401         layout = Roo.factory(layout, Roo);
31402     }
31403     */
31404     
31405     
31406     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31407     
31408     layout.monitorWindowResize = false; // turn off autosizing
31409     this.layout = layout;
31410     this.layout.getEl().addClass("x-layout-nested-layout");
31411     
31412     
31413     
31414     
31415 };
31416
31417 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31418
31419     setSize : function(width, height){
31420         if(!this.ignoreResize(width, height)){
31421             var size = this.adjustForComponents(width, height);
31422             var el = this.layout.getEl();
31423             el.setSize(size.width, size.height);
31424             var touch = el.dom.offsetWidth;
31425             this.layout.layout();
31426             // ie requires a double layout on the first pass
31427             if(Roo.isIE && !this.initialized){
31428                 this.initialized = true;
31429                 this.layout.layout();
31430             }
31431         }
31432     },
31433     
31434     // activate all subpanels if not currently active..
31435     
31436     setActiveState : function(active){
31437         this.active = active;
31438         if(!active){
31439             this.fireEvent("deactivate", this);
31440             return;
31441         }
31442         
31443         this.fireEvent("activate", this);
31444         // not sure if this should happen before or after..
31445         if (!this.layout) {
31446             return; // should not happen..
31447         }
31448         var reg = false;
31449         for (var r in this.layout.regions) {
31450             reg = this.layout.getRegion(r);
31451             if (reg.getActivePanel()) {
31452                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31453                 reg.setActivePanel(reg.getActivePanel());
31454                 continue;
31455             }
31456             if (!reg.panels.length) {
31457                 continue;
31458             }
31459             reg.showPanel(reg.getPanel(0));
31460         }
31461         
31462         
31463         
31464         
31465     },
31466     
31467     /**
31468      * Returns the nested BorderLayout for this panel
31469      * @return {Roo.BorderLayout} 
31470      */
31471     getLayout : function(){
31472         return this.layout;
31473     },
31474     
31475      /**
31476      * Adds a xtype elements to the layout of the nested panel
31477      * <pre><code>
31478
31479 panel.addxtype({
31480        xtype : 'ContentPanel',
31481        region: 'west',
31482        items: [ .... ]
31483    }
31484 );
31485
31486 panel.addxtype({
31487         xtype : 'NestedLayoutPanel',
31488         region: 'west',
31489         layout: {
31490            center: { },
31491            west: { }   
31492         },
31493         items : [ ... list of content panels or nested layout panels.. ]
31494    }
31495 );
31496 </code></pre>
31497      * @param {Object} cfg Xtype definition of item to add.
31498      */
31499     addxtype : function(cfg) {
31500         return this.layout.addxtype(cfg);
31501     
31502     }
31503 });
31504
31505 Roo.ScrollPanel = function(el, config, content){
31506     config = config || {};
31507     config.fitToFrame = true;
31508     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31509     
31510     this.el.dom.style.overflow = "hidden";
31511     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31512     this.el.removeClass("x-layout-inactive-content");
31513     this.el.on("mousewheel", this.onWheel, this);
31514
31515     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31516     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31517     up.unselectable(); down.unselectable();
31518     up.on("click", this.scrollUp, this);
31519     down.on("click", this.scrollDown, this);
31520     up.addClassOnOver("x-scroller-btn-over");
31521     down.addClassOnOver("x-scroller-btn-over");
31522     up.addClassOnClick("x-scroller-btn-click");
31523     down.addClassOnClick("x-scroller-btn-click");
31524     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31525
31526     this.resizeEl = this.el;
31527     this.el = wrap; this.up = up; this.down = down;
31528 };
31529
31530 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31531     increment : 100,
31532     wheelIncrement : 5,
31533     scrollUp : function(){
31534         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31535     },
31536
31537     scrollDown : function(){
31538         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31539     },
31540
31541     afterScroll : function(){
31542         var el = this.resizeEl;
31543         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31544         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31545         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31546     },
31547
31548     setSize : function(){
31549         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31550         this.afterScroll();
31551     },
31552
31553     onWheel : function(e){
31554         var d = e.getWheelDelta();
31555         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31556         this.afterScroll();
31557         e.stopEvent();
31558     },
31559
31560     setContent : function(content, loadScripts){
31561         this.resizeEl.update(content, loadScripts);
31562     }
31563
31564 });
31565
31566
31567
31568
31569
31570
31571
31572
31573
31574 /**
31575  * @class Roo.TreePanel
31576  * @extends Roo.ContentPanel
31577  * @constructor
31578  * Create a new TreePanel. - defaults to fit/scoll contents.
31579  * @param {String/Object} config A string to set only the panel's title, or a config object
31580  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31581  */
31582 Roo.TreePanel = function(config){
31583     var el = config.el;
31584     var tree = config.tree;
31585     delete config.tree; 
31586     delete config.el; // hopefull!
31587     
31588     // wrapper for IE7 strict & safari scroll issue
31589     
31590     var treeEl = el.createChild();
31591     config.resizeEl = treeEl;
31592     
31593     
31594     
31595     Roo.TreePanel.superclass.constructor.call(this, el, config);
31596  
31597  
31598     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31599     //console.log(tree);
31600     this.on('activate', function()
31601     {
31602         if (this.tree.rendered) {
31603             return;
31604         }
31605         //console.log('render tree');
31606         this.tree.render();
31607     });
31608     // this should not be needed.. - it's actually the 'el' that resizes?
31609     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31610     
31611     //this.on('resize',  function (cp, w, h) {
31612     //        this.tree.innerCt.setWidth(w);
31613     //        this.tree.innerCt.setHeight(h);
31614     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31615     //});
31616
31617         
31618     
31619 };
31620
31621 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31622     fitToFrame : true,
31623     autoScroll : true
31624 });
31625
31626
31627
31628
31629
31630
31631
31632
31633
31634
31635
31636 /*
31637  * Based on:
31638  * Ext JS Library 1.1.1
31639  * Copyright(c) 2006-2007, Ext JS, LLC.
31640  *
31641  * Originally Released Under LGPL - original licence link has changed is not relivant.
31642  *
31643  * Fork - LGPL
31644  * <script type="text/javascript">
31645  */
31646  
31647
31648 /**
31649  * @class Roo.ReaderLayout
31650  * @extends Roo.BorderLayout
31651  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31652  * center region containing two nested regions (a top one for a list view and one for item preview below),
31653  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31654  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31655  * expedites the setup of the overall layout and regions for this common application style.
31656  * Example:
31657  <pre><code>
31658 var reader = new Roo.ReaderLayout();
31659 var CP = Roo.ContentPanel;  // shortcut for adding
31660
31661 reader.beginUpdate();
31662 reader.add("north", new CP("north", "North"));
31663 reader.add("west", new CP("west", {title: "West"}));
31664 reader.add("east", new CP("east", {title: "East"}));
31665
31666 reader.regions.listView.add(new CP("listView", "List"));
31667 reader.regions.preview.add(new CP("preview", "Preview"));
31668 reader.endUpdate();
31669 </code></pre>
31670 * @constructor
31671 * Create a new ReaderLayout
31672 * @param {Object} config Configuration options
31673 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31674 * document.body if omitted)
31675 */
31676 Roo.ReaderLayout = function(config, renderTo){
31677     var c = config || {size:{}};
31678     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31679         north: c.north !== false ? Roo.apply({
31680             split:false,
31681             initialSize: 32,
31682             titlebar: false
31683         }, c.north) : false,
31684         west: c.west !== false ? Roo.apply({
31685             split:true,
31686             initialSize: 200,
31687             minSize: 175,
31688             maxSize: 400,
31689             titlebar: true,
31690             collapsible: true,
31691             animate: true,
31692             margins:{left:5,right:0,bottom:5,top:5},
31693             cmargins:{left:5,right:5,bottom:5,top:5}
31694         }, c.west) : false,
31695         east: c.east !== false ? Roo.apply({
31696             split:true,
31697             initialSize: 200,
31698             minSize: 175,
31699             maxSize: 400,
31700             titlebar: true,
31701             collapsible: true,
31702             animate: true,
31703             margins:{left:0,right:5,bottom:5,top:5},
31704             cmargins:{left:5,right:5,bottom:5,top:5}
31705         }, c.east) : false,
31706         center: Roo.apply({
31707             tabPosition: 'top',
31708             autoScroll:false,
31709             closeOnTab: true,
31710             titlebar:false,
31711             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31712         }, c.center)
31713     });
31714
31715     this.el.addClass('x-reader');
31716
31717     this.beginUpdate();
31718
31719     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31720         south: c.preview !== false ? Roo.apply({
31721             split:true,
31722             initialSize: 200,
31723             minSize: 100,
31724             autoScroll:true,
31725             collapsible:true,
31726             titlebar: true,
31727             cmargins:{top:5,left:0, right:0, bottom:0}
31728         }, c.preview) : false,
31729         center: Roo.apply({
31730             autoScroll:false,
31731             titlebar:false,
31732             minHeight:200
31733         }, c.listView)
31734     });
31735     this.add('center', new Roo.NestedLayoutPanel(inner,
31736             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31737
31738     this.endUpdate();
31739
31740     this.regions.preview = inner.getRegion('south');
31741     this.regions.listView = inner.getRegion('center');
31742 };
31743
31744 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31745  * Based on:
31746  * Ext JS Library 1.1.1
31747  * Copyright(c) 2006-2007, Ext JS, LLC.
31748  *
31749  * Originally Released Under LGPL - original licence link has changed is not relivant.
31750  *
31751  * Fork - LGPL
31752  * <script type="text/javascript">
31753  */
31754  
31755 /**
31756  * @class Roo.grid.Grid
31757  * @extends Roo.util.Observable
31758  * This class represents the primary interface of a component based grid control.
31759  * <br><br>Usage:<pre><code>
31760  var grid = new Roo.grid.Grid("my-container-id", {
31761      ds: myDataStore,
31762      cm: myColModel,
31763      selModel: mySelectionModel,
31764      autoSizeColumns: true,
31765      monitorWindowResize: false,
31766      trackMouseOver: true
31767  });
31768  // set any options
31769  grid.render();
31770  * </code></pre>
31771  * <b>Common Problems:</b><br/>
31772  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31773  * element will correct this<br/>
31774  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31775  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31776  * are unpredictable.<br/>
31777  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31778  * grid to calculate dimensions/offsets.<br/>
31779   * @constructor
31780  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31781  * The container MUST have some type of size defined for the grid to fill. The container will be
31782  * automatically set to position relative if it isn't already.
31783  * @param {Object} config A config object that sets properties on this grid.
31784  */
31785 Roo.grid.Grid = function(container, config){
31786         // initialize the container
31787         this.container = Roo.get(container);
31788         this.container.update("");
31789         this.container.setStyle("overflow", "hidden");
31790     this.container.addClass('x-grid-container');
31791
31792     this.id = this.container.id;
31793
31794     Roo.apply(this, config);
31795     // check and correct shorthanded configs
31796     if(this.ds){
31797         this.dataSource = this.ds;
31798         delete this.ds;
31799     }
31800     if(this.cm){
31801         this.colModel = this.cm;
31802         delete this.cm;
31803     }
31804     if(this.sm){
31805         this.selModel = this.sm;
31806         delete this.sm;
31807     }
31808
31809     if (this.selModel) {
31810         this.selModel = Roo.factory(this.selModel, Roo.grid);
31811         this.sm = this.selModel;
31812         this.sm.xmodule = this.xmodule || false;
31813     }
31814     if (typeof(this.colModel.config) == 'undefined') {
31815         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31816         this.cm = this.colModel;
31817         this.cm.xmodule = this.xmodule || false;
31818     }
31819     if (this.dataSource) {
31820         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31821         this.ds = this.dataSource;
31822         this.ds.xmodule = this.xmodule || false;
31823          
31824     }
31825     
31826     
31827     
31828     if(this.width){
31829         this.container.setWidth(this.width);
31830     }
31831
31832     if(this.height){
31833         this.container.setHeight(this.height);
31834     }
31835     /** @private */
31836         this.addEvents({
31837         // raw events
31838         /**
31839          * @event click
31840          * The raw click event for the entire grid.
31841          * @param {Roo.EventObject} e
31842          */
31843         "click" : true,
31844         /**
31845          * @event dblclick
31846          * The raw dblclick event for the entire grid.
31847          * @param {Roo.EventObject} e
31848          */
31849         "dblclick" : true,
31850         /**
31851          * @event contextmenu
31852          * The raw contextmenu event for the entire grid.
31853          * @param {Roo.EventObject} e
31854          */
31855         "contextmenu" : true,
31856         /**
31857          * @event mousedown
31858          * The raw mousedown event for the entire grid.
31859          * @param {Roo.EventObject} e
31860          */
31861         "mousedown" : true,
31862         /**
31863          * @event mouseup
31864          * The raw mouseup event for the entire grid.
31865          * @param {Roo.EventObject} e
31866          */
31867         "mouseup" : true,
31868         /**
31869          * @event mouseover
31870          * The raw mouseover event for the entire grid.
31871          * @param {Roo.EventObject} e
31872          */
31873         "mouseover" : true,
31874         /**
31875          * @event mouseout
31876          * The raw mouseout event for the entire grid.
31877          * @param {Roo.EventObject} e
31878          */
31879         "mouseout" : true,
31880         /**
31881          * @event keypress
31882          * The raw keypress event for the entire grid.
31883          * @param {Roo.EventObject} e
31884          */
31885         "keypress" : true,
31886         /**
31887          * @event keydown
31888          * The raw keydown event for the entire grid.
31889          * @param {Roo.EventObject} e
31890          */
31891         "keydown" : true,
31892
31893         // custom events
31894
31895         /**
31896          * @event cellclick
31897          * Fires when a cell is clicked
31898          * @param {Grid} this
31899          * @param {Number} rowIndex
31900          * @param {Number} columnIndex
31901          * @param {Roo.EventObject} e
31902          */
31903         "cellclick" : true,
31904         /**
31905          * @event celldblclick
31906          * Fires when a cell is double clicked
31907          * @param {Grid} this
31908          * @param {Number} rowIndex
31909          * @param {Number} columnIndex
31910          * @param {Roo.EventObject} e
31911          */
31912         "celldblclick" : true,
31913         /**
31914          * @event rowclick
31915          * Fires when a row is clicked
31916          * @param {Grid} this
31917          * @param {Number} rowIndex
31918          * @param {Roo.EventObject} e
31919          */
31920         "rowclick" : true,
31921         /**
31922          * @event rowdblclick
31923          * Fires when a row is double clicked
31924          * @param {Grid} this
31925          * @param {Number} rowIndex
31926          * @param {Roo.EventObject} e
31927          */
31928         "rowdblclick" : true,
31929         /**
31930          * @event headerclick
31931          * Fires when a header is clicked
31932          * @param {Grid} this
31933          * @param {Number} columnIndex
31934          * @param {Roo.EventObject} e
31935          */
31936         "headerclick" : true,
31937         /**
31938          * @event headerdblclick
31939          * Fires when a header cell is double clicked
31940          * @param {Grid} this
31941          * @param {Number} columnIndex
31942          * @param {Roo.EventObject} e
31943          */
31944         "headerdblclick" : true,
31945         /**
31946          * @event rowcontextmenu
31947          * Fires when a row is right clicked
31948          * @param {Grid} this
31949          * @param {Number} rowIndex
31950          * @param {Roo.EventObject} e
31951          */
31952         "rowcontextmenu" : true,
31953         /**
31954          * @event cellcontextmenu
31955          * Fires when a cell is right clicked
31956          * @param {Grid} this
31957          * @param {Number} rowIndex
31958          * @param {Number} cellIndex
31959          * @param {Roo.EventObject} e
31960          */
31961          "cellcontextmenu" : true,
31962         /**
31963          * @event headercontextmenu
31964          * Fires when a header is right clicked
31965          * @param {Grid} this
31966          * @param {Number} columnIndex
31967          * @param {Roo.EventObject} e
31968          */
31969         "headercontextmenu" : true,
31970         /**
31971          * @event bodyscroll
31972          * Fires when the body element is scrolled
31973          * @param {Number} scrollLeft
31974          * @param {Number} scrollTop
31975          */
31976         "bodyscroll" : true,
31977         /**
31978          * @event columnresize
31979          * Fires when the user resizes a column
31980          * @param {Number} columnIndex
31981          * @param {Number} newSize
31982          */
31983         "columnresize" : true,
31984         /**
31985          * @event columnmove
31986          * Fires when the user moves a column
31987          * @param {Number} oldIndex
31988          * @param {Number} newIndex
31989          */
31990         "columnmove" : true,
31991         /**
31992          * @event startdrag
31993          * Fires when row(s) start being dragged
31994          * @param {Grid} this
31995          * @param {Roo.GridDD} dd The drag drop object
31996          * @param {event} e The raw browser event
31997          */
31998         "startdrag" : true,
31999         /**
32000          * @event enddrag
32001          * Fires when a drag operation is complete
32002          * @param {Grid} this
32003          * @param {Roo.GridDD} dd The drag drop object
32004          * @param {event} e The raw browser event
32005          */
32006         "enddrag" : true,
32007         /**
32008          * @event dragdrop
32009          * Fires when dragged row(s) are dropped on a valid DD target
32010          * @param {Grid} this
32011          * @param {Roo.GridDD} dd The drag drop object
32012          * @param {String} targetId The target drag drop object
32013          * @param {event} e The raw browser event
32014          */
32015         "dragdrop" : true,
32016         /**
32017          * @event dragover
32018          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32019          * @param {Grid} this
32020          * @param {Roo.GridDD} dd The drag drop object
32021          * @param {String} targetId The target drag drop object
32022          * @param {event} e The raw browser event
32023          */
32024         "dragover" : true,
32025         /**
32026          * @event dragenter
32027          *  Fires when the dragged row(s) first cross another DD target while being dragged
32028          * @param {Grid} this
32029          * @param {Roo.GridDD} dd The drag drop object
32030          * @param {String} targetId The target drag drop object
32031          * @param {event} e The raw browser event
32032          */
32033         "dragenter" : true,
32034         /**
32035          * @event dragout
32036          * Fires when the dragged row(s) leave another DD target while being dragged
32037          * @param {Grid} this
32038          * @param {Roo.GridDD} dd The drag drop object
32039          * @param {String} targetId The target drag drop object
32040          * @param {event} e The raw browser event
32041          */
32042         "dragout" : true,
32043         /**
32044          * @event rowclass
32045          * Fires when a row is rendered, so you can change add a style to it.
32046          * @param {GridView} gridview   The grid view
32047          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32048          */
32049         'rowclass' : true,
32050
32051         /**
32052          * @event render
32053          * Fires when the grid is rendered
32054          * @param {Grid} grid
32055          */
32056         'render' : true
32057     });
32058
32059     Roo.grid.Grid.superclass.constructor.call(this);
32060 };
32061 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32062     
32063     /**
32064      * @cfg {String} ddGroup - drag drop group.
32065      */
32066
32067     /**
32068      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32069      */
32070     minColumnWidth : 25,
32071
32072     /**
32073      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32074      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32075      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32076      */
32077     autoSizeColumns : false,
32078
32079     /**
32080      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32081      */
32082     autoSizeHeaders : true,
32083
32084     /**
32085      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32086      */
32087     monitorWindowResize : true,
32088
32089     /**
32090      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32091      * rows measured to get a columns size. Default is 0 (all rows).
32092      */
32093     maxRowsToMeasure : 0,
32094
32095     /**
32096      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32097      */
32098     trackMouseOver : true,
32099
32100     /**
32101     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32102     */
32103     
32104     /**
32105     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32106     */
32107     enableDragDrop : false,
32108     
32109     /**
32110     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32111     */
32112     enableColumnMove : true,
32113     
32114     /**
32115     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32116     */
32117     enableColumnHide : true,
32118     
32119     /**
32120     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32121     */
32122     enableRowHeightSync : false,
32123     
32124     /**
32125     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32126     */
32127     stripeRows : true,
32128     
32129     /**
32130     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32131     */
32132     autoHeight : false,
32133
32134     /**
32135      * @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.
32136      */
32137     autoExpandColumn : false,
32138
32139     /**
32140     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32141     * Default is 50.
32142     */
32143     autoExpandMin : 50,
32144
32145     /**
32146     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32147     */
32148     autoExpandMax : 1000,
32149
32150     /**
32151     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32152     */
32153     view : null,
32154
32155     /**
32156     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32157     */
32158     loadMask : false,
32159     /**
32160     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32161     */
32162     dropTarget: false,
32163     
32164    
32165     
32166     // private
32167     rendered : false,
32168
32169     /**
32170     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32171     * of a fixed width. Default is false.
32172     */
32173     /**
32174     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32175     */
32176     /**
32177      * Called once after all setup has been completed and the grid is ready to be rendered.
32178      * @return {Roo.grid.Grid} this
32179      */
32180     render : function()
32181     {
32182         var c = this.container;
32183         // try to detect autoHeight/width mode
32184         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32185             this.autoHeight = true;
32186         }
32187         var view = this.getView();
32188         view.init(this);
32189
32190         c.on("click", this.onClick, this);
32191         c.on("dblclick", this.onDblClick, this);
32192         c.on("contextmenu", this.onContextMenu, this);
32193         c.on("keydown", this.onKeyDown, this);
32194         if (Roo.isTouch) {
32195             c.on("touchstart", this.onTouchStart, this);
32196         }
32197
32198         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32199
32200         this.getSelectionModel().init(this);
32201
32202         view.render();
32203
32204         if(this.loadMask){
32205             this.loadMask = new Roo.LoadMask(this.container,
32206                     Roo.apply({store:this.dataSource}, this.loadMask));
32207         }
32208         
32209         
32210         if (this.toolbar && this.toolbar.xtype) {
32211             this.toolbar.container = this.getView().getHeaderPanel(true);
32212             this.toolbar = new Roo.Toolbar(this.toolbar);
32213         }
32214         if (this.footer && this.footer.xtype) {
32215             this.footer.dataSource = this.getDataSource();
32216             this.footer.container = this.getView().getFooterPanel(true);
32217             this.footer = Roo.factory(this.footer, Roo);
32218         }
32219         if (this.dropTarget && this.dropTarget.xtype) {
32220             delete this.dropTarget.xtype;
32221             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32222         }
32223         
32224         
32225         this.rendered = true;
32226         this.fireEvent('render', this);
32227         return this;
32228     },
32229
32230         /**
32231          * Reconfigures the grid to use a different Store and Column Model.
32232          * The View will be bound to the new objects and refreshed.
32233          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32234          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32235          */
32236     reconfigure : function(dataSource, colModel){
32237         if(this.loadMask){
32238             this.loadMask.destroy();
32239             this.loadMask = new Roo.LoadMask(this.container,
32240                     Roo.apply({store:dataSource}, this.loadMask));
32241         }
32242         this.view.bind(dataSource, colModel);
32243         this.dataSource = dataSource;
32244         this.colModel = colModel;
32245         this.view.refresh(true);
32246     },
32247
32248     // private
32249     onKeyDown : function(e){
32250         this.fireEvent("keydown", e);
32251     },
32252
32253     /**
32254      * Destroy this grid.
32255      * @param {Boolean} removeEl True to remove the element
32256      */
32257     destroy : function(removeEl, keepListeners){
32258         if(this.loadMask){
32259             this.loadMask.destroy();
32260         }
32261         var c = this.container;
32262         c.removeAllListeners();
32263         this.view.destroy();
32264         this.colModel.purgeListeners();
32265         if(!keepListeners){
32266             this.purgeListeners();
32267         }
32268         c.update("");
32269         if(removeEl === true){
32270             c.remove();
32271         }
32272     },
32273
32274     // private
32275     processEvent : function(name, e){
32276         // does this fire select???
32277         //Roo.log('grid:processEvent '  + name);
32278         
32279         if (name != 'touchstart' ) {
32280             this.fireEvent(name, e);    
32281         }
32282         
32283         var t = e.getTarget();
32284         var v = this.view;
32285         var header = v.findHeaderIndex(t);
32286         if(header !== false){
32287             var ename = name == 'touchstart' ? 'click' : name;
32288              
32289             this.fireEvent("header" + ename, this, header, e);
32290         }else{
32291             var row = v.findRowIndex(t);
32292             var cell = v.findCellIndex(t);
32293             if (name == 'touchstart') {
32294                 // first touch is always a click.
32295                 // hopefull this happens after selection is updated.?
32296                 name = false;
32297                 
32298                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32299                     var cs = this.selModel.getSelectedCell();
32300                     if (row == cs[0] && cell == cs[1]){
32301                         name = 'dblclick';
32302                     }
32303                 }
32304                 if (typeof(this.selModel.getSelections) != 'undefined') {
32305                     var cs = this.selModel.getSelections();
32306                     var ds = this.dataSource;
32307                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32308                         name = 'dblclick';
32309                     }
32310                 }
32311                 if (!name) {
32312                     return;
32313                 }
32314             }
32315             
32316             
32317             if(row !== false){
32318                 this.fireEvent("row" + name, this, row, e);
32319                 if(cell !== false){
32320                     this.fireEvent("cell" + name, this, row, cell, e);
32321                 }
32322             }
32323         }
32324     },
32325
32326     // private
32327     onClick : function(e){
32328         this.processEvent("click", e);
32329     },
32330    // private
32331     onTouchStart : function(e){
32332         this.processEvent("touchstart", e);
32333     },
32334
32335     // private
32336     onContextMenu : function(e, t){
32337         this.processEvent("contextmenu", e);
32338     },
32339
32340     // private
32341     onDblClick : function(e){
32342         this.processEvent("dblclick", e);
32343     },
32344
32345     // private
32346     walkCells : function(row, col, step, fn, scope){
32347         var cm = this.colModel, clen = cm.getColumnCount();
32348         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32349         if(step < 0){
32350             if(col < 0){
32351                 row--;
32352                 first = false;
32353             }
32354             while(row >= 0){
32355                 if(!first){
32356                     col = clen-1;
32357                 }
32358                 first = false;
32359                 while(col >= 0){
32360                     if(fn.call(scope || this, row, col, cm) === true){
32361                         return [row, col];
32362                     }
32363                     col--;
32364                 }
32365                 row--;
32366             }
32367         } else {
32368             if(col >= clen){
32369                 row++;
32370                 first = false;
32371             }
32372             while(row < rlen){
32373                 if(!first){
32374                     col = 0;
32375                 }
32376                 first = false;
32377                 while(col < clen){
32378                     if(fn.call(scope || this, row, col, cm) === true){
32379                         return [row, col];
32380                     }
32381                     col++;
32382                 }
32383                 row++;
32384             }
32385         }
32386         return null;
32387     },
32388
32389     // private
32390     getSelections : function(){
32391         return this.selModel.getSelections();
32392     },
32393
32394     /**
32395      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32396      * but if manual update is required this method will initiate it.
32397      */
32398     autoSize : function(){
32399         if(this.rendered){
32400             this.view.layout();
32401             if(this.view.adjustForScroll){
32402                 this.view.adjustForScroll();
32403             }
32404         }
32405     },
32406
32407     /**
32408      * Returns the grid's underlying element.
32409      * @return {Element} The element
32410      */
32411     getGridEl : function(){
32412         return this.container;
32413     },
32414
32415     // private for compatibility, overridden by editor grid
32416     stopEditing : function(){},
32417
32418     /**
32419      * Returns the grid's SelectionModel.
32420      * @return {SelectionModel}
32421      */
32422     getSelectionModel : function(){
32423         if(!this.selModel){
32424             this.selModel = new Roo.grid.RowSelectionModel();
32425         }
32426         return this.selModel;
32427     },
32428
32429     /**
32430      * Returns the grid's DataSource.
32431      * @return {DataSource}
32432      */
32433     getDataSource : function(){
32434         return this.dataSource;
32435     },
32436
32437     /**
32438      * Returns the grid's ColumnModel.
32439      * @return {ColumnModel}
32440      */
32441     getColumnModel : function(){
32442         return this.colModel;
32443     },
32444
32445     /**
32446      * Returns the grid's GridView object.
32447      * @return {GridView}
32448      */
32449     getView : function(){
32450         if(!this.view){
32451             this.view = new Roo.grid.GridView(this.viewConfig);
32452         }
32453         return this.view;
32454     },
32455     /**
32456      * Called to get grid's drag proxy text, by default returns this.ddText.
32457      * @return {String}
32458      */
32459     getDragDropText : function(){
32460         var count = this.selModel.getCount();
32461         return String.format(this.ddText, count, count == 1 ? '' : 's');
32462     }
32463 });
32464 /**
32465  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32466  * %0 is replaced with the number of selected rows.
32467  * @type String
32468  */
32469 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32470  * Based on:
32471  * Ext JS Library 1.1.1
32472  * Copyright(c) 2006-2007, Ext JS, LLC.
32473  *
32474  * Originally Released Under LGPL - original licence link has changed is not relivant.
32475  *
32476  * Fork - LGPL
32477  * <script type="text/javascript">
32478  */
32479  
32480 Roo.grid.AbstractGridView = function(){
32481         this.grid = null;
32482         
32483         this.events = {
32484             "beforerowremoved" : true,
32485             "beforerowsinserted" : true,
32486             "beforerefresh" : true,
32487             "rowremoved" : true,
32488             "rowsinserted" : true,
32489             "rowupdated" : true,
32490             "refresh" : true
32491         };
32492     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32493 };
32494
32495 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32496     rowClass : "x-grid-row",
32497     cellClass : "x-grid-cell",
32498     tdClass : "x-grid-td",
32499     hdClass : "x-grid-hd",
32500     splitClass : "x-grid-hd-split",
32501     
32502     init: function(grid){
32503         this.grid = grid;
32504                 var cid = this.grid.getGridEl().id;
32505         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32506         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32507         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32508         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32509         },
32510         
32511     getColumnRenderers : function(){
32512         var renderers = [];
32513         var cm = this.grid.colModel;
32514         var colCount = cm.getColumnCount();
32515         for(var i = 0; i < colCount; i++){
32516             renderers[i] = cm.getRenderer(i);
32517         }
32518         return renderers;
32519     },
32520     
32521     getColumnIds : function(){
32522         var ids = [];
32523         var cm = this.grid.colModel;
32524         var colCount = cm.getColumnCount();
32525         for(var i = 0; i < colCount; i++){
32526             ids[i] = cm.getColumnId(i);
32527         }
32528         return ids;
32529     },
32530     
32531     getDataIndexes : function(){
32532         if(!this.indexMap){
32533             this.indexMap = this.buildIndexMap();
32534         }
32535         return this.indexMap.colToData;
32536     },
32537     
32538     getColumnIndexByDataIndex : function(dataIndex){
32539         if(!this.indexMap){
32540             this.indexMap = this.buildIndexMap();
32541         }
32542         return this.indexMap.dataToCol[dataIndex];
32543     },
32544     
32545     /**
32546      * Set a css style for a column dynamically. 
32547      * @param {Number} colIndex The index of the column
32548      * @param {String} name The css property name
32549      * @param {String} value The css value
32550      */
32551     setCSSStyle : function(colIndex, name, value){
32552         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32553         Roo.util.CSS.updateRule(selector, name, value);
32554     },
32555     
32556     generateRules : function(cm){
32557         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32558         Roo.util.CSS.removeStyleSheet(rulesId);
32559         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32560             var cid = cm.getColumnId(i);
32561             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32562                          this.tdSelector, cid, " {\n}\n",
32563                          this.hdSelector, cid, " {\n}\n",
32564                          this.splitSelector, cid, " {\n}\n");
32565         }
32566         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32567     }
32568 });/*
32569  * Based on:
32570  * Ext JS Library 1.1.1
32571  * Copyright(c) 2006-2007, Ext JS, LLC.
32572  *
32573  * Originally Released Under LGPL - original licence link has changed is not relivant.
32574  *
32575  * Fork - LGPL
32576  * <script type="text/javascript">
32577  */
32578
32579 // private
32580 // This is a support class used internally by the Grid components
32581 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32582     this.grid = grid;
32583     this.view = grid.getView();
32584     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32585     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32586     if(hd2){
32587         this.setHandleElId(Roo.id(hd));
32588         this.setOuterHandleElId(Roo.id(hd2));
32589     }
32590     this.scroll = false;
32591 };
32592 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32593     maxDragWidth: 120,
32594     getDragData : function(e){
32595         var t = Roo.lib.Event.getTarget(e);
32596         var h = this.view.findHeaderCell(t);
32597         if(h){
32598             return {ddel: h.firstChild, header:h};
32599         }
32600         return false;
32601     },
32602
32603     onInitDrag : function(e){
32604         this.view.headersDisabled = true;
32605         var clone = this.dragData.ddel.cloneNode(true);
32606         clone.id = Roo.id();
32607         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32608         this.proxy.update(clone);
32609         return true;
32610     },
32611
32612     afterValidDrop : function(){
32613         var v = this.view;
32614         setTimeout(function(){
32615             v.headersDisabled = false;
32616         }, 50);
32617     },
32618
32619     afterInvalidDrop : function(){
32620         var v = this.view;
32621         setTimeout(function(){
32622             v.headersDisabled = false;
32623         }, 50);
32624     }
32625 });
32626 /*
32627  * Based on:
32628  * Ext JS Library 1.1.1
32629  * Copyright(c) 2006-2007, Ext JS, LLC.
32630  *
32631  * Originally Released Under LGPL - original licence link has changed is not relivant.
32632  *
32633  * Fork - LGPL
32634  * <script type="text/javascript">
32635  */
32636 // private
32637 // This is a support class used internally by the Grid components
32638 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32639     this.grid = grid;
32640     this.view = grid.getView();
32641     // split the proxies so they don't interfere with mouse events
32642     this.proxyTop = Roo.DomHelper.append(document.body, {
32643         cls:"col-move-top", html:"&#160;"
32644     }, true);
32645     this.proxyBottom = Roo.DomHelper.append(document.body, {
32646         cls:"col-move-bottom", html:"&#160;"
32647     }, true);
32648     this.proxyTop.hide = this.proxyBottom.hide = function(){
32649         this.setLeftTop(-100,-100);
32650         this.setStyle("visibility", "hidden");
32651     };
32652     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32653     // temporarily disabled
32654     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32655     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32656 };
32657 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32658     proxyOffsets : [-4, -9],
32659     fly: Roo.Element.fly,
32660
32661     getTargetFromEvent : function(e){
32662         var t = Roo.lib.Event.getTarget(e);
32663         var cindex = this.view.findCellIndex(t);
32664         if(cindex !== false){
32665             return this.view.getHeaderCell(cindex);
32666         }
32667         return null;
32668     },
32669
32670     nextVisible : function(h){
32671         var v = this.view, cm = this.grid.colModel;
32672         h = h.nextSibling;
32673         while(h){
32674             if(!cm.isHidden(v.getCellIndex(h))){
32675                 return h;
32676             }
32677             h = h.nextSibling;
32678         }
32679         return null;
32680     },
32681
32682     prevVisible : function(h){
32683         var v = this.view, cm = this.grid.colModel;
32684         h = h.prevSibling;
32685         while(h){
32686             if(!cm.isHidden(v.getCellIndex(h))){
32687                 return h;
32688             }
32689             h = h.prevSibling;
32690         }
32691         return null;
32692     },
32693
32694     positionIndicator : function(h, n, e){
32695         var x = Roo.lib.Event.getPageX(e);
32696         var r = Roo.lib.Dom.getRegion(n.firstChild);
32697         var px, pt, py = r.top + this.proxyOffsets[1];
32698         if((r.right - x) <= (r.right-r.left)/2){
32699             px = r.right+this.view.borderWidth;
32700             pt = "after";
32701         }else{
32702             px = r.left;
32703             pt = "before";
32704         }
32705         var oldIndex = this.view.getCellIndex(h);
32706         var newIndex = this.view.getCellIndex(n);
32707
32708         if(this.grid.colModel.isFixed(newIndex)){
32709             return false;
32710         }
32711
32712         var locked = this.grid.colModel.isLocked(newIndex);
32713
32714         if(pt == "after"){
32715             newIndex++;
32716         }
32717         if(oldIndex < newIndex){
32718             newIndex--;
32719         }
32720         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32721             return false;
32722         }
32723         px +=  this.proxyOffsets[0];
32724         this.proxyTop.setLeftTop(px, py);
32725         this.proxyTop.show();
32726         if(!this.bottomOffset){
32727             this.bottomOffset = this.view.mainHd.getHeight();
32728         }
32729         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32730         this.proxyBottom.show();
32731         return pt;
32732     },
32733
32734     onNodeEnter : function(n, dd, e, data){
32735         if(data.header != n){
32736             this.positionIndicator(data.header, n, e);
32737         }
32738     },
32739
32740     onNodeOver : function(n, dd, e, data){
32741         var result = false;
32742         if(data.header != n){
32743             result = this.positionIndicator(data.header, n, e);
32744         }
32745         if(!result){
32746             this.proxyTop.hide();
32747             this.proxyBottom.hide();
32748         }
32749         return result ? this.dropAllowed : this.dropNotAllowed;
32750     },
32751
32752     onNodeOut : function(n, dd, e, data){
32753         this.proxyTop.hide();
32754         this.proxyBottom.hide();
32755     },
32756
32757     onNodeDrop : function(n, dd, e, data){
32758         var h = data.header;
32759         if(h != n){
32760             var cm = this.grid.colModel;
32761             var x = Roo.lib.Event.getPageX(e);
32762             var r = Roo.lib.Dom.getRegion(n.firstChild);
32763             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32764             var oldIndex = this.view.getCellIndex(h);
32765             var newIndex = this.view.getCellIndex(n);
32766             var locked = cm.isLocked(newIndex);
32767             if(pt == "after"){
32768                 newIndex++;
32769             }
32770             if(oldIndex < newIndex){
32771                 newIndex--;
32772             }
32773             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32774                 return false;
32775             }
32776             cm.setLocked(oldIndex, locked, true);
32777             cm.moveColumn(oldIndex, newIndex);
32778             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32779             return true;
32780         }
32781         return false;
32782     }
32783 });
32784 /*
32785  * Based on:
32786  * Ext JS Library 1.1.1
32787  * Copyright(c) 2006-2007, Ext JS, LLC.
32788  *
32789  * Originally Released Under LGPL - original licence link has changed is not relivant.
32790  *
32791  * Fork - LGPL
32792  * <script type="text/javascript">
32793  */
32794   
32795 /**
32796  * @class Roo.grid.GridView
32797  * @extends Roo.util.Observable
32798  *
32799  * @constructor
32800  * @param {Object} config
32801  */
32802 Roo.grid.GridView = function(config){
32803     Roo.grid.GridView.superclass.constructor.call(this);
32804     this.el = null;
32805
32806     Roo.apply(this, config);
32807 };
32808
32809 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32810
32811     unselectable :  'unselectable="on"',
32812     unselectableCls :  'x-unselectable',
32813     
32814     
32815     rowClass : "x-grid-row",
32816
32817     cellClass : "x-grid-col",
32818
32819     tdClass : "x-grid-td",
32820
32821     hdClass : "x-grid-hd",
32822
32823     splitClass : "x-grid-split",
32824
32825     sortClasses : ["sort-asc", "sort-desc"],
32826
32827     enableMoveAnim : false,
32828
32829     hlColor: "C3DAF9",
32830
32831     dh : Roo.DomHelper,
32832
32833     fly : Roo.Element.fly,
32834
32835     css : Roo.util.CSS,
32836
32837     borderWidth: 1,
32838
32839     splitOffset: 3,
32840
32841     scrollIncrement : 22,
32842
32843     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32844
32845     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32846
32847     bind : function(ds, cm){
32848         if(this.ds){
32849             this.ds.un("load", this.onLoad, this);
32850             this.ds.un("datachanged", this.onDataChange, this);
32851             this.ds.un("add", this.onAdd, this);
32852             this.ds.un("remove", this.onRemove, this);
32853             this.ds.un("update", this.onUpdate, this);
32854             this.ds.un("clear", this.onClear, this);
32855         }
32856         if(ds){
32857             ds.on("load", this.onLoad, this);
32858             ds.on("datachanged", this.onDataChange, this);
32859             ds.on("add", this.onAdd, this);
32860             ds.on("remove", this.onRemove, this);
32861             ds.on("update", this.onUpdate, this);
32862             ds.on("clear", this.onClear, this);
32863         }
32864         this.ds = ds;
32865
32866         if(this.cm){
32867             this.cm.un("widthchange", this.onColWidthChange, this);
32868             this.cm.un("headerchange", this.onHeaderChange, this);
32869             this.cm.un("hiddenchange", this.onHiddenChange, this);
32870             this.cm.un("columnmoved", this.onColumnMove, this);
32871             this.cm.un("columnlockchange", this.onColumnLock, this);
32872         }
32873         if(cm){
32874             this.generateRules(cm);
32875             cm.on("widthchange", this.onColWidthChange, this);
32876             cm.on("headerchange", this.onHeaderChange, this);
32877             cm.on("hiddenchange", this.onHiddenChange, this);
32878             cm.on("columnmoved", this.onColumnMove, this);
32879             cm.on("columnlockchange", this.onColumnLock, this);
32880         }
32881         this.cm = cm;
32882     },
32883
32884     init: function(grid){
32885         Roo.grid.GridView.superclass.init.call(this, grid);
32886
32887         this.bind(grid.dataSource, grid.colModel);
32888
32889         grid.on("headerclick", this.handleHeaderClick, this);
32890
32891         if(grid.trackMouseOver){
32892             grid.on("mouseover", this.onRowOver, this);
32893             grid.on("mouseout", this.onRowOut, this);
32894         }
32895         grid.cancelTextSelection = function(){};
32896         this.gridId = grid.id;
32897
32898         var tpls = this.templates || {};
32899
32900         if(!tpls.master){
32901             tpls.master = new Roo.Template(
32902                '<div class="x-grid" hidefocus="true">',
32903                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32904                   '<div class="x-grid-topbar"></div>',
32905                   '<div class="x-grid-scroller"><div></div></div>',
32906                   '<div class="x-grid-locked">',
32907                       '<div class="x-grid-header">{lockedHeader}</div>',
32908                       '<div class="x-grid-body">{lockedBody}</div>',
32909                   "</div>",
32910                   '<div class="x-grid-viewport">',
32911                       '<div class="x-grid-header">{header}</div>',
32912                       '<div class="x-grid-body">{body}</div>',
32913                   "</div>",
32914                   '<div class="x-grid-bottombar"></div>',
32915                  
32916                   '<div class="x-grid-resize-proxy">&#160;</div>',
32917                "</div>"
32918             );
32919             tpls.master.disableformats = true;
32920         }
32921
32922         if(!tpls.header){
32923             tpls.header = new Roo.Template(
32924                '<table border="0" cellspacing="0" cellpadding="0">',
32925                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32926                "</table>{splits}"
32927             );
32928             tpls.header.disableformats = true;
32929         }
32930         tpls.header.compile();
32931
32932         if(!tpls.hcell){
32933             tpls.hcell = new Roo.Template(
32934                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32935                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32936                 "</div></td>"
32937              );
32938              tpls.hcell.disableFormats = true;
32939         }
32940         tpls.hcell.compile();
32941
32942         if(!tpls.hsplit){
32943             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
32944                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
32945             tpls.hsplit.disableFormats = true;
32946         }
32947         tpls.hsplit.compile();
32948
32949         if(!tpls.body){
32950             tpls.body = new Roo.Template(
32951                '<table border="0" cellspacing="0" cellpadding="0">',
32952                "<tbody>{rows}</tbody>",
32953                "</table>"
32954             );
32955             tpls.body.disableFormats = true;
32956         }
32957         tpls.body.compile();
32958
32959         if(!tpls.row){
32960             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32961             tpls.row.disableFormats = true;
32962         }
32963         tpls.row.compile();
32964
32965         if(!tpls.cell){
32966             tpls.cell = new Roo.Template(
32967                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32968                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
32969                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
32970                 "</td>"
32971             );
32972             tpls.cell.disableFormats = true;
32973         }
32974         tpls.cell.compile();
32975
32976         this.templates = tpls;
32977     },
32978
32979     // remap these for backwards compat
32980     onColWidthChange : function(){
32981         this.updateColumns.apply(this, arguments);
32982     },
32983     onHeaderChange : function(){
32984         this.updateHeaders.apply(this, arguments);
32985     }, 
32986     onHiddenChange : function(){
32987         this.handleHiddenChange.apply(this, arguments);
32988     },
32989     onColumnMove : function(){
32990         this.handleColumnMove.apply(this, arguments);
32991     },
32992     onColumnLock : function(){
32993         this.handleLockChange.apply(this, arguments);
32994     },
32995
32996     onDataChange : function(){
32997         this.refresh();
32998         this.updateHeaderSortState();
32999     },
33000
33001     onClear : function(){
33002         this.refresh();
33003     },
33004
33005     onUpdate : function(ds, record){
33006         this.refreshRow(record);
33007     },
33008
33009     refreshRow : function(record){
33010         var ds = this.ds, index;
33011         if(typeof record == 'number'){
33012             index = record;
33013             record = ds.getAt(index);
33014         }else{
33015             index = ds.indexOf(record);
33016         }
33017         this.insertRows(ds, index, index, true);
33018         this.onRemove(ds, record, index+1, true);
33019         this.syncRowHeights(index, index);
33020         this.layout();
33021         this.fireEvent("rowupdated", this, index, record);
33022     },
33023
33024     onAdd : function(ds, records, index){
33025         this.insertRows(ds, index, index + (records.length-1));
33026     },
33027
33028     onRemove : function(ds, record, index, isUpdate){
33029         if(isUpdate !== true){
33030             this.fireEvent("beforerowremoved", this, index, record);
33031         }
33032         var bt = this.getBodyTable(), lt = this.getLockedTable();
33033         if(bt.rows[index]){
33034             bt.firstChild.removeChild(bt.rows[index]);
33035         }
33036         if(lt.rows[index]){
33037             lt.firstChild.removeChild(lt.rows[index]);
33038         }
33039         if(isUpdate !== true){
33040             this.stripeRows(index);
33041             this.syncRowHeights(index, index);
33042             this.layout();
33043             this.fireEvent("rowremoved", this, index, record);
33044         }
33045     },
33046
33047     onLoad : function(){
33048         this.scrollToTop();
33049     },
33050
33051     /**
33052      * Scrolls the grid to the top
33053      */
33054     scrollToTop : function(){
33055         if(this.scroller){
33056             this.scroller.dom.scrollTop = 0;
33057             this.syncScroll();
33058         }
33059     },
33060
33061     /**
33062      * Gets a panel in the header of the grid that can be used for toolbars etc.
33063      * After modifying the contents of this panel a call to grid.autoSize() may be
33064      * required to register any changes in size.
33065      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33066      * @return Roo.Element
33067      */
33068     getHeaderPanel : function(doShow){
33069         if(doShow){
33070             this.headerPanel.show();
33071         }
33072         return this.headerPanel;
33073     },
33074
33075     /**
33076      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33077      * After modifying the contents of this panel a call to grid.autoSize() may be
33078      * required to register any changes in size.
33079      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33080      * @return Roo.Element
33081      */
33082     getFooterPanel : function(doShow){
33083         if(doShow){
33084             this.footerPanel.show();
33085         }
33086         return this.footerPanel;
33087     },
33088
33089     initElements : function(){
33090         var E = Roo.Element;
33091         var el = this.grid.getGridEl().dom.firstChild;
33092         var cs = el.childNodes;
33093
33094         this.el = new E(el);
33095         
33096          this.focusEl = new E(el.firstChild);
33097         this.focusEl.swallowEvent("click", true);
33098         
33099         this.headerPanel = new E(cs[1]);
33100         this.headerPanel.enableDisplayMode("block");
33101
33102         this.scroller = new E(cs[2]);
33103         this.scrollSizer = new E(this.scroller.dom.firstChild);
33104
33105         this.lockedWrap = new E(cs[3]);
33106         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33107         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33108
33109         this.mainWrap = new E(cs[4]);
33110         this.mainHd = new E(this.mainWrap.dom.firstChild);
33111         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33112
33113         this.footerPanel = new E(cs[5]);
33114         this.footerPanel.enableDisplayMode("block");
33115
33116         this.resizeProxy = new E(cs[6]);
33117
33118         this.headerSelector = String.format(
33119            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33120            this.lockedHd.id, this.mainHd.id
33121         );
33122
33123         this.splitterSelector = String.format(
33124            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33125            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33126         );
33127     },
33128     idToCssName : function(s)
33129     {
33130         return s.replace(/[^a-z0-9]+/ig, '-');
33131     },
33132
33133     getHeaderCell : function(index){
33134         return Roo.DomQuery.select(this.headerSelector)[index];
33135     },
33136
33137     getHeaderCellMeasure : function(index){
33138         return this.getHeaderCell(index).firstChild;
33139     },
33140
33141     getHeaderCellText : function(index){
33142         return this.getHeaderCell(index).firstChild.firstChild;
33143     },
33144
33145     getLockedTable : function(){
33146         return this.lockedBody.dom.firstChild;
33147     },
33148
33149     getBodyTable : function(){
33150         return this.mainBody.dom.firstChild;
33151     },
33152
33153     getLockedRow : function(index){
33154         return this.getLockedTable().rows[index];
33155     },
33156
33157     getRow : function(index){
33158         return this.getBodyTable().rows[index];
33159     },
33160
33161     getRowComposite : function(index){
33162         if(!this.rowEl){
33163             this.rowEl = new Roo.CompositeElementLite();
33164         }
33165         var els = [], lrow, mrow;
33166         if(lrow = this.getLockedRow(index)){
33167             els.push(lrow);
33168         }
33169         if(mrow = this.getRow(index)){
33170             els.push(mrow);
33171         }
33172         this.rowEl.elements = els;
33173         return this.rowEl;
33174     },
33175     /**
33176      * Gets the 'td' of the cell
33177      * 
33178      * @param {Integer} rowIndex row to select
33179      * @param {Integer} colIndex column to select
33180      * 
33181      * @return {Object} 
33182      */
33183     getCell : function(rowIndex, colIndex){
33184         var locked = this.cm.getLockedCount();
33185         var source;
33186         if(colIndex < locked){
33187             source = this.lockedBody.dom.firstChild;
33188         }else{
33189             source = this.mainBody.dom.firstChild;
33190             colIndex -= locked;
33191         }
33192         return source.rows[rowIndex].childNodes[colIndex];
33193     },
33194
33195     getCellText : function(rowIndex, colIndex){
33196         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33197     },
33198
33199     getCellBox : function(cell){
33200         var b = this.fly(cell).getBox();
33201         if(Roo.isOpera){ // opera fails to report the Y
33202             b.y = cell.offsetTop + this.mainBody.getY();
33203         }
33204         return b;
33205     },
33206
33207     getCellIndex : function(cell){
33208         var id = String(cell.className).match(this.cellRE);
33209         if(id){
33210             return parseInt(id[1], 10);
33211         }
33212         return 0;
33213     },
33214
33215     findHeaderIndex : function(n){
33216         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33217         return r ? this.getCellIndex(r) : false;
33218     },
33219
33220     findHeaderCell : function(n){
33221         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33222         return r ? r : false;
33223     },
33224
33225     findRowIndex : function(n){
33226         if(!n){
33227             return false;
33228         }
33229         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33230         return r ? r.rowIndex : false;
33231     },
33232
33233     findCellIndex : function(node){
33234         var stop = this.el.dom;
33235         while(node && node != stop){
33236             if(this.findRE.test(node.className)){
33237                 return this.getCellIndex(node);
33238             }
33239             node = node.parentNode;
33240         }
33241         return false;
33242     },
33243
33244     getColumnId : function(index){
33245         return this.cm.getColumnId(index);
33246     },
33247
33248     getSplitters : function()
33249     {
33250         if(this.splitterSelector){
33251            return Roo.DomQuery.select(this.splitterSelector);
33252         }else{
33253             return null;
33254       }
33255     },
33256
33257     getSplitter : function(index){
33258         return this.getSplitters()[index];
33259     },
33260
33261     onRowOver : function(e, t){
33262         var row;
33263         if((row = this.findRowIndex(t)) !== false){
33264             this.getRowComposite(row).addClass("x-grid-row-over");
33265         }
33266     },
33267
33268     onRowOut : function(e, t){
33269         var row;
33270         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33271             this.getRowComposite(row).removeClass("x-grid-row-over");
33272         }
33273     },
33274
33275     renderHeaders : function(){
33276         var cm = this.cm;
33277         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33278         var cb = [], lb = [], sb = [], lsb = [], p = {};
33279         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33280             p.cellId = "x-grid-hd-0-" + i;
33281             p.splitId = "x-grid-csplit-0-" + i;
33282             p.id = cm.getColumnId(i);
33283             p.value = cm.getColumnHeader(i) || "";
33284             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33285             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33286             if(!cm.isLocked(i)){
33287                 cb[cb.length] = ct.apply(p);
33288                 sb[sb.length] = st.apply(p);
33289             }else{
33290                 lb[lb.length] = ct.apply(p);
33291                 lsb[lsb.length] = st.apply(p);
33292             }
33293         }
33294         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33295                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33296     },
33297
33298     updateHeaders : function(){
33299         var html = this.renderHeaders();
33300         this.lockedHd.update(html[0]);
33301         this.mainHd.update(html[1]);
33302     },
33303
33304     /**
33305      * Focuses the specified row.
33306      * @param {Number} row The row index
33307      */
33308     focusRow : function(row)
33309     {
33310         //Roo.log('GridView.focusRow');
33311         var x = this.scroller.dom.scrollLeft;
33312         this.focusCell(row, 0, false);
33313         this.scroller.dom.scrollLeft = x;
33314     },
33315
33316     /**
33317      * Focuses the specified cell.
33318      * @param {Number} row The row index
33319      * @param {Number} col The column index
33320      * @param {Boolean} hscroll false to disable horizontal scrolling
33321      */
33322     focusCell : function(row, col, hscroll)
33323     {
33324         //Roo.log('GridView.focusCell');
33325         var el = this.ensureVisible(row, col, hscroll);
33326         this.focusEl.alignTo(el, "tl-tl");
33327         if(Roo.isGecko){
33328             this.focusEl.focus();
33329         }else{
33330             this.focusEl.focus.defer(1, this.focusEl);
33331         }
33332     },
33333
33334     /**
33335      * Scrolls the specified cell into view
33336      * @param {Number} row The row index
33337      * @param {Number} col The column index
33338      * @param {Boolean} hscroll false to disable horizontal scrolling
33339      */
33340     ensureVisible : function(row, col, hscroll)
33341     {
33342         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33343         //return null; //disable for testing.
33344         if(typeof row != "number"){
33345             row = row.rowIndex;
33346         }
33347         if(row < 0 && row >= this.ds.getCount()){
33348             return  null;
33349         }
33350         col = (col !== undefined ? col : 0);
33351         var cm = this.grid.colModel;
33352         while(cm.isHidden(col)){
33353             col++;
33354         }
33355
33356         var el = this.getCell(row, col);
33357         if(!el){
33358             return null;
33359         }
33360         var c = this.scroller.dom;
33361
33362         var ctop = parseInt(el.offsetTop, 10);
33363         var cleft = parseInt(el.offsetLeft, 10);
33364         var cbot = ctop + el.offsetHeight;
33365         var cright = cleft + el.offsetWidth;
33366         
33367         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33368         var stop = parseInt(c.scrollTop, 10);
33369         var sleft = parseInt(c.scrollLeft, 10);
33370         var sbot = stop + ch;
33371         var sright = sleft + c.clientWidth;
33372         /*
33373         Roo.log('GridView.ensureVisible:' +
33374                 ' ctop:' + ctop +
33375                 ' c.clientHeight:' + c.clientHeight +
33376                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33377                 ' stop:' + stop +
33378                 ' cbot:' + cbot +
33379                 ' sbot:' + sbot +
33380                 ' ch:' + ch  
33381                 );
33382         */
33383         if(ctop < stop){
33384              c.scrollTop = ctop;
33385             //Roo.log("set scrolltop to ctop DISABLE?");
33386         }else if(cbot > sbot){
33387             //Roo.log("set scrolltop to cbot-ch");
33388             c.scrollTop = cbot-ch;
33389         }
33390         
33391         if(hscroll !== false){
33392             if(cleft < sleft){
33393                 c.scrollLeft = cleft;
33394             }else if(cright > sright){
33395                 c.scrollLeft = cright-c.clientWidth;
33396             }
33397         }
33398          
33399         return el;
33400     },
33401
33402     updateColumns : function(){
33403         this.grid.stopEditing();
33404         var cm = this.grid.colModel, colIds = this.getColumnIds();
33405         //var totalWidth = cm.getTotalWidth();
33406         var pos = 0;
33407         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33408             //if(cm.isHidden(i)) continue;
33409             var w = cm.getColumnWidth(i);
33410             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33411             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33412         }
33413         this.updateSplitters();
33414     },
33415
33416     generateRules : function(cm){
33417         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33418         Roo.util.CSS.removeStyleSheet(rulesId);
33419         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33420             var cid = cm.getColumnId(i);
33421             var align = '';
33422             if(cm.config[i].align){
33423                 align = 'text-align:'+cm.config[i].align+';';
33424             }
33425             var hidden = '';
33426             if(cm.isHidden(i)){
33427                 hidden = 'display:none;';
33428             }
33429             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33430             ruleBuf.push(
33431                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33432                     this.hdSelector, cid, " {\n", align, width, "}\n",
33433                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33434                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33435         }
33436         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33437     },
33438
33439     updateSplitters : function(){
33440         var cm = this.cm, s = this.getSplitters();
33441         if(s){ // splitters not created yet
33442             var pos = 0, locked = true;
33443             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33444                 if(cm.isHidden(i)) {
33445                     continue;
33446                 }
33447                 var w = cm.getColumnWidth(i); // make sure it's a number
33448                 if(!cm.isLocked(i) && locked){
33449                     pos = 0;
33450                     locked = false;
33451                 }
33452                 pos += w;
33453                 s[i].style.left = (pos-this.splitOffset) + "px";
33454             }
33455         }
33456     },
33457
33458     handleHiddenChange : function(colModel, colIndex, hidden){
33459         if(hidden){
33460             this.hideColumn(colIndex);
33461         }else{
33462             this.unhideColumn(colIndex);
33463         }
33464     },
33465
33466     hideColumn : function(colIndex){
33467         var cid = this.getColumnId(colIndex);
33468         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33469         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33470         if(Roo.isSafari){
33471             this.updateHeaders();
33472         }
33473         this.updateSplitters();
33474         this.layout();
33475     },
33476
33477     unhideColumn : function(colIndex){
33478         var cid = this.getColumnId(colIndex);
33479         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33480         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33481
33482         if(Roo.isSafari){
33483             this.updateHeaders();
33484         }
33485         this.updateSplitters();
33486         this.layout();
33487     },
33488
33489     insertRows : function(dm, firstRow, lastRow, isUpdate){
33490         if(firstRow == 0 && lastRow == dm.getCount()-1){
33491             this.refresh();
33492         }else{
33493             if(!isUpdate){
33494                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33495             }
33496             var s = this.getScrollState();
33497             var markup = this.renderRows(firstRow, lastRow);
33498             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33499             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33500             this.restoreScroll(s);
33501             if(!isUpdate){
33502                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33503                 this.syncRowHeights(firstRow, lastRow);
33504                 this.stripeRows(firstRow);
33505                 this.layout();
33506             }
33507         }
33508     },
33509
33510     bufferRows : function(markup, target, index){
33511         var before = null, trows = target.rows, tbody = target.tBodies[0];
33512         if(index < trows.length){
33513             before = trows[index];
33514         }
33515         var b = document.createElement("div");
33516         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33517         var rows = b.firstChild.rows;
33518         for(var i = 0, len = rows.length; i < len; i++){
33519             if(before){
33520                 tbody.insertBefore(rows[0], before);
33521             }else{
33522                 tbody.appendChild(rows[0]);
33523             }
33524         }
33525         b.innerHTML = "";
33526         b = null;
33527     },
33528
33529     deleteRows : function(dm, firstRow, lastRow){
33530         if(dm.getRowCount()<1){
33531             this.fireEvent("beforerefresh", this);
33532             this.mainBody.update("");
33533             this.lockedBody.update("");
33534             this.fireEvent("refresh", this);
33535         }else{
33536             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33537             var bt = this.getBodyTable();
33538             var tbody = bt.firstChild;
33539             var rows = bt.rows;
33540             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33541                 tbody.removeChild(rows[firstRow]);
33542             }
33543             this.stripeRows(firstRow);
33544             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33545         }
33546     },
33547
33548     updateRows : function(dataSource, firstRow, lastRow){
33549         var s = this.getScrollState();
33550         this.refresh();
33551         this.restoreScroll(s);
33552     },
33553
33554     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33555         if(!noRefresh){
33556            this.refresh();
33557         }
33558         this.updateHeaderSortState();
33559     },
33560
33561     getScrollState : function(){
33562         
33563         var sb = this.scroller.dom;
33564         return {left: sb.scrollLeft, top: sb.scrollTop};
33565     },
33566
33567     stripeRows : function(startRow){
33568         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33569             return;
33570         }
33571         startRow = startRow || 0;
33572         var rows = this.getBodyTable().rows;
33573         var lrows = this.getLockedTable().rows;
33574         var cls = ' x-grid-row-alt ';
33575         for(var i = startRow, len = rows.length; i < len; i++){
33576             var row = rows[i], lrow = lrows[i];
33577             var isAlt = ((i+1) % 2 == 0);
33578             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33579             if(isAlt == hasAlt){
33580                 continue;
33581             }
33582             if(isAlt){
33583                 row.className += " x-grid-row-alt";
33584             }else{
33585                 row.className = row.className.replace("x-grid-row-alt", "");
33586             }
33587             if(lrow){
33588                 lrow.className = row.className;
33589             }
33590         }
33591     },
33592
33593     restoreScroll : function(state){
33594         //Roo.log('GridView.restoreScroll');
33595         var sb = this.scroller.dom;
33596         sb.scrollLeft = state.left;
33597         sb.scrollTop = state.top;
33598         this.syncScroll();
33599     },
33600
33601     syncScroll : function(){
33602         //Roo.log('GridView.syncScroll');
33603         var sb = this.scroller.dom;
33604         var sh = this.mainHd.dom;
33605         var bs = this.mainBody.dom;
33606         var lv = this.lockedBody.dom;
33607         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33608         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33609     },
33610
33611     handleScroll : function(e){
33612         this.syncScroll();
33613         var sb = this.scroller.dom;
33614         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33615         e.stopEvent();
33616     },
33617
33618     handleWheel : function(e){
33619         var d = e.getWheelDelta();
33620         this.scroller.dom.scrollTop -= d*22;
33621         // set this here to prevent jumpy scrolling on large tables
33622         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33623         e.stopEvent();
33624     },
33625
33626     renderRows : function(startRow, endRow){
33627         // pull in all the crap needed to render rows
33628         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33629         var colCount = cm.getColumnCount();
33630
33631         if(ds.getCount() < 1){
33632             return ["", ""];
33633         }
33634
33635         // build a map for all the columns
33636         var cs = [];
33637         for(var i = 0; i < colCount; i++){
33638             var name = cm.getDataIndex(i);
33639             cs[i] = {
33640                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33641                 renderer : cm.getRenderer(i),
33642                 id : cm.getColumnId(i),
33643                 locked : cm.isLocked(i),
33644                 has_editor : cm.isCellEditable(i)
33645             };
33646         }
33647
33648         startRow = startRow || 0;
33649         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33650
33651         // records to render
33652         var rs = ds.getRange(startRow, endRow);
33653
33654         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33655     },
33656
33657     // As much as I hate to duplicate code, this was branched because FireFox really hates
33658     // [].join("") on strings. The performance difference was substantial enough to
33659     // branch this function
33660     doRender : Roo.isGecko ?
33661             function(cs, rs, ds, startRow, colCount, stripe){
33662                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33663                 // buffers
33664                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33665                 
33666                 var hasListener = this.grid.hasListener('rowclass');
33667                 var rowcfg = {};
33668                 for(var j = 0, len = rs.length; j < len; j++){
33669                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33670                     for(var i = 0; i < colCount; i++){
33671                         c = cs[i];
33672                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33673                         p.id = c.id;
33674                         p.css = p.attr = "";
33675                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33676                         if(p.value == undefined || p.value === "") {
33677                             p.value = "&#160;";
33678                         }
33679                         if(c.has_editor){
33680                             p.css += ' x-grid-editable-cell';
33681                         }
33682                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33683                             p.css +=  ' x-grid-dirty-cell';
33684                         }
33685                         var markup = ct.apply(p);
33686                         if(!c.locked){
33687                             cb+= markup;
33688                         }else{
33689                             lcb+= markup;
33690                         }
33691                     }
33692                     var alt = [];
33693                     if(stripe && ((rowIndex+1) % 2 == 0)){
33694                         alt.push("x-grid-row-alt")
33695                     }
33696                     if(r.dirty){
33697                         alt.push(  " x-grid-dirty-row");
33698                     }
33699                     rp.cells = lcb;
33700                     if(this.getRowClass){
33701                         alt.push(this.getRowClass(r, rowIndex));
33702                     }
33703                     if (hasListener) {
33704                         rowcfg = {
33705                              
33706                             record: r,
33707                             rowIndex : rowIndex,
33708                             rowClass : ''
33709                         };
33710                         this.grid.fireEvent('rowclass', this, rowcfg);
33711                         alt.push(rowcfg.rowClass);
33712                     }
33713                     rp.alt = alt.join(" ");
33714                     lbuf+= rt.apply(rp);
33715                     rp.cells = cb;
33716                     buf+=  rt.apply(rp);
33717                 }
33718                 return [lbuf, buf];
33719             } :
33720             function(cs, rs, ds, startRow, colCount, stripe){
33721                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33722                 // buffers
33723                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33724                 var hasListener = this.grid.hasListener('rowclass');
33725  
33726                 var rowcfg = {};
33727                 for(var j = 0, len = rs.length; j < len; j++){
33728                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33729                     for(var i = 0; i < colCount; i++){
33730                         c = cs[i];
33731                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33732                         p.id = c.id;
33733                         p.css = p.attr = "";
33734                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33735                         if(p.value == undefined || p.value === "") {
33736                             p.value = "&#160;";
33737                         }
33738                         //Roo.log(c);
33739                          if(c.has_editor){
33740                             p.css += ' x-grid-editable-cell';
33741                         }
33742                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33743                             p.css += ' x-grid-dirty-cell' 
33744                         }
33745                         
33746                         var markup = ct.apply(p);
33747                         if(!c.locked){
33748                             cb[cb.length] = markup;
33749                         }else{
33750                             lcb[lcb.length] = markup;
33751                         }
33752                     }
33753                     var alt = [];
33754                     if(stripe && ((rowIndex+1) % 2 == 0)){
33755                         alt.push( "x-grid-row-alt");
33756                     }
33757                     if(r.dirty){
33758                         alt.push(" x-grid-dirty-row");
33759                     }
33760                     rp.cells = lcb;
33761                     if(this.getRowClass){
33762                         alt.push( this.getRowClass(r, rowIndex));
33763                     }
33764                     if (hasListener) {
33765                         rowcfg = {
33766                              
33767                             record: r,
33768                             rowIndex : rowIndex,
33769                             rowClass : ''
33770                         };
33771                         this.grid.fireEvent('rowclass', this, rowcfg);
33772                         alt.push(rowcfg.rowClass);
33773                     }
33774                     
33775                     rp.alt = alt.join(" ");
33776                     rp.cells = lcb.join("");
33777                     lbuf[lbuf.length] = rt.apply(rp);
33778                     rp.cells = cb.join("");
33779                     buf[buf.length] =  rt.apply(rp);
33780                 }
33781                 return [lbuf.join(""), buf.join("")];
33782             },
33783
33784     renderBody : function(){
33785         var markup = this.renderRows();
33786         var bt = this.templates.body;
33787         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33788     },
33789
33790     /**
33791      * Refreshes the grid
33792      * @param {Boolean} headersToo
33793      */
33794     refresh : function(headersToo){
33795         this.fireEvent("beforerefresh", this);
33796         this.grid.stopEditing();
33797         var result = this.renderBody();
33798         this.lockedBody.update(result[0]);
33799         this.mainBody.update(result[1]);
33800         if(headersToo === true){
33801             this.updateHeaders();
33802             this.updateColumns();
33803             this.updateSplitters();
33804             this.updateHeaderSortState();
33805         }
33806         this.syncRowHeights();
33807         this.layout();
33808         this.fireEvent("refresh", this);
33809     },
33810
33811     handleColumnMove : function(cm, oldIndex, newIndex){
33812         this.indexMap = null;
33813         var s = this.getScrollState();
33814         this.refresh(true);
33815         this.restoreScroll(s);
33816         this.afterMove(newIndex);
33817     },
33818
33819     afterMove : function(colIndex){
33820         if(this.enableMoveAnim && Roo.enableFx){
33821             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33822         }
33823         // if multisort - fix sortOrder, and reload..
33824         if (this.grid.dataSource.multiSort) {
33825             // the we can call sort again..
33826             var dm = this.grid.dataSource;
33827             var cm = this.grid.colModel;
33828             var so = [];
33829             for(var i = 0; i < cm.config.length; i++ ) {
33830                 
33831                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33832                     continue; // dont' bother, it's not in sort list or being set.
33833                 }
33834                 
33835                 so.push(cm.config[i].dataIndex);
33836             };
33837             dm.sortOrder = so;
33838             dm.load(dm.lastOptions);
33839             
33840             
33841         }
33842         
33843     },
33844
33845     updateCell : function(dm, rowIndex, dataIndex){
33846         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33847         if(typeof colIndex == "undefined"){ // not present in grid
33848             return;
33849         }
33850         var cm = this.grid.colModel;
33851         var cell = this.getCell(rowIndex, colIndex);
33852         var cellText = this.getCellText(rowIndex, colIndex);
33853
33854         var p = {
33855             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33856             id : cm.getColumnId(colIndex),
33857             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33858         };
33859         var renderer = cm.getRenderer(colIndex);
33860         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33861         if(typeof val == "undefined" || val === "") {
33862             val = "&#160;";
33863         }
33864         cellText.innerHTML = val;
33865         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33866         this.syncRowHeights(rowIndex, rowIndex);
33867     },
33868
33869     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33870         var maxWidth = 0;
33871         if(this.grid.autoSizeHeaders){
33872             var h = this.getHeaderCellMeasure(colIndex);
33873             maxWidth = Math.max(maxWidth, h.scrollWidth);
33874         }
33875         var tb, index;
33876         if(this.cm.isLocked(colIndex)){
33877             tb = this.getLockedTable();
33878             index = colIndex;
33879         }else{
33880             tb = this.getBodyTable();
33881             index = colIndex - this.cm.getLockedCount();
33882         }
33883         if(tb && tb.rows){
33884             var rows = tb.rows;
33885             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33886             for(var i = 0; i < stopIndex; i++){
33887                 var cell = rows[i].childNodes[index].firstChild;
33888                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33889             }
33890         }
33891         return maxWidth + /*margin for error in IE*/ 5;
33892     },
33893     /**
33894      * Autofit a column to its content.
33895      * @param {Number} colIndex
33896      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33897      */
33898      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33899          if(this.cm.isHidden(colIndex)){
33900              return; // can't calc a hidden column
33901          }
33902         if(forceMinSize){
33903             var cid = this.cm.getColumnId(colIndex);
33904             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33905            if(this.grid.autoSizeHeaders){
33906                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33907            }
33908         }
33909         var newWidth = this.calcColumnWidth(colIndex);
33910         this.cm.setColumnWidth(colIndex,
33911             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33912         if(!suppressEvent){
33913             this.grid.fireEvent("columnresize", colIndex, newWidth);
33914         }
33915     },
33916
33917     /**
33918      * Autofits all columns to their content and then expands to fit any extra space in the grid
33919      */
33920      autoSizeColumns : function(){
33921         var cm = this.grid.colModel;
33922         var colCount = cm.getColumnCount();
33923         for(var i = 0; i < colCount; i++){
33924             this.autoSizeColumn(i, true, true);
33925         }
33926         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33927             this.fitColumns();
33928         }else{
33929             this.updateColumns();
33930             this.layout();
33931         }
33932     },
33933
33934     /**
33935      * Autofits all columns to the grid's width proportionate with their current size
33936      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33937      */
33938     fitColumns : function(reserveScrollSpace){
33939         var cm = this.grid.colModel;
33940         var colCount = cm.getColumnCount();
33941         var cols = [];
33942         var width = 0;
33943         var i, w;
33944         for (i = 0; i < colCount; i++){
33945             if(!cm.isHidden(i) && !cm.isFixed(i)){
33946                 w = cm.getColumnWidth(i);
33947                 cols.push(i);
33948                 cols.push(w);
33949                 width += w;
33950             }
33951         }
33952         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33953         if(reserveScrollSpace){
33954             avail -= 17;
33955         }
33956         var frac = (avail - cm.getTotalWidth())/width;
33957         while (cols.length){
33958             w = cols.pop();
33959             i = cols.pop();
33960             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33961         }
33962         this.updateColumns();
33963         this.layout();
33964     },
33965
33966     onRowSelect : function(rowIndex){
33967         var row = this.getRowComposite(rowIndex);
33968         row.addClass("x-grid-row-selected");
33969     },
33970
33971     onRowDeselect : function(rowIndex){
33972         var row = this.getRowComposite(rowIndex);
33973         row.removeClass("x-grid-row-selected");
33974     },
33975
33976     onCellSelect : function(row, col){
33977         var cell = this.getCell(row, col);
33978         if(cell){
33979             Roo.fly(cell).addClass("x-grid-cell-selected");
33980         }
33981     },
33982
33983     onCellDeselect : function(row, col){
33984         var cell = this.getCell(row, col);
33985         if(cell){
33986             Roo.fly(cell).removeClass("x-grid-cell-selected");
33987         }
33988     },
33989
33990     updateHeaderSortState : function(){
33991         
33992         // sort state can be single { field: xxx, direction : yyy}
33993         // or   { xxx=>ASC , yyy : DESC ..... }
33994         
33995         var mstate = {};
33996         if (!this.ds.multiSort) { 
33997             var state = this.ds.getSortState();
33998             if(!state){
33999                 return;
34000             }
34001             mstate[state.field] = state.direction;
34002             // FIXME... - this is not used here.. but might be elsewhere..
34003             this.sortState = state;
34004             
34005         } else {
34006             mstate = this.ds.sortToggle;
34007         }
34008         //remove existing sort classes..
34009         
34010         var sc = this.sortClasses;
34011         var hds = this.el.select(this.headerSelector).removeClass(sc);
34012         
34013         for(var f in mstate) {
34014         
34015             var sortColumn = this.cm.findColumnIndex(f);
34016             
34017             if(sortColumn != -1){
34018                 var sortDir = mstate[f];        
34019                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34020             }
34021         }
34022         
34023          
34024         
34025     },
34026
34027
34028     handleHeaderClick : function(g, index,e){
34029         
34030         Roo.log("header click");
34031         
34032         if (Roo.isTouch) {
34033             // touch events on header are handled by context
34034             this.handleHdCtx(g,index,e);
34035             return;
34036         }
34037         
34038         
34039         if(this.headersDisabled){
34040             return;
34041         }
34042         var dm = g.dataSource, cm = g.colModel;
34043         if(!cm.isSortable(index)){
34044             return;
34045         }
34046         g.stopEditing();
34047         
34048         if (dm.multiSort) {
34049             // update the sortOrder
34050             var so = [];
34051             for(var i = 0; i < cm.config.length; i++ ) {
34052                 
34053                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34054                     continue; // dont' bother, it's not in sort list or being set.
34055                 }
34056                 
34057                 so.push(cm.config[i].dataIndex);
34058             };
34059             dm.sortOrder = so;
34060         }
34061         
34062         
34063         dm.sort(cm.getDataIndex(index));
34064     },
34065
34066
34067     destroy : function(){
34068         if(this.colMenu){
34069             this.colMenu.removeAll();
34070             Roo.menu.MenuMgr.unregister(this.colMenu);
34071             this.colMenu.getEl().remove();
34072             delete this.colMenu;
34073         }
34074         if(this.hmenu){
34075             this.hmenu.removeAll();
34076             Roo.menu.MenuMgr.unregister(this.hmenu);
34077             this.hmenu.getEl().remove();
34078             delete this.hmenu;
34079         }
34080         if(this.grid.enableColumnMove){
34081             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34082             if(dds){
34083                 for(var dd in dds){
34084                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34085                         var elid = dds[dd].dragElId;
34086                         dds[dd].unreg();
34087                         Roo.get(elid).remove();
34088                     } else if(dds[dd].config.isTarget){
34089                         dds[dd].proxyTop.remove();
34090                         dds[dd].proxyBottom.remove();
34091                         dds[dd].unreg();
34092                     }
34093                     if(Roo.dd.DDM.locationCache[dd]){
34094                         delete Roo.dd.DDM.locationCache[dd];
34095                     }
34096                 }
34097                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34098             }
34099         }
34100         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34101         this.bind(null, null);
34102         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34103     },
34104
34105     handleLockChange : function(){
34106         this.refresh(true);
34107     },
34108
34109     onDenyColumnLock : function(){
34110
34111     },
34112
34113     onDenyColumnHide : function(){
34114
34115     },
34116
34117     handleHdMenuClick : function(item){
34118         var index = this.hdCtxIndex;
34119         var cm = this.cm, ds = this.ds;
34120         switch(item.id){
34121             case "asc":
34122                 ds.sort(cm.getDataIndex(index), "ASC");
34123                 break;
34124             case "desc":
34125                 ds.sort(cm.getDataIndex(index), "DESC");
34126                 break;
34127             case "lock":
34128                 var lc = cm.getLockedCount();
34129                 if(cm.getColumnCount(true) <= lc+1){
34130                     this.onDenyColumnLock();
34131                     return;
34132                 }
34133                 if(lc != index){
34134                     cm.setLocked(index, true, true);
34135                     cm.moveColumn(index, lc);
34136                     this.grid.fireEvent("columnmove", index, lc);
34137                 }else{
34138                     cm.setLocked(index, true);
34139                 }
34140             break;
34141             case "unlock":
34142                 var lc = cm.getLockedCount();
34143                 if((lc-1) != index){
34144                     cm.setLocked(index, false, true);
34145                     cm.moveColumn(index, lc-1);
34146                     this.grid.fireEvent("columnmove", index, lc-1);
34147                 }else{
34148                     cm.setLocked(index, false);
34149                 }
34150             break;
34151             case 'wider': // used to expand cols on touch..
34152             case 'narrow':
34153                 var cw = cm.getColumnWidth(index);
34154                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34155                 cw = Math.max(0, cw);
34156                 cw = Math.min(cw,4000);
34157                 cm.setColumnWidth(index, cw);
34158                 break;
34159                 
34160             default:
34161                 index = cm.getIndexById(item.id.substr(4));
34162                 if(index != -1){
34163                     if(item.checked && cm.getColumnCount(true) <= 1){
34164                         this.onDenyColumnHide();
34165                         return false;
34166                     }
34167                     cm.setHidden(index, item.checked);
34168                 }
34169         }
34170         return true;
34171     },
34172
34173     beforeColMenuShow : function(){
34174         var cm = this.cm,  colCount = cm.getColumnCount();
34175         this.colMenu.removeAll();
34176         for(var i = 0; i < colCount; i++){
34177             this.colMenu.add(new Roo.menu.CheckItem({
34178                 id: "col-"+cm.getColumnId(i),
34179                 text: cm.getColumnHeader(i),
34180                 checked: !cm.isHidden(i),
34181                 hideOnClick:false
34182             }));
34183         }
34184     },
34185
34186     handleHdCtx : function(g, index, e){
34187         e.stopEvent();
34188         var hd = this.getHeaderCell(index);
34189         this.hdCtxIndex = index;
34190         var ms = this.hmenu.items, cm = this.cm;
34191         ms.get("asc").setDisabled(!cm.isSortable(index));
34192         ms.get("desc").setDisabled(!cm.isSortable(index));
34193         if(this.grid.enableColLock !== false){
34194             ms.get("lock").setDisabled(cm.isLocked(index));
34195             ms.get("unlock").setDisabled(!cm.isLocked(index));
34196         }
34197         this.hmenu.show(hd, "tl-bl");
34198     },
34199
34200     handleHdOver : function(e){
34201         var hd = this.findHeaderCell(e.getTarget());
34202         if(hd && !this.headersDisabled){
34203             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34204                this.fly(hd).addClass("x-grid-hd-over");
34205             }
34206         }
34207     },
34208
34209     handleHdOut : function(e){
34210         var hd = this.findHeaderCell(e.getTarget());
34211         if(hd){
34212             this.fly(hd).removeClass("x-grid-hd-over");
34213         }
34214     },
34215
34216     handleSplitDblClick : function(e, t){
34217         var i = this.getCellIndex(t);
34218         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34219             this.autoSizeColumn(i, true);
34220             this.layout();
34221         }
34222     },
34223
34224     render : function(){
34225
34226         var cm = this.cm;
34227         var colCount = cm.getColumnCount();
34228
34229         if(this.grid.monitorWindowResize === true){
34230             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34231         }
34232         var header = this.renderHeaders();
34233         var body = this.templates.body.apply({rows:""});
34234         var html = this.templates.master.apply({
34235             lockedBody: body,
34236             body: body,
34237             lockedHeader: header[0],
34238             header: header[1]
34239         });
34240
34241         //this.updateColumns();
34242
34243         this.grid.getGridEl().dom.innerHTML = html;
34244
34245         this.initElements();
34246         
34247         // a kludge to fix the random scolling effect in webkit
34248         this.el.on("scroll", function() {
34249             this.el.dom.scrollTop=0; // hopefully not recursive..
34250         },this);
34251
34252         this.scroller.on("scroll", this.handleScroll, this);
34253         this.lockedBody.on("mousewheel", this.handleWheel, this);
34254         this.mainBody.on("mousewheel", this.handleWheel, this);
34255
34256         this.mainHd.on("mouseover", this.handleHdOver, this);
34257         this.mainHd.on("mouseout", this.handleHdOut, this);
34258         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34259                 {delegate: "."+this.splitClass});
34260
34261         this.lockedHd.on("mouseover", this.handleHdOver, this);
34262         this.lockedHd.on("mouseout", this.handleHdOut, this);
34263         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34264                 {delegate: "."+this.splitClass});
34265
34266         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34267             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34268         }
34269
34270         this.updateSplitters();
34271
34272         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34273             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34274             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34275         }
34276
34277         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34278             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34279             this.hmenu.add(
34280                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34281                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34282             );
34283             if(this.grid.enableColLock !== false){
34284                 this.hmenu.add('-',
34285                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34286                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34287                 );
34288             }
34289             if (Roo.isTouch) {
34290                  this.hmenu.add('-',
34291                     {id:"wider", text: this.columnsWiderText},
34292                     {id:"narrow", text: this.columnsNarrowText }
34293                 );
34294                 
34295                  
34296             }
34297             
34298             if(this.grid.enableColumnHide !== false){
34299
34300                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34301                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34302                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34303
34304                 this.hmenu.add('-',
34305                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34306                 );
34307             }
34308             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34309
34310             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34311         }
34312
34313         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34314             this.dd = new Roo.grid.GridDragZone(this.grid, {
34315                 ddGroup : this.grid.ddGroup || 'GridDD'
34316             });
34317             
34318         }
34319
34320         /*
34321         for(var i = 0; i < colCount; i++){
34322             if(cm.isHidden(i)){
34323                 this.hideColumn(i);
34324             }
34325             if(cm.config[i].align){
34326                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34327                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34328             }
34329         }*/
34330         
34331         this.updateHeaderSortState();
34332
34333         this.beforeInitialResize();
34334         this.layout(true);
34335
34336         // two part rendering gives faster view to the user
34337         this.renderPhase2.defer(1, this);
34338     },
34339
34340     renderPhase2 : function(){
34341         // render the rows now
34342         this.refresh();
34343         if(this.grid.autoSizeColumns){
34344             this.autoSizeColumns();
34345         }
34346     },
34347
34348     beforeInitialResize : function(){
34349
34350     },
34351
34352     onColumnSplitterMoved : function(i, w){
34353         this.userResized = true;
34354         var cm = this.grid.colModel;
34355         cm.setColumnWidth(i, w, true);
34356         var cid = cm.getColumnId(i);
34357         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34358         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34359         this.updateSplitters();
34360         this.layout();
34361         this.grid.fireEvent("columnresize", i, w);
34362     },
34363
34364     syncRowHeights : function(startIndex, endIndex){
34365         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34366             startIndex = startIndex || 0;
34367             var mrows = this.getBodyTable().rows;
34368             var lrows = this.getLockedTable().rows;
34369             var len = mrows.length-1;
34370             endIndex = Math.min(endIndex || len, len);
34371             for(var i = startIndex; i <= endIndex; i++){
34372                 var m = mrows[i], l = lrows[i];
34373                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34374                 m.style.height = l.style.height = h + "px";
34375             }
34376         }
34377     },
34378
34379     layout : function(initialRender, is2ndPass){
34380         var g = this.grid;
34381         var auto = g.autoHeight;
34382         var scrollOffset = 16;
34383         var c = g.getGridEl(), cm = this.cm,
34384                 expandCol = g.autoExpandColumn,
34385                 gv = this;
34386         //c.beginMeasure();
34387
34388         if(!c.dom.offsetWidth){ // display:none?
34389             if(initialRender){
34390                 this.lockedWrap.show();
34391                 this.mainWrap.show();
34392             }
34393             return;
34394         }
34395
34396         var hasLock = this.cm.isLocked(0);
34397
34398         var tbh = this.headerPanel.getHeight();
34399         var bbh = this.footerPanel.getHeight();
34400
34401         if(auto){
34402             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34403             var newHeight = ch + c.getBorderWidth("tb");
34404             if(g.maxHeight){
34405                 newHeight = Math.min(g.maxHeight, newHeight);
34406             }
34407             c.setHeight(newHeight);
34408         }
34409
34410         if(g.autoWidth){
34411             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34412         }
34413
34414         var s = this.scroller;
34415
34416         var csize = c.getSize(true);
34417
34418         this.el.setSize(csize.width, csize.height);
34419
34420         this.headerPanel.setWidth(csize.width);
34421         this.footerPanel.setWidth(csize.width);
34422
34423         var hdHeight = this.mainHd.getHeight();
34424         var vw = csize.width;
34425         var vh = csize.height - (tbh + bbh);
34426
34427         s.setSize(vw, vh);
34428
34429         var bt = this.getBodyTable();
34430         
34431         if(cm.getLockedCount() == cm.config.length){
34432             bt = this.getLockedTable();
34433         }
34434         
34435         var ltWidth = hasLock ?
34436                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34437
34438         var scrollHeight = bt.offsetHeight;
34439         var scrollWidth = ltWidth + bt.offsetWidth;
34440         var vscroll = false, hscroll = false;
34441
34442         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34443
34444         var lw = this.lockedWrap, mw = this.mainWrap;
34445         var lb = this.lockedBody, mb = this.mainBody;
34446
34447         setTimeout(function(){
34448             var t = s.dom.offsetTop;
34449             var w = s.dom.clientWidth,
34450                 h = s.dom.clientHeight;
34451
34452             lw.setTop(t);
34453             lw.setSize(ltWidth, h);
34454
34455             mw.setLeftTop(ltWidth, t);
34456             mw.setSize(w-ltWidth, h);
34457
34458             lb.setHeight(h-hdHeight);
34459             mb.setHeight(h-hdHeight);
34460
34461             if(is2ndPass !== true && !gv.userResized && expandCol){
34462                 // high speed resize without full column calculation
34463                 
34464                 var ci = cm.getIndexById(expandCol);
34465                 if (ci < 0) {
34466                     ci = cm.findColumnIndex(expandCol);
34467                 }
34468                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34469                 var expandId = cm.getColumnId(ci);
34470                 var  tw = cm.getTotalWidth(false);
34471                 var currentWidth = cm.getColumnWidth(ci);
34472                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34473                 if(currentWidth != cw){
34474                     cm.setColumnWidth(ci, cw, true);
34475                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34476                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34477                     gv.updateSplitters();
34478                     gv.layout(false, true);
34479                 }
34480             }
34481
34482             if(initialRender){
34483                 lw.show();
34484                 mw.show();
34485             }
34486             //c.endMeasure();
34487         }, 10);
34488     },
34489
34490     onWindowResize : function(){
34491         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34492             return;
34493         }
34494         this.layout();
34495     },
34496
34497     appendFooter : function(parentEl){
34498         return null;
34499     },
34500
34501     sortAscText : "Sort Ascending",
34502     sortDescText : "Sort Descending",
34503     lockText : "Lock Column",
34504     unlockText : "Unlock Column",
34505     columnsText : "Columns",
34506  
34507     columnsWiderText : "Wider",
34508     columnsNarrowText : "Thinner"
34509 });
34510
34511
34512 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34513     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34514     this.proxy.el.addClass('x-grid3-col-dd');
34515 };
34516
34517 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34518     handleMouseDown : function(e){
34519
34520     },
34521
34522     callHandleMouseDown : function(e){
34523         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34524     }
34525 });
34526 /*
34527  * Based on:
34528  * Ext JS Library 1.1.1
34529  * Copyright(c) 2006-2007, Ext JS, LLC.
34530  *
34531  * Originally Released Under LGPL - original licence link has changed is not relivant.
34532  *
34533  * Fork - LGPL
34534  * <script type="text/javascript">
34535  */
34536  
34537 // private
34538 // This is a support class used internally by the Grid components
34539 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34540     this.grid = grid;
34541     this.view = grid.getView();
34542     this.proxy = this.view.resizeProxy;
34543     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34544         "gridSplitters" + this.grid.getGridEl().id, {
34545         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34546     });
34547     this.setHandleElId(Roo.id(hd));
34548     this.setOuterHandleElId(Roo.id(hd2));
34549     this.scroll = false;
34550 };
34551 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34552     fly: Roo.Element.fly,
34553
34554     b4StartDrag : function(x, y){
34555         this.view.headersDisabled = true;
34556         this.proxy.setHeight(this.view.mainWrap.getHeight());
34557         var w = this.cm.getColumnWidth(this.cellIndex);
34558         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34559         this.resetConstraints();
34560         this.setXConstraint(minw, 1000);
34561         this.setYConstraint(0, 0);
34562         this.minX = x - minw;
34563         this.maxX = x + 1000;
34564         this.startPos = x;
34565         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34566     },
34567
34568
34569     handleMouseDown : function(e){
34570         ev = Roo.EventObject.setEvent(e);
34571         var t = this.fly(ev.getTarget());
34572         if(t.hasClass("x-grid-split")){
34573             this.cellIndex = this.view.getCellIndex(t.dom);
34574             this.split = t.dom;
34575             this.cm = this.grid.colModel;
34576             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34577                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34578             }
34579         }
34580     },
34581
34582     endDrag : function(e){
34583         this.view.headersDisabled = false;
34584         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34585         var diff = endX - this.startPos;
34586         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34587     },
34588
34589     autoOffset : function(){
34590         this.setDelta(0,0);
34591     }
34592 });/*
34593  * Based on:
34594  * Ext JS Library 1.1.1
34595  * Copyright(c) 2006-2007, Ext JS, LLC.
34596  *
34597  * Originally Released Under LGPL - original licence link has changed is not relivant.
34598  *
34599  * Fork - LGPL
34600  * <script type="text/javascript">
34601  */
34602  
34603 // private
34604 // This is a support class used internally by the Grid components
34605 Roo.grid.GridDragZone = function(grid, config){
34606     this.view = grid.getView();
34607     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34608     if(this.view.lockedBody){
34609         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34610         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34611     }
34612     this.scroll = false;
34613     this.grid = grid;
34614     this.ddel = document.createElement('div');
34615     this.ddel.className = 'x-grid-dd-wrap';
34616 };
34617
34618 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34619     ddGroup : "GridDD",
34620
34621     getDragData : function(e){
34622         var t = Roo.lib.Event.getTarget(e);
34623         var rowIndex = this.view.findRowIndex(t);
34624         var sm = this.grid.selModel;
34625             
34626         //Roo.log(rowIndex);
34627         
34628         if (sm.getSelectedCell) {
34629             // cell selection..
34630             if (!sm.getSelectedCell()) {
34631                 return false;
34632             }
34633             if (rowIndex != sm.getSelectedCell()[0]) {
34634                 return false;
34635             }
34636         
34637         }
34638         
34639         if(rowIndex !== false){
34640             
34641             // if editorgrid.. 
34642             
34643             
34644             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34645                
34646             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34647               //  
34648             //}
34649             if (e.hasModifier()){
34650                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34651             }
34652             
34653             Roo.log("getDragData");
34654             
34655             return {
34656                 grid: this.grid,
34657                 ddel: this.ddel,
34658                 rowIndex: rowIndex,
34659                 selections:sm.getSelections ? sm.getSelections() : (
34660                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34661                 )
34662             };
34663         }
34664         return false;
34665     },
34666
34667     onInitDrag : function(e){
34668         var data = this.dragData;
34669         this.ddel.innerHTML = this.grid.getDragDropText();
34670         this.proxy.update(this.ddel);
34671         // fire start drag?
34672     },
34673
34674     afterRepair : function(){
34675         this.dragging = false;
34676     },
34677
34678     getRepairXY : function(e, data){
34679         return false;
34680     },
34681
34682     onEndDrag : function(data, e){
34683         // fire end drag?
34684     },
34685
34686     onValidDrop : function(dd, e, id){
34687         // fire drag drop?
34688         this.hideProxy();
34689     },
34690
34691     beforeInvalidDrop : function(e, id){
34692
34693     }
34694 });/*
34695  * Based on:
34696  * Ext JS Library 1.1.1
34697  * Copyright(c) 2006-2007, Ext JS, LLC.
34698  *
34699  * Originally Released Under LGPL - original licence link has changed is not relivant.
34700  *
34701  * Fork - LGPL
34702  * <script type="text/javascript">
34703  */
34704  
34705
34706 /**
34707  * @class Roo.grid.ColumnModel
34708  * @extends Roo.util.Observable
34709  * This is the default implementation of a ColumnModel used by the Grid. It defines
34710  * the columns in the grid.
34711  * <br>Usage:<br>
34712  <pre><code>
34713  var colModel = new Roo.grid.ColumnModel([
34714         {header: "Ticker", width: 60, sortable: true, locked: true},
34715         {header: "Company Name", width: 150, sortable: true},
34716         {header: "Market Cap.", width: 100, sortable: true},
34717         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34718         {header: "Employees", width: 100, sortable: true, resizable: false}
34719  ]);
34720  </code></pre>
34721  * <p>
34722  
34723  * The config options listed for this class are options which may appear in each
34724  * individual column definition.
34725  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34726  * @constructor
34727  * @param {Object} config An Array of column config objects. See this class's
34728  * config objects for details.
34729 */
34730 Roo.grid.ColumnModel = function(config){
34731         /**
34732      * The config passed into the constructor
34733      */
34734     this.config = config;
34735     this.lookup = {};
34736
34737     // if no id, create one
34738     // if the column does not have a dataIndex mapping,
34739     // map it to the order it is in the config
34740     for(var i = 0, len = config.length; i < len; i++){
34741         var c = config[i];
34742         if(typeof c.dataIndex == "undefined"){
34743             c.dataIndex = i;
34744         }
34745         if(typeof c.renderer == "string"){
34746             c.renderer = Roo.util.Format[c.renderer];
34747         }
34748         if(typeof c.id == "undefined"){
34749             c.id = Roo.id();
34750         }
34751         if(c.editor && c.editor.xtype){
34752             c.editor  = Roo.factory(c.editor, Roo.grid);
34753         }
34754         if(c.editor && c.editor.isFormField){
34755             c.editor = new Roo.grid.GridEditor(c.editor);
34756         }
34757         this.lookup[c.id] = c;
34758     }
34759
34760     /**
34761      * The width of columns which have no width specified (defaults to 100)
34762      * @type Number
34763      */
34764     this.defaultWidth = 100;
34765
34766     /**
34767      * Default sortable of columns which have no sortable specified (defaults to false)
34768      * @type Boolean
34769      */
34770     this.defaultSortable = false;
34771
34772     this.addEvents({
34773         /**
34774              * @event widthchange
34775              * Fires when the width of a column changes.
34776              * @param {ColumnModel} this
34777              * @param {Number} columnIndex The column index
34778              * @param {Number} newWidth The new width
34779              */
34780             "widthchange": true,
34781         /**
34782              * @event headerchange
34783              * Fires when the text of a header changes.
34784              * @param {ColumnModel} this
34785              * @param {Number} columnIndex The column index
34786              * @param {Number} newText The new header text
34787              */
34788             "headerchange": true,
34789         /**
34790              * @event hiddenchange
34791              * Fires when a column is hidden or "unhidden".
34792              * @param {ColumnModel} this
34793              * @param {Number} columnIndex The column index
34794              * @param {Boolean} hidden true if hidden, false otherwise
34795              */
34796             "hiddenchange": true,
34797             /**
34798          * @event columnmoved
34799          * Fires when a column is moved.
34800          * @param {ColumnModel} this
34801          * @param {Number} oldIndex
34802          * @param {Number} newIndex
34803          */
34804         "columnmoved" : true,
34805         /**
34806          * @event columlockchange
34807          * Fires when a column's locked state is changed
34808          * @param {ColumnModel} this
34809          * @param {Number} colIndex
34810          * @param {Boolean} locked true if locked
34811          */
34812         "columnlockchange" : true
34813     });
34814     Roo.grid.ColumnModel.superclass.constructor.call(this);
34815 };
34816 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34817     /**
34818      * @cfg {String} header The header text to display in the Grid view.
34819      */
34820     /**
34821      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34822      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34823      * specified, the column's index is used as an index into the Record's data Array.
34824      */
34825     /**
34826      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34827      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34828      */
34829     /**
34830      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34831      * Defaults to the value of the {@link #defaultSortable} property.
34832      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34833      */
34834     /**
34835      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34836      */
34837     /**
34838      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34839      */
34840     /**
34841      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34842      */
34843     /**
34844      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34845      */
34846     /**
34847      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34848      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34849      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
34850      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
34851      */
34852        /**
34853      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34854      */
34855     /**
34856      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34857      */
34858     /**
34859      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
34860      */
34861     /**
34862      * @cfg {String} cursor (Optional)
34863      */
34864     /**
34865      * @cfg {String} tooltip (Optional)
34866      */
34867     /**
34868      * @cfg {Number} xs (Optional)
34869      */
34870     /**
34871      * @cfg {Number} sm (Optional)
34872      */
34873     /**
34874      * @cfg {Number} md (Optional)
34875      */
34876     /**
34877      * @cfg {Number} lg (Optional)
34878      */
34879     /**
34880      * Returns the id of the column at the specified index.
34881      * @param {Number} index The column index
34882      * @return {String} the id
34883      */
34884     getColumnId : function(index){
34885         return this.config[index].id;
34886     },
34887
34888     /**
34889      * Returns the column for a specified id.
34890      * @param {String} id The column id
34891      * @return {Object} the column
34892      */
34893     getColumnById : function(id){
34894         return this.lookup[id];
34895     },
34896
34897     
34898     /**
34899      * Returns the column for a specified dataIndex.
34900      * @param {String} dataIndex The column dataIndex
34901      * @return {Object|Boolean} the column or false if not found
34902      */
34903     getColumnByDataIndex: function(dataIndex){
34904         var index = this.findColumnIndex(dataIndex);
34905         return index > -1 ? this.config[index] : false;
34906     },
34907     
34908     /**
34909      * Returns the index for a specified column id.
34910      * @param {String} id The column id
34911      * @return {Number} the index, or -1 if not found
34912      */
34913     getIndexById : function(id){
34914         for(var i = 0, len = this.config.length; i < len; i++){
34915             if(this.config[i].id == id){
34916                 return i;
34917             }
34918         }
34919         return -1;
34920     },
34921     
34922     /**
34923      * Returns the index for a specified column dataIndex.
34924      * @param {String} dataIndex The column dataIndex
34925      * @return {Number} the index, or -1 if not found
34926      */
34927     
34928     findColumnIndex : function(dataIndex){
34929         for(var i = 0, len = this.config.length; i < len; i++){
34930             if(this.config[i].dataIndex == dataIndex){
34931                 return i;
34932             }
34933         }
34934         return -1;
34935     },
34936     
34937     
34938     moveColumn : function(oldIndex, newIndex){
34939         var c = this.config[oldIndex];
34940         this.config.splice(oldIndex, 1);
34941         this.config.splice(newIndex, 0, c);
34942         this.dataMap = null;
34943         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34944     },
34945
34946     isLocked : function(colIndex){
34947         return this.config[colIndex].locked === true;
34948     },
34949
34950     setLocked : function(colIndex, value, suppressEvent){
34951         if(this.isLocked(colIndex) == value){
34952             return;
34953         }
34954         this.config[colIndex].locked = value;
34955         if(!suppressEvent){
34956             this.fireEvent("columnlockchange", this, colIndex, value);
34957         }
34958     },
34959
34960     getTotalLockedWidth : function(){
34961         var totalWidth = 0;
34962         for(var i = 0; i < this.config.length; i++){
34963             if(this.isLocked(i) && !this.isHidden(i)){
34964                 this.totalWidth += this.getColumnWidth(i);
34965             }
34966         }
34967         return totalWidth;
34968     },
34969
34970     getLockedCount : function(){
34971         for(var i = 0, len = this.config.length; i < len; i++){
34972             if(!this.isLocked(i)){
34973                 return i;
34974             }
34975         }
34976         
34977         return this.config.length;
34978     },
34979
34980     /**
34981      * Returns the number of columns.
34982      * @return {Number}
34983      */
34984     getColumnCount : function(visibleOnly){
34985         if(visibleOnly === true){
34986             var c = 0;
34987             for(var i = 0, len = this.config.length; i < len; i++){
34988                 if(!this.isHidden(i)){
34989                     c++;
34990                 }
34991             }
34992             return c;
34993         }
34994         return this.config.length;
34995     },
34996
34997     /**
34998      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34999      * @param {Function} fn
35000      * @param {Object} scope (optional)
35001      * @return {Array} result
35002      */
35003     getColumnsBy : function(fn, scope){
35004         var r = [];
35005         for(var i = 0, len = this.config.length; i < len; i++){
35006             var c = this.config[i];
35007             if(fn.call(scope||this, c, i) === true){
35008                 r[r.length] = c;
35009             }
35010         }
35011         return r;
35012     },
35013
35014     /**
35015      * Returns true if the specified column is sortable.
35016      * @param {Number} col The column index
35017      * @return {Boolean}
35018      */
35019     isSortable : function(col){
35020         if(typeof this.config[col].sortable == "undefined"){
35021             return this.defaultSortable;
35022         }
35023         return this.config[col].sortable;
35024     },
35025
35026     /**
35027      * Returns the rendering (formatting) function defined for the column.
35028      * @param {Number} col The column index.
35029      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35030      */
35031     getRenderer : function(col){
35032         if(!this.config[col].renderer){
35033             return Roo.grid.ColumnModel.defaultRenderer;
35034         }
35035         return this.config[col].renderer;
35036     },
35037
35038     /**
35039      * Sets the rendering (formatting) function for a column.
35040      * @param {Number} col The column index
35041      * @param {Function} fn The function to use to process the cell's raw data
35042      * to return HTML markup for the grid view. The render function is called with
35043      * the following parameters:<ul>
35044      * <li>Data value.</li>
35045      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35046      * <li>css A CSS style string to apply to the table cell.</li>
35047      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35048      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35049      * <li>Row index</li>
35050      * <li>Column index</li>
35051      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35052      */
35053     setRenderer : function(col, fn){
35054         this.config[col].renderer = fn;
35055     },
35056
35057     /**
35058      * Returns the width for the specified column.
35059      * @param {Number} col The column index
35060      * @return {Number}
35061      */
35062     getColumnWidth : function(col){
35063         return this.config[col].width * 1 || this.defaultWidth;
35064     },
35065
35066     /**
35067      * Sets the width for a column.
35068      * @param {Number} col The column index
35069      * @param {Number} width The new width
35070      */
35071     setColumnWidth : function(col, width, suppressEvent){
35072         this.config[col].width = width;
35073         this.totalWidth = null;
35074         if(!suppressEvent){
35075              this.fireEvent("widthchange", this, col, width);
35076         }
35077     },
35078
35079     /**
35080      * Returns the total width of all columns.
35081      * @param {Boolean} includeHidden True to include hidden column widths
35082      * @return {Number}
35083      */
35084     getTotalWidth : function(includeHidden){
35085         if(!this.totalWidth){
35086             this.totalWidth = 0;
35087             for(var i = 0, len = this.config.length; i < len; i++){
35088                 if(includeHidden || !this.isHidden(i)){
35089                     this.totalWidth += this.getColumnWidth(i);
35090                 }
35091             }
35092         }
35093         return this.totalWidth;
35094     },
35095
35096     /**
35097      * Returns the header for the specified column.
35098      * @param {Number} col The column index
35099      * @return {String}
35100      */
35101     getColumnHeader : function(col){
35102         return this.config[col].header;
35103     },
35104
35105     /**
35106      * Sets the header for a column.
35107      * @param {Number} col The column index
35108      * @param {String} header The new header
35109      */
35110     setColumnHeader : function(col, header){
35111         this.config[col].header = header;
35112         this.fireEvent("headerchange", this, col, header);
35113     },
35114
35115     /**
35116      * Returns the tooltip for the specified column.
35117      * @param {Number} col The column index
35118      * @return {String}
35119      */
35120     getColumnTooltip : function(col){
35121             return this.config[col].tooltip;
35122     },
35123     /**
35124      * Sets the tooltip for a column.
35125      * @param {Number} col The column index
35126      * @param {String} tooltip The new tooltip
35127      */
35128     setColumnTooltip : function(col, tooltip){
35129             this.config[col].tooltip = tooltip;
35130     },
35131
35132     /**
35133      * Returns the dataIndex for the specified column.
35134      * @param {Number} col The column index
35135      * @return {Number}
35136      */
35137     getDataIndex : function(col){
35138         return this.config[col].dataIndex;
35139     },
35140
35141     /**
35142      * Sets the dataIndex for a column.
35143      * @param {Number} col The column index
35144      * @param {Number} dataIndex The new dataIndex
35145      */
35146     setDataIndex : function(col, dataIndex){
35147         this.config[col].dataIndex = dataIndex;
35148     },
35149
35150     
35151     
35152     /**
35153      * Returns true if the cell is editable.
35154      * @param {Number} colIndex The column index
35155      * @param {Number} rowIndex The row index - this is nto actually used..?
35156      * @return {Boolean}
35157      */
35158     isCellEditable : function(colIndex, rowIndex){
35159         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35160     },
35161
35162     /**
35163      * Returns the editor defined for the cell/column.
35164      * return false or null to disable editing.
35165      * @param {Number} colIndex The column index
35166      * @param {Number} rowIndex The row index
35167      * @return {Object}
35168      */
35169     getCellEditor : function(colIndex, rowIndex){
35170         return this.config[colIndex].editor;
35171     },
35172
35173     /**
35174      * Sets if a column is editable.
35175      * @param {Number} col The column index
35176      * @param {Boolean} editable True if the column is editable
35177      */
35178     setEditable : function(col, editable){
35179         this.config[col].editable = editable;
35180     },
35181
35182
35183     /**
35184      * Returns true if the column is hidden.
35185      * @param {Number} colIndex The column index
35186      * @return {Boolean}
35187      */
35188     isHidden : function(colIndex){
35189         return this.config[colIndex].hidden;
35190     },
35191
35192
35193     /**
35194      * Returns true if the column width cannot be changed
35195      */
35196     isFixed : function(colIndex){
35197         return this.config[colIndex].fixed;
35198     },
35199
35200     /**
35201      * Returns true if the column can be resized
35202      * @return {Boolean}
35203      */
35204     isResizable : function(colIndex){
35205         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35206     },
35207     /**
35208      * Sets if a column is hidden.
35209      * @param {Number} colIndex The column index
35210      * @param {Boolean} hidden True if the column is hidden
35211      */
35212     setHidden : function(colIndex, hidden){
35213         this.config[colIndex].hidden = hidden;
35214         this.totalWidth = null;
35215         this.fireEvent("hiddenchange", this, colIndex, hidden);
35216     },
35217
35218     /**
35219      * Sets the editor for a column.
35220      * @param {Number} col The column index
35221      * @param {Object} editor The editor object
35222      */
35223     setEditor : function(col, editor){
35224         this.config[col].editor = editor;
35225     }
35226 });
35227
35228 Roo.grid.ColumnModel.defaultRenderer = function(value)
35229 {
35230     if(typeof value == "object") {
35231         return value;
35232     }
35233         if(typeof value == "string" && value.length < 1){
35234             return "&#160;";
35235         }
35236     
35237         return String.format("{0}", value);
35238 };
35239
35240 // Alias for backwards compatibility
35241 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35242 /*
35243  * Based on:
35244  * Ext JS Library 1.1.1
35245  * Copyright(c) 2006-2007, Ext JS, LLC.
35246  *
35247  * Originally Released Under LGPL - original licence link has changed is not relivant.
35248  *
35249  * Fork - LGPL
35250  * <script type="text/javascript">
35251  */
35252
35253 /**
35254  * @class Roo.grid.AbstractSelectionModel
35255  * @extends Roo.util.Observable
35256  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35257  * implemented by descendant classes.  This class should not be directly instantiated.
35258  * @constructor
35259  */
35260 Roo.grid.AbstractSelectionModel = function(){
35261     this.locked = false;
35262     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35263 };
35264
35265 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35266     /** @ignore Called by the grid automatically. Do not call directly. */
35267     init : function(grid){
35268         this.grid = grid;
35269         this.initEvents();
35270     },
35271
35272     /**
35273      * Locks the selections.
35274      */
35275     lock : function(){
35276         this.locked = true;
35277     },
35278
35279     /**
35280      * Unlocks the selections.
35281      */
35282     unlock : function(){
35283         this.locked = false;
35284     },
35285
35286     /**
35287      * Returns true if the selections are locked.
35288      * @return {Boolean}
35289      */
35290     isLocked : function(){
35291         return this.locked;
35292     }
35293 });/*
35294  * Based on:
35295  * Ext JS Library 1.1.1
35296  * Copyright(c) 2006-2007, Ext JS, LLC.
35297  *
35298  * Originally Released Under LGPL - original licence link has changed is not relivant.
35299  *
35300  * Fork - LGPL
35301  * <script type="text/javascript">
35302  */
35303 /**
35304  * @extends Roo.grid.AbstractSelectionModel
35305  * @class Roo.grid.RowSelectionModel
35306  * The default SelectionModel used by {@link Roo.grid.Grid}.
35307  * It supports multiple selections and keyboard selection/navigation. 
35308  * @constructor
35309  * @param {Object} config
35310  */
35311 Roo.grid.RowSelectionModel = function(config){
35312     Roo.apply(this, config);
35313     this.selections = new Roo.util.MixedCollection(false, function(o){
35314         return o.id;
35315     });
35316
35317     this.last = false;
35318     this.lastActive = false;
35319
35320     this.addEvents({
35321         /**
35322              * @event selectionchange
35323              * Fires when the selection changes
35324              * @param {SelectionModel} this
35325              */
35326             "selectionchange" : true,
35327         /**
35328              * @event afterselectionchange
35329              * Fires after the selection changes (eg. by key press or clicking)
35330              * @param {SelectionModel} this
35331              */
35332             "afterselectionchange" : true,
35333         /**
35334              * @event beforerowselect
35335              * Fires when a row is selected being selected, return false to cancel.
35336              * @param {SelectionModel} this
35337              * @param {Number} rowIndex The selected index
35338              * @param {Boolean} keepExisting False if other selections will be cleared
35339              */
35340             "beforerowselect" : true,
35341         /**
35342              * @event rowselect
35343              * Fires when a row is selected.
35344              * @param {SelectionModel} this
35345              * @param {Number} rowIndex The selected index
35346              * @param {Roo.data.Record} r The record
35347              */
35348             "rowselect" : true,
35349         /**
35350              * @event rowdeselect
35351              * Fires when a row is deselected.
35352              * @param {SelectionModel} this
35353              * @param {Number} rowIndex The selected index
35354              */
35355         "rowdeselect" : true
35356     });
35357     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35358     this.locked = false;
35359 };
35360
35361 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35362     /**
35363      * @cfg {Boolean} singleSelect
35364      * True to allow selection of only one row at a time (defaults to false)
35365      */
35366     singleSelect : false,
35367
35368     // private
35369     initEvents : function(){
35370
35371         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35372             this.grid.on("mousedown", this.handleMouseDown, this);
35373         }else{ // allow click to work like normal
35374             this.grid.on("rowclick", this.handleDragableRowClick, this);
35375         }
35376
35377         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35378             "up" : function(e){
35379                 if(!e.shiftKey){
35380                     this.selectPrevious(e.shiftKey);
35381                 }else if(this.last !== false && this.lastActive !== false){
35382                     var last = this.last;
35383                     this.selectRange(this.last,  this.lastActive-1);
35384                     this.grid.getView().focusRow(this.lastActive);
35385                     if(last !== false){
35386                         this.last = last;
35387                     }
35388                 }else{
35389                     this.selectFirstRow();
35390                 }
35391                 this.fireEvent("afterselectionchange", this);
35392             },
35393             "down" : function(e){
35394                 if(!e.shiftKey){
35395                     this.selectNext(e.shiftKey);
35396                 }else if(this.last !== false && this.lastActive !== false){
35397                     var last = this.last;
35398                     this.selectRange(this.last,  this.lastActive+1);
35399                     this.grid.getView().focusRow(this.lastActive);
35400                     if(last !== false){
35401                         this.last = last;
35402                     }
35403                 }else{
35404                     this.selectFirstRow();
35405                 }
35406                 this.fireEvent("afterselectionchange", this);
35407             },
35408             scope: this
35409         });
35410
35411         var view = this.grid.view;
35412         view.on("refresh", this.onRefresh, this);
35413         view.on("rowupdated", this.onRowUpdated, this);
35414         view.on("rowremoved", this.onRemove, this);
35415     },
35416
35417     // private
35418     onRefresh : function(){
35419         var ds = this.grid.dataSource, i, v = this.grid.view;
35420         var s = this.selections;
35421         s.each(function(r){
35422             if((i = ds.indexOfId(r.id)) != -1){
35423                 v.onRowSelect(i);
35424                 s.add(ds.getAt(i)); // updating the selection relate data
35425             }else{
35426                 s.remove(r);
35427             }
35428         });
35429     },
35430
35431     // private
35432     onRemove : function(v, index, r){
35433         this.selections.remove(r);
35434     },
35435
35436     // private
35437     onRowUpdated : function(v, index, r){
35438         if(this.isSelected(r)){
35439             v.onRowSelect(index);
35440         }
35441     },
35442
35443     /**
35444      * Select records.
35445      * @param {Array} records The records to select
35446      * @param {Boolean} keepExisting (optional) True to keep existing selections
35447      */
35448     selectRecords : function(records, keepExisting){
35449         if(!keepExisting){
35450             this.clearSelections();
35451         }
35452         var ds = this.grid.dataSource;
35453         for(var i = 0, len = records.length; i < len; i++){
35454             this.selectRow(ds.indexOf(records[i]), true);
35455         }
35456     },
35457
35458     /**
35459      * Gets the number of selected rows.
35460      * @return {Number}
35461      */
35462     getCount : function(){
35463         return this.selections.length;
35464     },
35465
35466     /**
35467      * Selects the first row in the grid.
35468      */
35469     selectFirstRow : function(){
35470         this.selectRow(0);
35471     },
35472
35473     /**
35474      * Select the last row.
35475      * @param {Boolean} keepExisting (optional) True to keep existing selections
35476      */
35477     selectLastRow : function(keepExisting){
35478         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35479     },
35480
35481     /**
35482      * Selects the row immediately following the last selected row.
35483      * @param {Boolean} keepExisting (optional) True to keep existing selections
35484      */
35485     selectNext : function(keepExisting){
35486         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35487             this.selectRow(this.last+1, keepExisting);
35488             this.grid.getView().focusRow(this.last);
35489         }
35490     },
35491
35492     /**
35493      * Selects the row that precedes the last selected row.
35494      * @param {Boolean} keepExisting (optional) True to keep existing selections
35495      */
35496     selectPrevious : function(keepExisting){
35497         if(this.last){
35498             this.selectRow(this.last-1, keepExisting);
35499             this.grid.getView().focusRow(this.last);
35500         }
35501     },
35502
35503     /**
35504      * Returns the selected records
35505      * @return {Array} Array of selected records
35506      */
35507     getSelections : function(){
35508         return [].concat(this.selections.items);
35509     },
35510
35511     /**
35512      * Returns the first selected record.
35513      * @return {Record}
35514      */
35515     getSelected : function(){
35516         return this.selections.itemAt(0);
35517     },
35518
35519
35520     /**
35521      * Clears all selections.
35522      */
35523     clearSelections : function(fast){
35524         if(this.locked) {
35525             return;
35526         }
35527         if(fast !== true){
35528             var ds = this.grid.dataSource;
35529             var s = this.selections;
35530             s.each(function(r){
35531                 this.deselectRow(ds.indexOfId(r.id));
35532             }, this);
35533             s.clear();
35534         }else{
35535             this.selections.clear();
35536         }
35537         this.last = false;
35538     },
35539
35540
35541     /**
35542      * Selects all rows.
35543      */
35544     selectAll : function(){
35545         if(this.locked) {
35546             return;
35547         }
35548         this.selections.clear();
35549         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35550             this.selectRow(i, true);
35551         }
35552     },
35553
35554     /**
35555      * Returns True if there is a selection.
35556      * @return {Boolean}
35557      */
35558     hasSelection : function(){
35559         return this.selections.length > 0;
35560     },
35561
35562     /**
35563      * Returns True if the specified row is selected.
35564      * @param {Number/Record} record The record or index of the record to check
35565      * @return {Boolean}
35566      */
35567     isSelected : function(index){
35568         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35569         return (r && this.selections.key(r.id) ? true : false);
35570     },
35571
35572     /**
35573      * Returns True if the specified record id is selected.
35574      * @param {String} id The id of record to check
35575      * @return {Boolean}
35576      */
35577     isIdSelected : function(id){
35578         return (this.selections.key(id) ? true : false);
35579     },
35580
35581     // private
35582     handleMouseDown : function(e, t){
35583         var view = this.grid.getView(), rowIndex;
35584         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35585             return;
35586         };
35587         if(e.shiftKey && this.last !== false){
35588             var last = this.last;
35589             this.selectRange(last, rowIndex, e.ctrlKey);
35590             this.last = last; // reset the last
35591             view.focusRow(rowIndex);
35592         }else{
35593             var isSelected = this.isSelected(rowIndex);
35594             if(e.button !== 0 && isSelected){
35595                 view.focusRow(rowIndex);
35596             }else if(e.ctrlKey && isSelected){
35597                 this.deselectRow(rowIndex);
35598             }else if(!isSelected){
35599                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35600                 view.focusRow(rowIndex);
35601             }
35602         }
35603         this.fireEvent("afterselectionchange", this);
35604     },
35605     // private
35606     handleDragableRowClick :  function(grid, rowIndex, e) 
35607     {
35608         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35609             this.selectRow(rowIndex, false);
35610             grid.view.focusRow(rowIndex);
35611              this.fireEvent("afterselectionchange", this);
35612         }
35613     },
35614     
35615     /**
35616      * Selects multiple rows.
35617      * @param {Array} rows Array of the indexes of the row to select
35618      * @param {Boolean} keepExisting (optional) True to keep existing selections
35619      */
35620     selectRows : function(rows, keepExisting){
35621         if(!keepExisting){
35622             this.clearSelections();
35623         }
35624         for(var i = 0, len = rows.length; i < len; i++){
35625             this.selectRow(rows[i], true);
35626         }
35627     },
35628
35629     /**
35630      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35631      * @param {Number} startRow The index of the first row in the range
35632      * @param {Number} endRow The index of the last row in the range
35633      * @param {Boolean} keepExisting (optional) True to retain existing selections
35634      */
35635     selectRange : function(startRow, endRow, keepExisting){
35636         if(this.locked) {
35637             return;
35638         }
35639         if(!keepExisting){
35640             this.clearSelections();
35641         }
35642         if(startRow <= endRow){
35643             for(var i = startRow; i <= endRow; i++){
35644                 this.selectRow(i, true);
35645             }
35646         }else{
35647             for(var i = startRow; i >= endRow; i--){
35648                 this.selectRow(i, true);
35649             }
35650         }
35651     },
35652
35653     /**
35654      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35655      * @param {Number} startRow The index of the first row in the range
35656      * @param {Number} endRow The index of the last row in the range
35657      */
35658     deselectRange : function(startRow, endRow, preventViewNotify){
35659         if(this.locked) {
35660             return;
35661         }
35662         for(var i = startRow; i <= endRow; i++){
35663             this.deselectRow(i, preventViewNotify);
35664         }
35665     },
35666
35667     /**
35668      * Selects a row.
35669      * @param {Number} row The index of the row to select
35670      * @param {Boolean} keepExisting (optional) True to keep existing selections
35671      */
35672     selectRow : function(index, keepExisting, preventViewNotify){
35673         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35674             return;
35675         }
35676         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35677             if(!keepExisting || this.singleSelect){
35678                 this.clearSelections();
35679             }
35680             var r = this.grid.dataSource.getAt(index);
35681             this.selections.add(r);
35682             this.last = this.lastActive = index;
35683             if(!preventViewNotify){
35684                 this.grid.getView().onRowSelect(index);
35685             }
35686             this.fireEvent("rowselect", this, index, r);
35687             this.fireEvent("selectionchange", this);
35688         }
35689     },
35690
35691     /**
35692      * Deselects a row.
35693      * @param {Number} row The index of the row to deselect
35694      */
35695     deselectRow : function(index, preventViewNotify){
35696         if(this.locked) {
35697             return;
35698         }
35699         if(this.last == index){
35700             this.last = false;
35701         }
35702         if(this.lastActive == index){
35703             this.lastActive = false;
35704         }
35705         var r = this.grid.dataSource.getAt(index);
35706         this.selections.remove(r);
35707         if(!preventViewNotify){
35708             this.grid.getView().onRowDeselect(index);
35709         }
35710         this.fireEvent("rowdeselect", this, index);
35711         this.fireEvent("selectionchange", this);
35712     },
35713
35714     // private
35715     restoreLast : function(){
35716         if(this._last){
35717             this.last = this._last;
35718         }
35719     },
35720
35721     // private
35722     acceptsNav : function(row, col, cm){
35723         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35724     },
35725
35726     // private
35727     onEditorKey : function(field, e){
35728         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35729         if(k == e.TAB){
35730             e.stopEvent();
35731             ed.completeEdit();
35732             if(e.shiftKey){
35733                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35734             }else{
35735                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35736             }
35737         }else if(k == e.ENTER && !e.ctrlKey){
35738             e.stopEvent();
35739             ed.completeEdit();
35740             if(e.shiftKey){
35741                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35742             }else{
35743                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35744             }
35745         }else if(k == e.ESC){
35746             ed.cancelEdit();
35747         }
35748         if(newCell){
35749             g.startEditing(newCell[0], newCell[1]);
35750         }
35751     }
35752 });/*
35753  * Based on:
35754  * Ext JS Library 1.1.1
35755  * Copyright(c) 2006-2007, Ext JS, LLC.
35756  *
35757  * Originally Released Under LGPL - original licence link has changed is not relivant.
35758  *
35759  * Fork - LGPL
35760  * <script type="text/javascript">
35761  */
35762 /**
35763  * @class Roo.grid.CellSelectionModel
35764  * @extends Roo.grid.AbstractSelectionModel
35765  * This class provides the basic implementation for cell selection in a grid.
35766  * @constructor
35767  * @param {Object} config The object containing the configuration of this model.
35768  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
35769  */
35770 Roo.grid.CellSelectionModel = function(config){
35771     Roo.apply(this, config);
35772
35773     this.selection = null;
35774
35775     this.addEvents({
35776         /**
35777              * @event beforerowselect
35778              * Fires before a cell is selected.
35779              * @param {SelectionModel} this
35780              * @param {Number} rowIndex The selected row index
35781              * @param {Number} colIndex The selected cell index
35782              */
35783             "beforecellselect" : true,
35784         /**
35785              * @event cellselect
35786              * Fires when a cell is selected.
35787              * @param {SelectionModel} this
35788              * @param {Number} rowIndex The selected row index
35789              * @param {Number} colIndex The selected cell index
35790              */
35791             "cellselect" : true,
35792         /**
35793              * @event selectionchange
35794              * Fires when the active selection changes.
35795              * @param {SelectionModel} this
35796              * @param {Object} selection null for no selection or an object (o) with two properties
35797                 <ul>
35798                 <li>o.record: the record object for the row the selection is in</li>
35799                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35800                 </ul>
35801              */
35802             "selectionchange" : true,
35803         /**
35804              * @event tabend
35805              * Fires when the tab (or enter) was pressed on the last editable cell
35806              * You can use this to trigger add new row.
35807              * @param {SelectionModel} this
35808              */
35809             "tabend" : true,
35810          /**
35811              * @event beforeeditnext
35812              * Fires before the next editable sell is made active
35813              * You can use this to skip to another cell or fire the tabend
35814              *    if you set cell to false
35815              * @param {Object} eventdata object : { cell : [ row, col ] } 
35816              */
35817             "beforeeditnext" : true
35818     });
35819     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35820 };
35821
35822 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35823     
35824     enter_is_tab: false,
35825
35826     /** @ignore */
35827     initEvents : function(){
35828         this.grid.on("mousedown", this.handleMouseDown, this);
35829         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35830         var view = this.grid.view;
35831         view.on("refresh", this.onViewChange, this);
35832         view.on("rowupdated", this.onRowUpdated, this);
35833         view.on("beforerowremoved", this.clearSelections, this);
35834         view.on("beforerowsinserted", this.clearSelections, this);
35835         if(this.grid.isEditor){
35836             this.grid.on("beforeedit", this.beforeEdit,  this);
35837         }
35838     },
35839
35840         //private
35841     beforeEdit : function(e){
35842         this.select(e.row, e.column, false, true, e.record);
35843     },
35844
35845         //private
35846     onRowUpdated : function(v, index, r){
35847         if(this.selection && this.selection.record == r){
35848             v.onCellSelect(index, this.selection.cell[1]);
35849         }
35850     },
35851
35852         //private
35853     onViewChange : function(){
35854         this.clearSelections(true);
35855     },
35856
35857         /**
35858          * Returns the currently selected cell,.
35859          * @return {Array} The selected cell (row, column) or null if none selected.
35860          */
35861     getSelectedCell : function(){
35862         return this.selection ? this.selection.cell : null;
35863     },
35864
35865     /**
35866      * Clears all selections.
35867      * @param {Boolean} true to prevent the gridview from being notified about the change.
35868      */
35869     clearSelections : function(preventNotify){
35870         var s = this.selection;
35871         if(s){
35872             if(preventNotify !== true){
35873                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35874             }
35875             this.selection = null;
35876             this.fireEvent("selectionchange", this, null);
35877         }
35878     },
35879
35880     /**
35881      * Returns true if there is a selection.
35882      * @return {Boolean}
35883      */
35884     hasSelection : function(){
35885         return this.selection ? true : false;
35886     },
35887
35888     /** @ignore */
35889     handleMouseDown : function(e, t){
35890         var v = this.grid.getView();
35891         if(this.isLocked()){
35892             return;
35893         };
35894         var row = v.findRowIndex(t);
35895         var cell = v.findCellIndex(t);
35896         if(row !== false && cell !== false){
35897             this.select(row, cell);
35898         }
35899     },
35900
35901     /**
35902      * Selects a cell.
35903      * @param {Number} rowIndex
35904      * @param {Number} collIndex
35905      */
35906     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35907         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35908             this.clearSelections();
35909             r = r || this.grid.dataSource.getAt(rowIndex);
35910             this.selection = {
35911                 record : r,
35912                 cell : [rowIndex, colIndex]
35913             };
35914             if(!preventViewNotify){
35915                 var v = this.grid.getView();
35916                 v.onCellSelect(rowIndex, colIndex);
35917                 if(preventFocus !== true){
35918                     v.focusCell(rowIndex, colIndex);
35919                 }
35920             }
35921             this.fireEvent("cellselect", this, rowIndex, colIndex);
35922             this.fireEvent("selectionchange", this, this.selection);
35923         }
35924     },
35925
35926         //private
35927     isSelectable : function(rowIndex, colIndex, cm){
35928         return !cm.isHidden(colIndex);
35929     },
35930
35931     /** @ignore */
35932     handleKeyDown : function(e){
35933         //Roo.log('Cell Sel Model handleKeyDown');
35934         if(!e.isNavKeyPress()){
35935             return;
35936         }
35937         var g = this.grid, s = this.selection;
35938         if(!s){
35939             e.stopEvent();
35940             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35941             if(cell){
35942                 this.select(cell[0], cell[1]);
35943             }
35944             return;
35945         }
35946         var sm = this;
35947         var walk = function(row, col, step){
35948             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35949         };
35950         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35951         var newCell;
35952
35953       
35954
35955         switch(k){
35956             case e.TAB:
35957                 // handled by onEditorKey
35958                 if (g.isEditor && g.editing) {
35959                     return;
35960                 }
35961                 if(e.shiftKey) {
35962                     newCell = walk(r, c-1, -1);
35963                 } else {
35964                     newCell = walk(r, c+1, 1);
35965                 }
35966                 break;
35967             
35968             case e.DOWN:
35969                newCell = walk(r+1, c, 1);
35970                 break;
35971             
35972             case e.UP:
35973                 newCell = walk(r-1, c, -1);
35974                 break;
35975             
35976             case e.RIGHT:
35977                 newCell = walk(r, c+1, 1);
35978                 break;
35979             
35980             case e.LEFT:
35981                 newCell = walk(r, c-1, -1);
35982                 break;
35983             
35984             case e.ENTER:
35985                 
35986                 if(g.isEditor && !g.editing){
35987                    g.startEditing(r, c);
35988                    e.stopEvent();
35989                    return;
35990                 }
35991                 
35992                 
35993              break;
35994         };
35995         if(newCell){
35996             this.select(newCell[0], newCell[1]);
35997             e.stopEvent();
35998             
35999         }
36000     },
36001
36002     acceptsNav : function(row, col, cm){
36003         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36004     },
36005     /**
36006      * Selects a cell.
36007      * @param {Number} field (not used) - as it's normally used as a listener
36008      * @param {Number} e - event - fake it by using
36009      *
36010      * var e = Roo.EventObjectImpl.prototype;
36011      * e.keyCode = e.TAB
36012      *
36013      * 
36014      */
36015     onEditorKey : function(field, e){
36016         
36017         var k = e.getKey(),
36018             newCell,
36019             g = this.grid,
36020             ed = g.activeEditor,
36021             forward = false;
36022         ///Roo.log('onEditorKey' + k);
36023         
36024         
36025         if (this.enter_is_tab && k == e.ENTER) {
36026             k = e.TAB;
36027         }
36028         
36029         if(k == e.TAB){
36030             if(e.shiftKey){
36031                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36032             }else{
36033                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36034                 forward = true;
36035             }
36036             
36037             e.stopEvent();
36038             
36039         } else if(k == e.ENTER &&  !e.ctrlKey){
36040             ed.completeEdit();
36041             e.stopEvent();
36042             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36043         
36044                 } else if(k == e.ESC){
36045             ed.cancelEdit();
36046         }
36047                 
36048         if (newCell) {
36049             var ecall = { cell : newCell, forward : forward };
36050             this.fireEvent('beforeeditnext', ecall );
36051             newCell = ecall.cell;
36052                         forward = ecall.forward;
36053         }
36054                 
36055         if(newCell){
36056             //Roo.log('next cell after edit');
36057             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36058         } else if (forward) {
36059             // tabbed past last
36060             this.fireEvent.defer(100, this, ['tabend',this]);
36061         }
36062     }
36063 });/*
36064  * Based on:
36065  * Ext JS Library 1.1.1
36066  * Copyright(c) 2006-2007, Ext JS, LLC.
36067  *
36068  * Originally Released Under LGPL - original licence link has changed is not relivant.
36069  *
36070  * Fork - LGPL
36071  * <script type="text/javascript">
36072  */
36073  
36074 /**
36075  * @class Roo.grid.EditorGrid
36076  * @extends Roo.grid.Grid
36077  * Class for creating and editable grid.
36078  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36079  * The container MUST have some type of size defined for the grid to fill. The container will be 
36080  * automatically set to position relative if it isn't already.
36081  * @param {Object} dataSource The data model to bind to
36082  * @param {Object} colModel The column model with info about this grid's columns
36083  */
36084 Roo.grid.EditorGrid = function(container, config){
36085     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36086     this.getGridEl().addClass("xedit-grid");
36087
36088     if(!this.selModel){
36089         this.selModel = new Roo.grid.CellSelectionModel();
36090     }
36091
36092     this.activeEditor = null;
36093
36094         this.addEvents({
36095             /**
36096              * @event beforeedit
36097              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36098              * <ul style="padding:5px;padding-left:16px;">
36099              * <li>grid - This grid</li>
36100              * <li>record - The record being edited</li>
36101              * <li>field - The field name being edited</li>
36102              * <li>value - The value for the field being edited.</li>
36103              * <li>row - The grid row index</li>
36104              * <li>column - The grid column index</li>
36105              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36106              * </ul>
36107              * @param {Object} e An edit event (see above for description)
36108              */
36109             "beforeedit" : true,
36110             /**
36111              * @event afteredit
36112              * Fires after a cell is edited. <br />
36113              * <ul style="padding:5px;padding-left:16px;">
36114              * <li>grid - This grid</li>
36115              * <li>record - The record being edited</li>
36116              * <li>field - The field name being edited</li>
36117              * <li>value - The value being set</li>
36118              * <li>originalValue - The original value for the field, before the edit.</li>
36119              * <li>row - The grid row index</li>
36120              * <li>column - The grid column index</li>
36121              * </ul>
36122              * @param {Object} e An edit event (see above for description)
36123              */
36124             "afteredit" : true,
36125             /**
36126              * @event validateedit
36127              * Fires after a cell is edited, but before the value is set in the record. 
36128          * You can use this to modify the value being set in the field, Return false
36129              * to cancel the change. The edit event object has the following properties <br />
36130              * <ul style="padding:5px;padding-left:16px;">
36131          * <li>editor - This editor</li>
36132              * <li>grid - This grid</li>
36133              * <li>record - The record being edited</li>
36134              * <li>field - The field name being edited</li>
36135              * <li>value - The value being set</li>
36136              * <li>originalValue - The original value for the field, before the edit.</li>
36137              * <li>row - The grid row index</li>
36138              * <li>column - The grid column index</li>
36139              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36140              * </ul>
36141              * @param {Object} e An edit event (see above for description)
36142              */
36143             "validateedit" : true
36144         });
36145     this.on("bodyscroll", this.stopEditing,  this);
36146     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36147 };
36148
36149 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36150     /**
36151      * @cfg {Number} clicksToEdit
36152      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36153      */
36154     clicksToEdit: 2,
36155
36156     // private
36157     isEditor : true,
36158     // private
36159     trackMouseOver: false, // causes very odd FF errors
36160
36161     onCellDblClick : function(g, row, col){
36162         this.startEditing(row, col);
36163     },
36164
36165     onEditComplete : function(ed, value, startValue){
36166         this.editing = false;
36167         this.activeEditor = null;
36168         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36169         var r = ed.record;
36170         var field = this.colModel.getDataIndex(ed.col);
36171         var e = {
36172             grid: this,
36173             record: r,
36174             field: field,
36175             originalValue: startValue,
36176             value: value,
36177             row: ed.row,
36178             column: ed.col,
36179             cancel:false,
36180             editor: ed
36181         };
36182         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36183         cell.show();
36184           
36185         if(String(value) !== String(startValue)){
36186             
36187             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36188                 r.set(field, e.value);
36189                 // if we are dealing with a combo box..
36190                 // then we also set the 'name' colum to be the displayField
36191                 if (ed.field.displayField && ed.field.name) {
36192                     r.set(ed.field.name, ed.field.el.dom.value);
36193                 }
36194                 
36195                 delete e.cancel; //?? why!!!
36196                 this.fireEvent("afteredit", e);
36197             }
36198         } else {
36199             this.fireEvent("afteredit", e); // always fire it!
36200         }
36201         this.view.focusCell(ed.row, ed.col);
36202     },
36203
36204     /**
36205      * Starts editing the specified for the specified row/column
36206      * @param {Number} rowIndex
36207      * @param {Number} colIndex
36208      */
36209     startEditing : function(row, col){
36210         this.stopEditing();
36211         if(this.colModel.isCellEditable(col, row)){
36212             this.view.ensureVisible(row, col, true);
36213           
36214             var r = this.dataSource.getAt(row);
36215             var field = this.colModel.getDataIndex(col);
36216             var cell = Roo.get(this.view.getCell(row,col));
36217             var e = {
36218                 grid: this,
36219                 record: r,
36220                 field: field,
36221                 value: r.data[field],
36222                 row: row,
36223                 column: col,
36224                 cancel:false 
36225             };
36226             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36227                 this.editing = true;
36228                 var ed = this.colModel.getCellEditor(col, row);
36229                 
36230                 if (!ed) {
36231                     return;
36232                 }
36233                 if(!ed.rendered){
36234                     ed.render(ed.parentEl || document.body);
36235                 }
36236                 ed.field.reset();
36237                
36238                 cell.hide();
36239                 
36240                 (function(){ // complex but required for focus issues in safari, ie and opera
36241                     ed.row = row;
36242                     ed.col = col;
36243                     ed.record = r;
36244                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36245                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36246                     this.activeEditor = ed;
36247                     var v = r.data[field];
36248                     ed.startEdit(this.view.getCell(row, col), v);
36249                     // combo's with 'displayField and name set
36250                     if (ed.field.displayField && ed.field.name) {
36251                         ed.field.el.dom.value = r.data[ed.field.name];
36252                     }
36253                     
36254                     
36255                 }).defer(50, this);
36256             }
36257         }
36258     },
36259         
36260     /**
36261      * Stops any active editing
36262      */
36263     stopEditing : function(){
36264         if(this.activeEditor){
36265             this.activeEditor.completeEdit();
36266         }
36267         this.activeEditor = null;
36268     },
36269         
36270          /**
36271      * Called to get grid's drag proxy text, by default returns this.ddText.
36272      * @return {String}
36273      */
36274     getDragDropText : function(){
36275         var count = this.selModel.getSelectedCell() ? 1 : 0;
36276         return String.format(this.ddText, count, count == 1 ? '' : 's');
36277     }
36278         
36279 });/*
36280  * Based on:
36281  * Ext JS Library 1.1.1
36282  * Copyright(c) 2006-2007, Ext JS, LLC.
36283  *
36284  * Originally Released Under LGPL - original licence link has changed is not relivant.
36285  *
36286  * Fork - LGPL
36287  * <script type="text/javascript">
36288  */
36289
36290 // private - not really -- you end up using it !
36291 // This is a support class used internally by the Grid components
36292
36293 /**
36294  * @class Roo.grid.GridEditor
36295  * @extends Roo.Editor
36296  * Class for creating and editable grid elements.
36297  * @param {Object} config any settings (must include field)
36298  */
36299 Roo.grid.GridEditor = function(field, config){
36300     if (!config && field.field) {
36301         config = field;
36302         field = Roo.factory(config.field, Roo.form);
36303     }
36304     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36305     field.monitorTab = false;
36306 };
36307
36308 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36309     
36310     /**
36311      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36312      */
36313     
36314     alignment: "tl-tl",
36315     autoSize: "width",
36316     hideEl : false,
36317     cls: "x-small-editor x-grid-editor",
36318     shim:false,
36319     shadow:"frame"
36320 });/*
36321  * Based on:
36322  * Ext JS Library 1.1.1
36323  * Copyright(c) 2006-2007, Ext JS, LLC.
36324  *
36325  * Originally Released Under LGPL - original licence link has changed is not relivant.
36326  *
36327  * Fork - LGPL
36328  * <script type="text/javascript">
36329  */
36330   
36331
36332   
36333 Roo.grid.PropertyRecord = Roo.data.Record.create([
36334     {name:'name',type:'string'},  'value'
36335 ]);
36336
36337
36338 Roo.grid.PropertyStore = function(grid, source){
36339     this.grid = grid;
36340     this.store = new Roo.data.Store({
36341         recordType : Roo.grid.PropertyRecord
36342     });
36343     this.store.on('update', this.onUpdate,  this);
36344     if(source){
36345         this.setSource(source);
36346     }
36347     Roo.grid.PropertyStore.superclass.constructor.call(this);
36348 };
36349
36350
36351
36352 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36353     setSource : function(o){
36354         this.source = o;
36355         this.store.removeAll();
36356         var data = [];
36357         for(var k in o){
36358             if(this.isEditableValue(o[k])){
36359                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36360             }
36361         }
36362         this.store.loadRecords({records: data}, {}, true);
36363     },
36364
36365     onUpdate : function(ds, record, type){
36366         if(type == Roo.data.Record.EDIT){
36367             var v = record.data['value'];
36368             var oldValue = record.modified['value'];
36369             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36370                 this.source[record.id] = v;
36371                 record.commit();
36372                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36373             }else{
36374                 record.reject();
36375             }
36376         }
36377     },
36378
36379     getProperty : function(row){
36380        return this.store.getAt(row);
36381     },
36382
36383     isEditableValue: function(val){
36384         if(val && val instanceof Date){
36385             return true;
36386         }else if(typeof val == 'object' || typeof val == 'function'){
36387             return false;
36388         }
36389         return true;
36390     },
36391
36392     setValue : function(prop, value){
36393         this.source[prop] = value;
36394         this.store.getById(prop).set('value', value);
36395     },
36396
36397     getSource : function(){
36398         return this.source;
36399     }
36400 });
36401
36402 Roo.grid.PropertyColumnModel = function(grid, store){
36403     this.grid = grid;
36404     var g = Roo.grid;
36405     g.PropertyColumnModel.superclass.constructor.call(this, [
36406         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36407         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36408     ]);
36409     this.store = store;
36410     this.bselect = Roo.DomHelper.append(document.body, {
36411         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36412             {tag: 'option', value: 'true', html: 'true'},
36413             {tag: 'option', value: 'false', html: 'false'}
36414         ]
36415     });
36416     Roo.id(this.bselect);
36417     var f = Roo.form;
36418     this.editors = {
36419         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36420         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36421         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36422         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36423         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36424     };
36425     this.renderCellDelegate = this.renderCell.createDelegate(this);
36426     this.renderPropDelegate = this.renderProp.createDelegate(this);
36427 };
36428
36429 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36430     
36431     
36432     nameText : 'Name',
36433     valueText : 'Value',
36434     
36435     dateFormat : 'm/j/Y',
36436     
36437     
36438     renderDate : function(dateVal){
36439         return dateVal.dateFormat(this.dateFormat);
36440     },
36441
36442     renderBool : function(bVal){
36443         return bVal ? 'true' : 'false';
36444     },
36445
36446     isCellEditable : function(colIndex, rowIndex){
36447         return colIndex == 1;
36448     },
36449
36450     getRenderer : function(col){
36451         return col == 1 ?
36452             this.renderCellDelegate : this.renderPropDelegate;
36453     },
36454
36455     renderProp : function(v){
36456         return this.getPropertyName(v);
36457     },
36458
36459     renderCell : function(val){
36460         var rv = val;
36461         if(val instanceof Date){
36462             rv = this.renderDate(val);
36463         }else if(typeof val == 'boolean'){
36464             rv = this.renderBool(val);
36465         }
36466         return Roo.util.Format.htmlEncode(rv);
36467     },
36468
36469     getPropertyName : function(name){
36470         var pn = this.grid.propertyNames;
36471         return pn && pn[name] ? pn[name] : name;
36472     },
36473
36474     getCellEditor : function(colIndex, rowIndex){
36475         var p = this.store.getProperty(rowIndex);
36476         var n = p.data['name'], val = p.data['value'];
36477         
36478         if(typeof(this.grid.customEditors[n]) == 'string'){
36479             return this.editors[this.grid.customEditors[n]];
36480         }
36481         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36482             return this.grid.customEditors[n];
36483         }
36484         if(val instanceof Date){
36485             return this.editors['date'];
36486         }else if(typeof val == 'number'){
36487             return this.editors['number'];
36488         }else if(typeof val == 'boolean'){
36489             return this.editors['boolean'];
36490         }else{
36491             return this.editors['string'];
36492         }
36493     }
36494 });
36495
36496 /**
36497  * @class Roo.grid.PropertyGrid
36498  * @extends Roo.grid.EditorGrid
36499  * This class represents the  interface of a component based property grid control.
36500  * <br><br>Usage:<pre><code>
36501  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36502       
36503  });
36504  // set any options
36505  grid.render();
36506  * </code></pre>
36507   
36508  * @constructor
36509  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36510  * The container MUST have some type of size defined for the grid to fill. The container will be
36511  * automatically set to position relative if it isn't already.
36512  * @param {Object} config A config object that sets properties on this grid.
36513  */
36514 Roo.grid.PropertyGrid = function(container, config){
36515     config = config || {};
36516     var store = new Roo.grid.PropertyStore(this);
36517     this.store = store;
36518     var cm = new Roo.grid.PropertyColumnModel(this, store);
36519     store.store.sort('name', 'ASC');
36520     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36521         ds: store.store,
36522         cm: cm,
36523         enableColLock:false,
36524         enableColumnMove:false,
36525         stripeRows:false,
36526         trackMouseOver: false,
36527         clicksToEdit:1
36528     }, config));
36529     this.getGridEl().addClass('x-props-grid');
36530     this.lastEditRow = null;
36531     this.on('columnresize', this.onColumnResize, this);
36532     this.addEvents({
36533          /**
36534              * @event beforepropertychange
36535              * Fires before a property changes (return false to stop?)
36536              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36537              * @param {String} id Record Id
36538              * @param {String} newval New Value
36539          * @param {String} oldval Old Value
36540              */
36541         "beforepropertychange": true,
36542         /**
36543              * @event propertychange
36544              * Fires after a property changes
36545              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36546              * @param {String} id Record Id
36547              * @param {String} newval New Value
36548          * @param {String} oldval Old Value
36549              */
36550         "propertychange": true
36551     });
36552     this.customEditors = this.customEditors || {};
36553 };
36554 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36555     
36556      /**
36557      * @cfg {Object} customEditors map of colnames=> custom editors.
36558      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36559      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36560      * false disables editing of the field.
36561          */
36562     
36563       /**
36564      * @cfg {Object} propertyNames map of property Names to their displayed value
36565          */
36566     
36567     render : function(){
36568         Roo.grid.PropertyGrid.superclass.render.call(this);
36569         this.autoSize.defer(100, this);
36570     },
36571
36572     autoSize : function(){
36573         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36574         if(this.view){
36575             this.view.fitColumns();
36576         }
36577     },
36578
36579     onColumnResize : function(){
36580         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36581         this.autoSize();
36582     },
36583     /**
36584      * Sets the data for the Grid
36585      * accepts a Key => Value object of all the elements avaiable.
36586      * @param {Object} data  to appear in grid.
36587      */
36588     setSource : function(source){
36589         this.store.setSource(source);
36590         //this.autoSize();
36591     },
36592     /**
36593      * Gets all the data from the grid.
36594      * @return {Object} data  data stored in grid
36595      */
36596     getSource : function(){
36597         return this.store.getSource();
36598     }
36599 });/*
36600   
36601  * Licence LGPL
36602  
36603  */
36604  
36605 /**
36606  * @class Roo.grid.Calendar
36607  * @extends Roo.util.Grid
36608  * This class extends the Grid to provide a calendar widget
36609  * <br><br>Usage:<pre><code>
36610  var grid = new Roo.grid.Calendar("my-container-id", {
36611      ds: myDataStore,
36612      cm: myColModel,
36613      selModel: mySelectionModel,
36614      autoSizeColumns: true,
36615      monitorWindowResize: false,
36616      trackMouseOver: true
36617      eventstore : real data store..
36618  });
36619  // set any options
36620  grid.render();
36621   
36622   * @constructor
36623  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36624  * The container MUST have some type of size defined for the grid to fill. The container will be
36625  * automatically set to position relative if it isn't already.
36626  * @param {Object} config A config object that sets properties on this grid.
36627  */
36628 Roo.grid.Calendar = function(container, config){
36629         // initialize the container
36630         this.container = Roo.get(container);
36631         this.container.update("");
36632         this.container.setStyle("overflow", "hidden");
36633     this.container.addClass('x-grid-container');
36634
36635     this.id = this.container.id;
36636
36637     Roo.apply(this, config);
36638     // check and correct shorthanded configs
36639     
36640     var rows = [];
36641     var d =1;
36642     for (var r = 0;r < 6;r++) {
36643         
36644         rows[r]=[];
36645         for (var c =0;c < 7;c++) {
36646             rows[r][c]= '';
36647         }
36648     }
36649     if (this.eventStore) {
36650         this.eventStore= Roo.factory(this.eventStore, Roo.data);
36651         this.eventStore.on('load',this.onLoad, this);
36652         this.eventStore.on('beforeload',this.clearEvents, this);
36653          
36654     }
36655     
36656     this.dataSource = new Roo.data.Store({
36657             proxy: new Roo.data.MemoryProxy(rows),
36658             reader: new Roo.data.ArrayReader({}, [
36659                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36660     });
36661
36662     this.dataSource.load();
36663     this.ds = this.dataSource;
36664     this.ds.xmodule = this.xmodule || false;
36665     
36666     
36667     var cellRender = function(v,x,r)
36668     {
36669         return String.format(
36670             '<div class="fc-day  fc-widget-content"><div>' +
36671                 '<div class="fc-event-container"></div>' +
36672                 '<div class="fc-day-number">{0}</div>'+
36673                 
36674                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36675             '</div></div>', v);
36676     
36677     }
36678     
36679     
36680     this.colModel = new Roo.grid.ColumnModel( [
36681         {
36682             xtype: 'ColumnModel',
36683             xns: Roo.grid,
36684             dataIndex : 'weekday0',
36685             header : 'Sunday',
36686             renderer : cellRender
36687         },
36688         {
36689             xtype: 'ColumnModel',
36690             xns: Roo.grid,
36691             dataIndex : 'weekday1',
36692             header : 'Monday',
36693             renderer : cellRender
36694         },
36695         {
36696             xtype: 'ColumnModel',
36697             xns: Roo.grid,
36698             dataIndex : 'weekday2',
36699             header : 'Tuesday',
36700             renderer : cellRender
36701         },
36702         {
36703             xtype: 'ColumnModel',
36704             xns: Roo.grid,
36705             dataIndex : 'weekday3',
36706             header : 'Wednesday',
36707             renderer : cellRender
36708         },
36709         {
36710             xtype: 'ColumnModel',
36711             xns: Roo.grid,
36712             dataIndex : 'weekday4',
36713             header : 'Thursday',
36714             renderer : cellRender
36715         },
36716         {
36717             xtype: 'ColumnModel',
36718             xns: Roo.grid,
36719             dataIndex : 'weekday5',
36720             header : 'Friday',
36721             renderer : cellRender
36722         },
36723         {
36724             xtype: 'ColumnModel',
36725             xns: Roo.grid,
36726             dataIndex : 'weekday6',
36727             header : 'Saturday',
36728             renderer : cellRender
36729         }
36730     ]);
36731     this.cm = this.colModel;
36732     this.cm.xmodule = this.xmodule || false;
36733  
36734         
36735           
36736     //this.selModel = new Roo.grid.CellSelectionModel();
36737     //this.sm = this.selModel;
36738     //this.selModel.init(this);
36739     
36740     
36741     if(this.width){
36742         this.container.setWidth(this.width);
36743     }
36744
36745     if(this.height){
36746         this.container.setHeight(this.height);
36747     }
36748     /** @private */
36749         this.addEvents({
36750         // raw events
36751         /**
36752          * @event click
36753          * The raw click event for the entire grid.
36754          * @param {Roo.EventObject} e
36755          */
36756         "click" : true,
36757         /**
36758          * @event dblclick
36759          * The raw dblclick event for the entire grid.
36760          * @param {Roo.EventObject} e
36761          */
36762         "dblclick" : true,
36763         /**
36764          * @event contextmenu
36765          * The raw contextmenu event for the entire grid.
36766          * @param {Roo.EventObject} e
36767          */
36768         "contextmenu" : true,
36769         /**
36770          * @event mousedown
36771          * The raw mousedown event for the entire grid.
36772          * @param {Roo.EventObject} e
36773          */
36774         "mousedown" : true,
36775         /**
36776          * @event mouseup
36777          * The raw mouseup event for the entire grid.
36778          * @param {Roo.EventObject} e
36779          */
36780         "mouseup" : true,
36781         /**
36782          * @event mouseover
36783          * The raw mouseover event for the entire grid.
36784          * @param {Roo.EventObject} e
36785          */
36786         "mouseover" : true,
36787         /**
36788          * @event mouseout
36789          * The raw mouseout event for the entire grid.
36790          * @param {Roo.EventObject} e
36791          */
36792         "mouseout" : true,
36793         /**
36794          * @event keypress
36795          * The raw keypress event for the entire grid.
36796          * @param {Roo.EventObject} e
36797          */
36798         "keypress" : true,
36799         /**
36800          * @event keydown
36801          * The raw keydown event for the entire grid.
36802          * @param {Roo.EventObject} e
36803          */
36804         "keydown" : true,
36805
36806         // custom events
36807
36808         /**
36809          * @event cellclick
36810          * Fires when a cell is clicked
36811          * @param {Grid} this
36812          * @param {Number} rowIndex
36813          * @param {Number} columnIndex
36814          * @param {Roo.EventObject} e
36815          */
36816         "cellclick" : true,
36817         /**
36818          * @event celldblclick
36819          * Fires when a cell is double clicked
36820          * @param {Grid} this
36821          * @param {Number} rowIndex
36822          * @param {Number} columnIndex
36823          * @param {Roo.EventObject} e
36824          */
36825         "celldblclick" : true,
36826         /**
36827          * @event rowclick
36828          * Fires when a row is clicked
36829          * @param {Grid} this
36830          * @param {Number} rowIndex
36831          * @param {Roo.EventObject} e
36832          */
36833         "rowclick" : true,
36834         /**
36835          * @event rowdblclick
36836          * Fires when a row is double clicked
36837          * @param {Grid} this
36838          * @param {Number} rowIndex
36839          * @param {Roo.EventObject} e
36840          */
36841         "rowdblclick" : true,
36842         /**
36843          * @event headerclick
36844          * Fires when a header is clicked
36845          * @param {Grid} this
36846          * @param {Number} columnIndex
36847          * @param {Roo.EventObject} e
36848          */
36849         "headerclick" : true,
36850         /**
36851          * @event headerdblclick
36852          * Fires when a header cell is double clicked
36853          * @param {Grid} this
36854          * @param {Number} columnIndex
36855          * @param {Roo.EventObject} e
36856          */
36857         "headerdblclick" : true,
36858         /**
36859          * @event rowcontextmenu
36860          * Fires when a row is right clicked
36861          * @param {Grid} this
36862          * @param {Number} rowIndex
36863          * @param {Roo.EventObject} e
36864          */
36865         "rowcontextmenu" : true,
36866         /**
36867          * @event cellcontextmenu
36868          * Fires when a cell is right clicked
36869          * @param {Grid} this
36870          * @param {Number} rowIndex
36871          * @param {Number} cellIndex
36872          * @param {Roo.EventObject} e
36873          */
36874          "cellcontextmenu" : true,
36875         /**
36876          * @event headercontextmenu
36877          * Fires when a header is right clicked
36878          * @param {Grid} this
36879          * @param {Number} columnIndex
36880          * @param {Roo.EventObject} e
36881          */
36882         "headercontextmenu" : true,
36883         /**
36884          * @event bodyscroll
36885          * Fires when the body element is scrolled
36886          * @param {Number} scrollLeft
36887          * @param {Number} scrollTop
36888          */
36889         "bodyscroll" : true,
36890         /**
36891          * @event columnresize
36892          * Fires when the user resizes a column
36893          * @param {Number} columnIndex
36894          * @param {Number} newSize
36895          */
36896         "columnresize" : true,
36897         /**
36898          * @event columnmove
36899          * Fires when the user moves a column
36900          * @param {Number} oldIndex
36901          * @param {Number} newIndex
36902          */
36903         "columnmove" : true,
36904         /**
36905          * @event startdrag
36906          * Fires when row(s) start being dragged
36907          * @param {Grid} this
36908          * @param {Roo.GridDD} dd The drag drop object
36909          * @param {event} e The raw browser event
36910          */
36911         "startdrag" : true,
36912         /**
36913          * @event enddrag
36914          * Fires when a drag operation is complete
36915          * @param {Grid} this
36916          * @param {Roo.GridDD} dd The drag drop object
36917          * @param {event} e The raw browser event
36918          */
36919         "enddrag" : true,
36920         /**
36921          * @event dragdrop
36922          * Fires when dragged row(s) are dropped on a valid DD target
36923          * @param {Grid} this
36924          * @param {Roo.GridDD} dd The drag drop object
36925          * @param {String} targetId The target drag drop object
36926          * @param {event} e The raw browser event
36927          */
36928         "dragdrop" : true,
36929         /**
36930          * @event dragover
36931          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36932          * @param {Grid} this
36933          * @param {Roo.GridDD} dd The drag drop object
36934          * @param {String} targetId The target drag drop object
36935          * @param {event} e The raw browser event
36936          */
36937         "dragover" : true,
36938         /**
36939          * @event dragenter
36940          *  Fires when the dragged row(s) first cross another DD target while being dragged
36941          * @param {Grid} this
36942          * @param {Roo.GridDD} dd The drag drop object
36943          * @param {String} targetId The target drag drop object
36944          * @param {event} e The raw browser event
36945          */
36946         "dragenter" : true,
36947         /**
36948          * @event dragout
36949          * Fires when the dragged row(s) leave another DD target while being dragged
36950          * @param {Grid} this
36951          * @param {Roo.GridDD} dd The drag drop object
36952          * @param {String} targetId The target drag drop object
36953          * @param {event} e The raw browser event
36954          */
36955         "dragout" : true,
36956         /**
36957          * @event rowclass
36958          * Fires when a row is rendered, so you can change add a style to it.
36959          * @param {GridView} gridview   The grid view
36960          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36961          */
36962         'rowclass' : true,
36963
36964         /**
36965          * @event render
36966          * Fires when the grid is rendered
36967          * @param {Grid} grid
36968          */
36969         'render' : true,
36970             /**
36971              * @event select
36972              * Fires when a date is selected
36973              * @param {DatePicker} this
36974              * @param {Date} date The selected date
36975              */
36976         'select': true,
36977         /**
36978              * @event monthchange
36979              * Fires when the displayed month changes 
36980              * @param {DatePicker} this
36981              * @param {Date} date The selected month
36982              */
36983         'monthchange': true,
36984         /**
36985              * @event evententer
36986              * Fires when mouse over an event
36987              * @param {Calendar} this
36988              * @param {event} Event
36989              */
36990         'evententer': true,
36991         /**
36992              * @event eventleave
36993              * Fires when the mouse leaves an
36994              * @param {Calendar} this
36995              * @param {event}
36996              */
36997         'eventleave': true,
36998         /**
36999              * @event eventclick
37000              * Fires when the mouse click an
37001              * @param {Calendar} this
37002              * @param {event}
37003              */
37004         'eventclick': true,
37005         /**
37006              * @event eventrender
37007              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37008              * @param {Calendar} this
37009              * @param {data} data to be modified
37010              */
37011         'eventrender': true
37012         
37013     });
37014
37015     Roo.grid.Grid.superclass.constructor.call(this);
37016     this.on('render', function() {
37017         this.view.el.addClass('x-grid-cal'); 
37018         
37019         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37020
37021     },this);
37022     
37023     if (!Roo.grid.Calendar.style) {
37024         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37025             
37026             
37027             '.x-grid-cal .x-grid-col' :  {
37028                 height: 'auto !important',
37029                 'vertical-align': 'top'
37030             },
37031             '.x-grid-cal  .fc-event-hori' : {
37032                 height: '14px'
37033             }
37034              
37035             
37036         }, Roo.id());
37037     }
37038
37039     
37040     
37041 };
37042 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37043     /**
37044      * @cfg {Store} eventStore The store that loads events.
37045      */
37046     eventStore : 25,
37047
37048      
37049     activeDate : false,
37050     startDay : 0,
37051     autoWidth : true,
37052     monitorWindowResize : false,
37053
37054     
37055     resizeColumns : function() {
37056         var col = (this.view.el.getWidth() / 7) - 3;
37057         // loop through cols, and setWidth
37058         for(var i =0 ; i < 7 ; i++){
37059             this.cm.setColumnWidth(i, col);
37060         }
37061     },
37062      setDate :function(date) {
37063         
37064         Roo.log('setDate?');
37065         
37066         this.resizeColumns();
37067         var vd = this.activeDate;
37068         this.activeDate = date;
37069 //        if(vd && this.el){
37070 //            var t = date.getTime();
37071 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37072 //                Roo.log('using add remove');
37073 //                
37074 //                this.fireEvent('monthchange', this, date);
37075 //                
37076 //                this.cells.removeClass("fc-state-highlight");
37077 //                this.cells.each(function(c){
37078 //                   if(c.dateValue == t){
37079 //                       c.addClass("fc-state-highlight");
37080 //                       setTimeout(function(){
37081 //                            try{c.dom.firstChild.focus();}catch(e){}
37082 //                       }, 50);
37083 //                       return false;
37084 //                   }
37085 //                   return true;
37086 //                });
37087 //                return;
37088 //            }
37089 //        }
37090         
37091         var days = date.getDaysInMonth();
37092         
37093         var firstOfMonth = date.getFirstDateOfMonth();
37094         var startingPos = firstOfMonth.getDay()-this.startDay;
37095         
37096         if(startingPos < this.startDay){
37097             startingPos += 7;
37098         }
37099         
37100         var pm = date.add(Date.MONTH, -1);
37101         var prevStart = pm.getDaysInMonth()-startingPos;
37102 //        
37103         
37104         
37105         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37106         
37107         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37108         //this.cells.addClassOnOver('fc-state-hover');
37109         
37110         var cells = this.cells.elements;
37111         var textEls = this.textNodes;
37112         
37113         //Roo.each(cells, function(cell){
37114         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37115         //});
37116         
37117         days += startingPos;
37118
37119         // convert everything to numbers so it's fast
37120         var day = 86400000;
37121         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37122         //Roo.log(d);
37123         //Roo.log(pm);
37124         //Roo.log(prevStart);
37125         
37126         var today = new Date().clearTime().getTime();
37127         var sel = date.clearTime().getTime();
37128         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37129         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37130         var ddMatch = this.disabledDatesRE;
37131         var ddText = this.disabledDatesText;
37132         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37133         var ddaysText = this.disabledDaysText;
37134         var format = this.format;
37135         
37136         var setCellClass = function(cal, cell){
37137             
37138             //Roo.log('set Cell Class');
37139             cell.title = "";
37140             var t = d.getTime();
37141             
37142             //Roo.log(d);
37143             
37144             
37145             cell.dateValue = t;
37146             if(t == today){
37147                 cell.className += " fc-today";
37148                 cell.className += " fc-state-highlight";
37149                 cell.title = cal.todayText;
37150             }
37151             if(t == sel){
37152                 // disable highlight in other month..
37153                 cell.className += " fc-state-highlight";
37154                 
37155             }
37156             // disabling
37157             if(t < min) {
37158                 //cell.className = " fc-state-disabled";
37159                 cell.title = cal.minText;
37160                 return;
37161             }
37162             if(t > max) {
37163                 //cell.className = " fc-state-disabled";
37164                 cell.title = cal.maxText;
37165                 return;
37166             }
37167             if(ddays){
37168                 if(ddays.indexOf(d.getDay()) != -1){
37169                     // cell.title = ddaysText;
37170                    // cell.className = " fc-state-disabled";
37171                 }
37172             }
37173             if(ddMatch && format){
37174                 var fvalue = d.dateFormat(format);
37175                 if(ddMatch.test(fvalue)){
37176                     cell.title = ddText.replace("%0", fvalue);
37177                    cell.className = " fc-state-disabled";
37178                 }
37179             }
37180             
37181             if (!cell.initialClassName) {
37182                 cell.initialClassName = cell.dom.className;
37183             }
37184             
37185             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37186         };
37187
37188         var i = 0;
37189         
37190         for(; i < startingPos; i++) {
37191             cells[i].dayName =  (++prevStart);
37192             Roo.log(textEls[i]);
37193             d.setDate(d.getDate()+1);
37194             
37195             //cells[i].className = "fc-past fc-other-month";
37196             setCellClass(this, cells[i]);
37197         }
37198         
37199         var intDay = 0;
37200         
37201         for(; i < days; i++){
37202             intDay = i - startingPos + 1;
37203             cells[i].dayName =  (intDay);
37204             d.setDate(d.getDate()+1);
37205             
37206             cells[i].className = ''; // "x-date-active";
37207             setCellClass(this, cells[i]);
37208         }
37209         var extraDays = 0;
37210         
37211         for(; i < 42; i++) {
37212             //textEls[i].innerHTML = (++extraDays);
37213             
37214             d.setDate(d.getDate()+1);
37215             cells[i].dayName = (++extraDays);
37216             cells[i].className = "fc-future fc-other-month";
37217             setCellClass(this, cells[i]);
37218         }
37219         
37220         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37221         
37222         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37223         
37224         // this will cause all the cells to mis
37225         var rows= [];
37226         var i =0;
37227         for (var r = 0;r < 6;r++) {
37228             for (var c =0;c < 7;c++) {
37229                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37230             }    
37231         }
37232         
37233         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37234         for(i=0;i<cells.length;i++) {
37235             
37236             this.cells.elements[i].dayName = cells[i].dayName ;
37237             this.cells.elements[i].className = cells[i].className;
37238             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37239             this.cells.elements[i].title = cells[i].title ;
37240             this.cells.elements[i].dateValue = cells[i].dateValue ;
37241         }
37242         
37243         
37244         
37245         
37246         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37247         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37248         
37249         ////if(totalRows != 6){
37250             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37251            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37252        // }
37253         
37254         this.fireEvent('monthchange', this, date);
37255         
37256         
37257     },
37258  /**
37259      * Returns the grid's SelectionModel.
37260      * @return {SelectionModel}
37261      */
37262     getSelectionModel : function(){
37263         if(!this.selModel){
37264             this.selModel = new Roo.grid.CellSelectionModel();
37265         }
37266         return this.selModel;
37267     },
37268
37269     load: function() {
37270         this.eventStore.load()
37271         
37272         
37273         
37274     },
37275     
37276     findCell : function(dt) {
37277         dt = dt.clearTime().getTime();
37278         var ret = false;
37279         this.cells.each(function(c){
37280             //Roo.log("check " +c.dateValue + '?=' + dt);
37281             if(c.dateValue == dt){
37282                 ret = c;
37283                 return false;
37284             }
37285             return true;
37286         });
37287         
37288         return ret;
37289     },
37290     
37291     findCells : function(rec) {
37292         var s = rec.data.start_dt.clone().clearTime().getTime();
37293        // Roo.log(s);
37294         var e= rec.data.end_dt.clone().clearTime().getTime();
37295        // Roo.log(e);
37296         var ret = [];
37297         this.cells.each(function(c){
37298              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37299             
37300             if(c.dateValue > e){
37301                 return ;
37302             }
37303             if(c.dateValue < s){
37304                 return ;
37305             }
37306             ret.push(c);
37307         });
37308         
37309         return ret;    
37310     },
37311     
37312     findBestRow: function(cells)
37313     {
37314         var ret = 0;
37315         
37316         for (var i =0 ; i < cells.length;i++) {
37317             ret  = Math.max(cells[i].rows || 0,ret);
37318         }
37319         return ret;
37320         
37321     },
37322     
37323     
37324     addItem : function(rec)
37325     {
37326         // look for vertical location slot in
37327         var cells = this.findCells(rec);
37328         
37329         rec.row = this.findBestRow(cells);
37330         
37331         // work out the location.
37332         
37333         var crow = false;
37334         var rows = [];
37335         for(var i =0; i < cells.length; i++) {
37336             if (!crow) {
37337                 crow = {
37338                     start : cells[i],
37339                     end :  cells[i]
37340                 };
37341                 continue;
37342             }
37343             if (crow.start.getY() == cells[i].getY()) {
37344                 // on same row.
37345                 crow.end = cells[i];
37346                 continue;
37347             }
37348             // different row.
37349             rows.push(crow);
37350             crow = {
37351                 start: cells[i],
37352                 end : cells[i]
37353             };
37354             
37355         }
37356         
37357         rows.push(crow);
37358         rec.els = [];
37359         rec.rows = rows;
37360         rec.cells = cells;
37361         for (var i = 0; i < cells.length;i++) {
37362             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37363             
37364         }
37365         
37366         
37367     },
37368     
37369     clearEvents: function() {
37370         
37371         if (!this.eventStore.getCount()) {
37372             return;
37373         }
37374         // reset number of rows in cells.
37375         Roo.each(this.cells.elements, function(c){
37376             c.rows = 0;
37377         });
37378         
37379         this.eventStore.each(function(e) {
37380             this.clearEvent(e);
37381         },this);
37382         
37383     },
37384     
37385     clearEvent : function(ev)
37386     {
37387         if (ev.els) {
37388             Roo.each(ev.els, function(el) {
37389                 el.un('mouseenter' ,this.onEventEnter, this);
37390                 el.un('mouseleave' ,this.onEventLeave, this);
37391                 el.remove();
37392             },this);
37393             ev.els = [];
37394         }
37395     },
37396     
37397     
37398     renderEvent : function(ev,ctr) {
37399         if (!ctr) {
37400              ctr = this.view.el.select('.fc-event-container',true).first();
37401         }
37402         
37403          
37404         this.clearEvent(ev);
37405             //code
37406        
37407         
37408         
37409         ev.els = [];
37410         var cells = ev.cells;
37411         var rows = ev.rows;
37412         this.fireEvent('eventrender', this, ev);
37413         
37414         for(var i =0; i < rows.length; i++) {
37415             
37416             cls = '';
37417             if (i == 0) {
37418                 cls += ' fc-event-start';
37419             }
37420             if ((i+1) == rows.length) {
37421                 cls += ' fc-event-end';
37422             }
37423             
37424             //Roo.log(ev.data);
37425             // how many rows should it span..
37426             var cg = this.eventTmpl.append(ctr,Roo.apply({
37427                 fccls : cls
37428                 
37429             }, ev.data) , true);
37430             
37431             
37432             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37433             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37434             cg.on('click', this.onEventClick, this, ev);
37435             
37436             ev.els.push(cg);
37437             
37438             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37439             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37440             //Roo.log(cg);
37441              
37442             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37443             cg.setWidth(ebox.right - sbox.x -2);
37444         }
37445     },
37446     
37447     renderEvents: function()
37448     {   
37449         // first make sure there is enough space..
37450         
37451         if (!this.eventTmpl) {
37452             this.eventTmpl = new Roo.Template(
37453                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37454                     '<div class="fc-event-inner">' +
37455                         '<span class="fc-event-time">{time}</span>' +
37456                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37457                     '</div>' +
37458                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37459                 '</div>'
37460             );
37461                 
37462         }
37463                
37464         
37465         
37466         this.cells.each(function(c) {
37467             //Roo.log(c.select('.fc-day-content div',true).first());
37468             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37469         });
37470         
37471         var ctr = this.view.el.select('.fc-event-container',true).first();
37472         
37473         var cls;
37474         this.eventStore.each(function(ev){
37475             
37476             this.renderEvent(ev);
37477              
37478              
37479         }, this);
37480         this.view.layout();
37481         
37482     },
37483     
37484     onEventEnter: function (e, el,event,d) {
37485         this.fireEvent('evententer', this, el, event);
37486     },
37487     
37488     onEventLeave: function (e, el,event,d) {
37489         this.fireEvent('eventleave', this, el, event);
37490     },
37491     
37492     onEventClick: function (e, el,event,d) {
37493         this.fireEvent('eventclick', this, el, event);
37494     },
37495     
37496     onMonthChange: function () {
37497         this.store.load();
37498     },
37499     
37500     onLoad: function () {
37501         
37502         //Roo.log('calendar onload');
37503 //         
37504         if(this.eventStore.getCount() > 0){
37505             
37506            
37507             
37508             this.eventStore.each(function(d){
37509                 
37510                 
37511                 // FIXME..
37512                 var add =   d.data;
37513                 if (typeof(add.end_dt) == 'undefined')  {
37514                     Roo.log("Missing End time in calendar data: ");
37515                     Roo.log(d);
37516                     return;
37517                 }
37518                 if (typeof(add.start_dt) == 'undefined')  {
37519                     Roo.log("Missing Start time in calendar data: ");
37520                     Roo.log(d);
37521                     return;
37522                 }
37523                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37524                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37525                 add.id = add.id || d.id;
37526                 add.title = add.title || '??';
37527                 
37528                 this.addItem(d);
37529                 
37530              
37531             },this);
37532         }
37533         
37534         this.renderEvents();
37535     }
37536     
37537
37538 });
37539 /*
37540  grid : {
37541                 xtype: 'Grid',
37542                 xns: Roo.grid,
37543                 listeners : {
37544                     render : function ()
37545                     {
37546                         _this.grid = this;
37547                         
37548                         if (!this.view.el.hasClass('course-timesheet')) {
37549                             this.view.el.addClass('course-timesheet');
37550                         }
37551                         if (this.tsStyle) {
37552                             this.ds.load({});
37553                             return; 
37554                         }
37555                         Roo.log('width');
37556                         Roo.log(_this.grid.view.el.getWidth());
37557                         
37558                         
37559                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37560                             '.course-timesheet .x-grid-row' : {
37561                                 height: '80px'
37562                             },
37563                             '.x-grid-row td' : {
37564                                 'vertical-align' : 0
37565                             },
37566                             '.course-edit-link' : {
37567                                 'color' : 'blue',
37568                                 'text-overflow' : 'ellipsis',
37569                                 'overflow' : 'hidden',
37570                                 'white-space' : 'nowrap',
37571                                 'cursor' : 'pointer'
37572                             },
37573                             '.sub-link' : {
37574                                 'color' : 'green'
37575                             },
37576                             '.de-act-sup-link' : {
37577                                 'color' : 'purple',
37578                                 'text-decoration' : 'line-through'
37579                             },
37580                             '.de-act-link' : {
37581                                 'color' : 'red',
37582                                 'text-decoration' : 'line-through'
37583                             },
37584                             '.course-timesheet .course-highlight' : {
37585                                 'border-top-style': 'dashed !important',
37586                                 'border-bottom-bottom': 'dashed !important'
37587                             },
37588                             '.course-timesheet .course-item' : {
37589                                 'font-family'   : 'tahoma, arial, helvetica',
37590                                 'font-size'     : '11px',
37591                                 'overflow'      : 'hidden',
37592                                 'padding-left'  : '10px',
37593                                 'padding-right' : '10px',
37594                                 'padding-top' : '10px' 
37595                             }
37596                             
37597                         }, Roo.id());
37598                                 this.ds.load({});
37599                     }
37600                 },
37601                 autoWidth : true,
37602                 monitorWindowResize : false,
37603                 cellrenderer : function(v,x,r)
37604                 {
37605                     return v;
37606                 },
37607                 sm : {
37608                     xtype: 'CellSelectionModel',
37609                     xns: Roo.grid
37610                 },
37611                 dataSource : {
37612                     xtype: 'Store',
37613                     xns: Roo.data,
37614                     listeners : {
37615                         beforeload : function (_self, options)
37616                         {
37617                             options.params = options.params || {};
37618                             options.params._month = _this.monthField.getValue();
37619                             options.params.limit = 9999;
37620                             options.params['sort'] = 'when_dt';    
37621                             options.params['dir'] = 'ASC';    
37622                             this.proxy.loadResponse = this.loadResponse;
37623                             Roo.log("load?");
37624                             //this.addColumns();
37625                         },
37626                         load : function (_self, records, options)
37627                         {
37628                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37629                                 // if you click on the translation.. you can edit it...
37630                                 var el = Roo.get(this);
37631                                 var id = el.dom.getAttribute('data-id');
37632                                 var d = el.dom.getAttribute('data-date');
37633                                 var t = el.dom.getAttribute('data-time');
37634                                 //var id = this.child('span').dom.textContent;
37635                                 
37636                                 //Roo.log(this);
37637                                 Pman.Dialog.CourseCalendar.show({
37638                                     id : id,
37639                                     when_d : d,
37640                                     when_t : t,
37641                                     productitem_active : id ? 1 : 0
37642                                 }, function() {
37643                                     _this.grid.ds.load({});
37644                                 });
37645                            
37646                            });
37647                            
37648                            _this.panel.fireEvent('resize', [ '', '' ]);
37649                         }
37650                     },
37651                     loadResponse : function(o, success, response){
37652                             // this is overridden on before load..
37653                             
37654                             Roo.log("our code?");       
37655                             //Roo.log(success);
37656                             //Roo.log(response)
37657                             delete this.activeRequest;
37658                             if(!success){
37659                                 this.fireEvent("loadexception", this, o, response);
37660                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37661                                 return;
37662                             }
37663                             var result;
37664                             try {
37665                                 result = o.reader.read(response);
37666                             }catch(e){
37667                                 Roo.log("load exception?");
37668                                 this.fireEvent("loadexception", this, o, response, e);
37669                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37670                                 return;
37671                             }
37672                             Roo.log("ready...");        
37673                             // loop through result.records;
37674                             // and set this.tdate[date] = [] << array of records..
37675                             _this.tdata  = {};
37676                             Roo.each(result.records, function(r){
37677                                 //Roo.log(r.data);
37678                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37679                                     _this.tdata[r.data.when_dt.format('j')] = [];
37680                                 }
37681                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37682                             });
37683                             
37684                             //Roo.log(_this.tdata);
37685                             
37686                             result.records = [];
37687                             result.totalRecords = 6;
37688                     
37689                             // let's generate some duumy records for the rows.
37690                             //var st = _this.dateField.getValue();
37691                             
37692                             // work out monday..
37693                             //st = st.add(Date.DAY, -1 * st.format('w'));
37694                             
37695                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37696                             
37697                             var firstOfMonth = date.getFirstDayOfMonth();
37698                             var days = date.getDaysInMonth();
37699                             var d = 1;
37700                             var firstAdded = false;
37701                             for (var i = 0; i < result.totalRecords ; i++) {
37702                                 //var d= st.add(Date.DAY, i);
37703                                 var row = {};
37704                                 var added = 0;
37705                                 for(var w = 0 ; w < 7 ; w++){
37706                                     if(!firstAdded && firstOfMonth != w){
37707                                         continue;
37708                                     }
37709                                     if(d > days){
37710                                         continue;
37711                                     }
37712                                     firstAdded = true;
37713                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
37714                                     row['weekday'+w] = String.format(
37715                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
37716                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
37717                                                     d,
37718                                                     date.format('Y-m-')+dd
37719                                                 );
37720                                     added++;
37721                                     if(typeof(_this.tdata[d]) != 'undefined'){
37722                                         Roo.each(_this.tdata[d], function(r){
37723                                             var is_sub = '';
37724                                             var deactive = '';
37725                                             var id = r.id;
37726                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
37727                                             if(r.parent_id*1>0){
37728                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
37729                                                 id = r.parent_id;
37730                                             }
37731                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
37732                                                 deactive = 'de-act-link';
37733                                             }
37734                                             
37735                                             row['weekday'+w] += String.format(
37736                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
37737                                                     id, //0
37738                                                     r.product_id_name, //1
37739                                                     r.when_dt.format('h:ia'), //2
37740                                                     is_sub, //3
37741                                                     deactive, //4
37742                                                     desc // 5
37743                                             );
37744                                         });
37745                                     }
37746                                     d++;
37747                                 }
37748                                 
37749                                 // only do this if something added..
37750                                 if(added > 0){ 
37751                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
37752                                 }
37753                                 
37754                                 
37755                                 // push it twice. (second one with an hour..
37756                                 
37757                             }
37758                             //Roo.log(result);
37759                             this.fireEvent("load", this, o, o.request.arg);
37760                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
37761                         },
37762                     sortInfo : {field: 'when_dt', direction : 'ASC' },
37763                     proxy : {
37764                         xtype: 'HttpProxy',
37765                         xns: Roo.data,
37766                         method : 'GET',
37767                         url : baseURL + '/Roo/Shop_course.php'
37768                     },
37769                     reader : {
37770                         xtype: 'JsonReader',
37771                         xns: Roo.data,
37772                         id : 'id',
37773                         fields : [
37774                             {
37775                                 'name': 'id',
37776                                 'type': 'int'
37777                             },
37778                             {
37779                                 'name': 'when_dt',
37780                                 'type': 'string'
37781                             },
37782                             {
37783                                 'name': 'end_dt',
37784                                 'type': 'string'
37785                             },
37786                             {
37787                                 'name': 'parent_id',
37788                                 'type': 'int'
37789                             },
37790                             {
37791                                 'name': 'product_id',
37792                                 'type': 'int'
37793                             },
37794                             {
37795                                 'name': 'productitem_id',
37796                                 'type': 'int'
37797                             },
37798                             {
37799                                 'name': 'guid',
37800                                 'type': 'int'
37801                             }
37802                         ]
37803                     }
37804                 },
37805                 toolbar : {
37806                     xtype: 'Toolbar',
37807                     xns: Roo,
37808                     items : [
37809                         {
37810                             xtype: 'Button',
37811                             xns: Roo.Toolbar,
37812                             listeners : {
37813                                 click : function (_self, e)
37814                                 {
37815                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37816                                     sd.setMonth(sd.getMonth()-1);
37817                                     _this.monthField.setValue(sd.format('Y-m-d'));
37818                                     _this.grid.ds.load({});
37819                                 }
37820                             },
37821                             text : "Back"
37822                         },
37823                         {
37824                             xtype: 'Separator',
37825                             xns: Roo.Toolbar
37826                         },
37827                         {
37828                             xtype: 'MonthField',
37829                             xns: Roo.form,
37830                             listeners : {
37831                                 render : function (_self)
37832                                 {
37833                                     _this.monthField = _self;
37834                                    // _this.monthField.set  today
37835                                 },
37836                                 select : function (combo, date)
37837                                 {
37838                                     _this.grid.ds.load({});
37839                                 }
37840                             },
37841                             value : (function() { return new Date(); })()
37842                         },
37843                         {
37844                             xtype: 'Separator',
37845                             xns: Roo.Toolbar
37846                         },
37847                         {
37848                             xtype: 'TextItem',
37849                             xns: Roo.Toolbar,
37850                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
37851                         },
37852                         {
37853                             xtype: 'Fill',
37854                             xns: Roo.Toolbar
37855                         },
37856                         {
37857                             xtype: 'Button',
37858                             xns: Roo.Toolbar,
37859                             listeners : {
37860                                 click : function (_self, e)
37861                                 {
37862                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37863                                     sd.setMonth(sd.getMonth()+1);
37864                                     _this.monthField.setValue(sd.format('Y-m-d'));
37865                                     _this.grid.ds.load({});
37866                                 }
37867                             },
37868                             text : "Next"
37869                         }
37870                     ]
37871                 },
37872                  
37873             }
37874         };
37875         
37876         *//*
37877  * Based on:
37878  * Ext JS Library 1.1.1
37879  * Copyright(c) 2006-2007, Ext JS, LLC.
37880  *
37881  * Originally Released Under LGPL - original licence link has changed is not relivant.
37882  *
37883  * Fork - LGPL
37884  * <script type="text/javascript">
37885  */
37886  
37887 /**
37888  * @class Roo.LoadMask
37889  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37890  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37891  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37892  * element's UpdateManager load indicator and will be destroyed after the initial load.
37893  * @constructor
37894  * Create a new LoadMask
37895  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37896  * @param {Object} config The config object
37897  */
37898 Roo.LoadMask = function(el, config){
37899     this.el = Roo.get(el);
37900     Roo.apply(this, config);
37901     if(this.store){
37902         this.store.on('beforeload', this.onBeforeLoad, this);
37903         this.store.on('load', this.onLoad, this);
37904         this.store.on('loadexception', this.onLoadException, this);
37905         this.removeMask = false;
37906     }else{
37907         var um = this.el.getUpdateManager();
37908         um.showLoadIndicator = false; // disable the default indicator
37909         um.on('beforeupdate', this.onBeforeLoad, this);
37910         um.on('update', this.onLoad, this);
37911         um.on('failure', this.onLoad, this);
37912         this.removeMask = true;
37913     }
37914 };
37915
37916 Roo.LoadMask.prototype = {
37917     /**
37918      * @cfg {Boolean} removeMask
37919      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37920      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37921      */
37922     /**
37923      * @cfg {String} msg
37924      * The text to display in a centered loading message box (defaults to 'Loading...')
37925      */
37926     msg : 'Loading...',
37927     /**
37928      * @cfg {String} msgCls
37929      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37930      */
37931     msgCls : 'x-mask-loading',
37932
37933     /**
37934      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37935      * @type Boolean
37936      */
37937     disabled: false,
37938
37939     /**
37940      * Disables the mask to prevent it from being displayed
37941      */
37942     disable : function(){
37943        this.disabled = true;
37944     },
37945
37946     /**
37947      * Enables the mask so that it can be displayed
37948      */
37949     enable : function(){
37950         this.disabled = false;
37951     },
37952     
37953     onLoadException : function()
37954     {
37955         Roo.log(arguments);
37956         
37957         if (typeof(arguments[3]) != 'undefined') {
37958             Roo.MessageBox.alert("Error loading",arguments[3]);
37959         } 
37960         /*
37961         try {
37962             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37963                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37964             }   
37965         } catch(e) {
37966             
37967         }
37968         */
37969     
37970         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37971     },
37972     // private
37973     onLoad : function()
37974     {
37975         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37976     },
37977
37978     // private
37979     onBeforeLoad : function(){
37980         if(!this.disabled){
37981             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
37982         }
37983     },
37984
37985     // private
37986     destroy : function(){
37987         if(this.store){
37988             this.store.un('beforeload', this.onBeforeLoad, this);
37989             this.store.un('load', this.onLoad, this);
37990             this.store.un('loadexception', this.onLoadException, this);
37991         }else{
37992             var um = this.el.getUpdateManager();
37993             um.un('beforeupdate', this.onBeforeLoad, this);
37994             um.un('update', this.onLoad, this);
37995             um.un('failure', this.onLoad, this);
37996         }
37997     }
37998 };/*
37999  * Based on:
38000  * Ext JS Library 1.1.1
38001  * Copyright(c) 2006-2007, Ext JS, LLC.
38002  *
38003  * Originally Released Under LGPL - original licence link has changed is not relivant.
38004  *
38005  * Fork - LGPL
38006  * <script type="text/javascript">
38007  */
38008
38009
38010 /**
38011  * @class Roo.XTemplate
38012  * @extends Roo.Template
38013  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38014 <pre><code>
38015 var t = new Roo.XTemplate(
38016         '&lt;select name="{name}"&gt;',
38017                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38018         '&lt;/select&gt;'
38019 );
38020  
38021 // then append, applying the master template values
38022  </code></pre>
38023  *
38024  * Supported features:
38025  *
38026  *  Tags:
38027
38028 <pre><code>
38029       {a_variable} - output encoded.
38030       {a_variable.format:("Y-m-d")} - call a method on the variable
38031       {a_variable:raw} - unencoded output
38032       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38033       {a_variable:this.method_on_template(...)} - call a method on the template object.
38034  
38035 </code></pre>
38036  *  The tpl tag:
38037 <pre><code>
38038         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38039         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38040         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38041         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38042   
38043         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38044         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38045 </code></pre>
38046  *      
38047  */
38048 Roo.XTemplate = function()
38049 {
38050     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38051     if (this.html) {
38052         this.compile();
38053     }
38054 };
38055
38056
38057 Roo.extend(Roo.XTemplate, Roo.Template, {
38058
38059     /**
38060      * The various sub templates
38061      */
38062     tpls : false,
38063     /**
38064      *
38065      * basic tag replacing syntax
38066      * WORD:WORD()
38067      *
38068      * // you can fake an object call by doing this
38069      *  x.t:(test,tesT) 
38070      * 
38071      */
38072     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38073
38074     /**
38075      * compile the template
38076      *
38077      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38078      *
38079      */
38080     compile: function()
38081     {
38082         var s = this.html;
38083      
38084         s = ['<tpl>', s, '</tpl>'].join('');
38085     
38086         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38087             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38088             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38089             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38090             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38091             m,
38092             id     = 0,
38093             tpls   = [];
38094     
38095         while(true == !!(m = s.match(re))){
38096             var forMatch   = m[0].match(nameRe),
38097                 ifMatch   = m[0].match(ifRe),
38098                 execMatch   = m[0].match(execRe),
38099                 namedMatch   = m[0].match(namedRe),
38100                 
38101                 exp  = null, 
38102                 fn   = null,
38103                 exec = null,
38104                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38105                 
38106             if (ifMatch) {
38107                 // if - puts fn into test..
38108                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38109                 if(exp){
38110                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38111                 }
38112             }
38113             
38114             if (execMatch) {
38115                 // exec - calls a function... returns empty if true is  returned.
38116                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38117                 if(exp){
38118                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38119                 }
38120             }
38121             
38122             
38123             if (name) {
38124                 // for = 
38125                 switch(name){
38126                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38127                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38128                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38129                 }
38130             }
38131             var uid = namedMatch ? namedMatch[1] : id;
38132             
38133             
38134             tpls.push({
38135                 id:     namedMatch ? namedMatch[1] : id,
38136                 target: name,
38137                 exec:   exec,
38138                 test:   fn,
38139                 body:   m[1] || ''
38140             });
38141             if (namedMatch) {
38142                 s = s.replace(m[0], '');
38143             } else { 
38144                 s = s.replace(m[0], '{xtpl'+ id + '}');
38145             }
38146             ++id;
38147         }
38148         this.tpls = [];
38149         for(var i = tpls.length-1; i >= 0; --i){
38150             this.compileTpl(tpls[i]);
38151             this.tpls[tpls[i].id] = tpls[i];
38152         }
38153         this.master = tpls[tpls.length-1];
38154         return this;
38155     },
38156     /**
38157      * same as applyTemplate, except it's done to one of the subTemplates
38158      * when using named templates, you can do:
38159      *
38160      * var str = pl.applySubTemplate('your-name', values);
38161      *
38162      * 
38163      * @param {Number} id of the template
38164      * @param {Object} values to apply to template
38165      * @param {Object} parent (normaly the instance of this object)
38166      */
38167     applySubTemplate : function(id, values, parent)
38168     {
38169         
38170         
38171         var t = this.tpls[id];
38172         
38173         
38174         try { 
38175             if(t.test && !t.test.call(this, values, parent)){
38176                 return '';
38177             }
38178         } catch(e) {
38179             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38180             Roo.log(e.toString());
38181             Roo.log(t.test);
38182             return ''
38183         }
38184         try { 
38185             
38186             if(t.exec && t.exec.call(this, values, parent)){
38187                 return '';
38188             }
38189         } catch(e) {
38190             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38191             Roo.log(e.toString());
38192             Roo.log(t.exec);
38193             return ''
38194         }
38195         try {
38196             var vs = t.target ? t.target.call(this, values, parent) : values;
38197             parent = t.target ? values : parent;
38198             if(t.target && vs instanceof Array){
38199                 var buf = [];
38200                 for(var i = 0, len = vs.length; i < len; i++){
38201                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38202                 }
38203                 return buf.join('');
38204             }
38205             return t.compiled.call(this, vs, parent);
38206         } catch (e) {
38207             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38208             Roo.log(e.toString());
38209             Roo.log(t.compiled);
38210             return '';
38211         }
38212     },
38213
38214     compileTpl : function(tpl)
38215     {
38216         var fm = Roo.util.Format;
38217         var useF = this.disableFormats !== true;
38218         var sep = Roo.isGecko ? "+" : ",";
38219         var undef = function(str) {
38220             Roo.log("Property not found :"  + str);
38221             return '';
38222         };
38223         
38224         var fn = function(m, name, format, args)
38225         {
38226             //Roo.log(arguments);
38227             args = args ? args.replace(/\\'/g,"'") : args;
38228             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38229             if (typeof(format) == 'undefined') {
38230                 format= 'htmlEncode';
38231             }
38232             if (format == 'raw' ) {
38233                 format = false;
38234             }
38235             
38236             if(name.substr(0, 4) == 'xtpl'){
38237                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38238             }
38239             
38240             // build an array of options to determine if value is undefined..
38241             
38242             // basically get 'xxxx.yyyy' then do
38243             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38244             //    (function () { Roo.log("Property not found"); return ''; })() :
38245             //    ......
38246             
38247             var udef_ar = [];
38248             var lookfor = '';
38249             Roo.each(name.split('.'), function(st) {
38250                 lookfor += (lookfor.length ? '.': '') + st;
38251                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38252             });
38253             
38254             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38255             
38256             
38257             if(format && useF){
38258                 
38259                 args = args ? ',' + args : "";
38260                  
38261                 if(format.substr(0, 5) != "this."){
38262                     format = "fm." + format + '(';
38263                 }else{
38264                     format = 'this.call("'+ format.substr(5) + '", ';
38265                     args = ", values";
38266                 }
38267                 
38268                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38269             }
38270              
38271             if (args.length) {
38272                 // called with xxyx.yuu:(test,test)
38273                 // change to ()
38274                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38275             }
38276             // raw.. - :raw modifier..
38277             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38278             
38279         };
38280         var body;
38281         // branched to use + in gecko and [].join() in others
38282         if(Roo.isGecko){
38283             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38284                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38285                     "';};};";
38286         }else{
38287             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38288             body.push(tpl.body.replace(/(\r\n|\n)/g,
38289                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38290             body.push("'].join('');};};");
38291             body = body.join('');
38292         }
38293         
38294         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38295        
38296         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38297         eval(body);
38298         
38299         return this;
38300     },
38301
38302     applyTemplate : function(values){
38303         return this.master.compiled.call(this, values, {});
38304         //var s = this.subs;
38305     },
38306
38307     apply : function(){
38308         return this.applyTemplate.apply(this, arguments);
38309     }
38310
38311  });
38312
38313 Roo.XTemplate.from = function(el){
38314     el = Roo.getDom(el);
38315     return new Roo.XTemplate(el.value || el.innerHTML);
38316 };