Fix #6696 - Document Browse
[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      * using 'cn' the nested child reader read the child array into it's child stores.
790      * @param {Object} rec The record with a 'children array
791      */
792     loadDataFromChildren : function(rec)
793     {
794         this.loadData(this.reader.toLoadData(rec));
795     },
796     
797
798     /**
799      * Gets the number of cached records.
800      * <p>
801      * <em>If using paging, this may not be the total size of the dataset. If the data object
802      * used by the Reader contains the dataset size, then the getTotalCount() function returns
803      * the data set size</em>
804      */
805     getCount : function(){
806         return this.data.length || 0;
807     },
808
809     /**
810      * Gets the total number of records in the dataset as returned by the server.
811      * <p>
812      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
813      * the dataset size</em>
814      */
815     getTotalCount : function(){
816         return this.totalLength || 0;
817     },
818
819     /**
820      * Returns the sort state of the Store as an object with two properties:
821      * <pre><code>
822  field {String} The name of the field by which the Records are sorted
823  direction {String} The sort order, "ASC" or "DESC"
824      * </code></pre>
825      */
826     getSortState : function(){
827         return this.sortInfo;
828     },
829
830     // private
831     applySort : function(){
832         if(this.sortInfo && !this.remoteSort){
833             var s = this.sortInfo, f = s.field;
834             var st = this.fields.get(f).sortType;
835             var fn = function(r1, r2){
836                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
837                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
838             };
839             this.data.sort(s.direction, fn);
840             if(this.snapshot && this.snapshot != this.data){
841                 this.snapshot.sort(s.direction, fn);
842             }
843         }
844     },
845
846     /**
847      * Sets the default sort column and order to be used by the next load operation.
848      * @param {String} fieldName The name of the field to sort by.
849      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
850      */
851     setDefaultSort : function(field, dir){
852         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
853     },
854
855     /**
856      * Sort the Records.
857      * If remote sorting is used, the sort is performed on the server, and the cache is
858      * reloaded. If local sorting is used, the cache is sorted internally.
859      * @param {String} fieldName The name of the field to sort by.
860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
861      */
862     sort : function(fieldName, dir){
863         var f = this.fields.get(fieldName);
864         if(!dir){
865             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
866             
867             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
868                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
869             }else{
870                 dir = f.sortDir;
871             }
872         }
873         this.sortToggle[f.name] = dir;
874         this.sortInfo = {field: f.name, direction: dir};
875         if(!this.remoteSort){
876             this.applySort();
877             this.fireEvent("datachanged", this);
878         }else{
879             this.load(this.lastOptions);
880         }
881     },
882
883     /**
884      * Calls the specified function for each of the Records in the cache.
885      * @param {Function} fn The function to call. The Record is passed as the first parameter.
886      * Returning <em>false</em> aborts and exits the iteration.
887      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
888      */
889     each : function(fn, scope){
890         this.data.each(fn, scope);
891     },
892
893     /**
894      * Gets all records modified since the last commit.  Modified records are persisted across load operations
895      * (e.g., during paging).
896      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
897      */
898     getModifiedRecords : function(){
899         return this.modified;
900     },
901
902     // private
903     createFilterFn : function(property, value, anyMatch){
904         if(!value.exec){ // not a regex
905             value = String(value);
906             if(value.length == 0){
907                 return false;
908             }
909             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
910         }
911         return function(r){
912             return value.test(r.data[property]);
913         };
914     },
915
916     /**
917      * Sums the value of <i>property</i> for each record between start and end and returns the result.
918      * @param {String} property A field on your records
919      * @param {Number} start The record index to start at (defaults to 0)
920      * @param {Number} end The last record index to include (defaults to length - 1)
921      * @return {Number} The sum
922      */
923     sum : function(property, start, end){
924         var rs = this.data.items, v = 0;
925         start = start || 0;
926         end = (end || end === 0) ? end : rs.length-1;
927
928         for(var i = start; i <= end; i++){
929             v += (rs[i].data[property] || 0);
930         }
931         return v;
932     },
933
934     /**
935      * Filter the records by a specified property.
936      * @param {String} field A field on your records
937      * @param {String/RegExp} value Either a string that the field
938      * should start with or a RegExp to test against the field
939      * @param {Boolean} anyMatch True to match any part not just the beginning
940      */
941     filter : function(property, value, anyMatch){
942         var fn = this.createFilterFn(property, value, anyMatch);
943         return fn ? this.filterBy(fn) : this.clearFilter();
944     },
945
946     /**
947      * Filter by a function. The specified function will be called with each
948      * record in this data source. If the function returns true the record is included,
949      * otherwise it is filtered.
950      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
951      * @param {Object} scope (optional) The scope of the function (defaults to this)
952      */
953     filterBy : function(fn, scope){
954         this.snapshot = this.snapshot || this.data;
955         this.data = this.queryBy(fn, scope||this);
956         this.fireEvent("datachanged", this);
957     },
958
959     /**
960      * Query the records by a specified property.
961      * @param {String} field A field on your records
962      * @param {String/RegExp} value Either a string that the field
963      * should start with or a RegExp to test against the field
964      * @param {Boolean} anyMatch True to match any part not just the beginning
965      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
966      */
967     query : function(property, value, anyMatch){
968         var fn = this.createFilterFn(property, value, anyMatch);
969         return fn ? this.queryBy(fn) : this.data.clone();
970     },
971
972     /**
973      * Query by a function. The specified function will be called with each
974      * record in this data source. If the function returns true the record is included
975      * in the results.
976      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
977      * @param {Object} scope (optional) The scope of the function (defaults to this)
978       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
979      **/
980     queryBy : function(fn, scope){
981         var data = this.snapshot || this.data;
982         return data.filterBy(fn, scope||this);
983     },
984
985     /**
986      * Collects unique values for a particular dataIndex from this store.
987      * @param {String} dataIndex The property to collect
988      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
989      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
990      * @return {Array} An array of the unique values
991      **/
992     collect : function(dataIndex, allowNull, bypassFilter){
993         var d = (bypassFilter === true && this.snapshot) ?
994                 this.snapshot.items : this.data.items;
995         var v, sv, r = [], l = {};
996         for(var i = 0, len = d.length; i < len; i++){
997             v = d[i].data[dataIndex];
998             sv = String(v);
999             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1000                 l[sv] = true;
1001                 r[r.length] = v;
1002             }
1003         }
1004         return r;
1005     },
1006
1007     /**
1008      * Revert to a view of the Record cache with no filtering applied.
1009      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1010      */
1011     clearFilter : function(suppressEvent){
1012         if(this.snapshot && this.snapshot != this.data){
1013             this.data = this.snapshot;
1014             delete this.snapshot;
1015             if(suppressEvent !== true){
1016                 this.fireEvent("datachanged", this);
1017             }
1018         }
1019     },
1020
1021     // private
1022     afterEdit : function(record){
1023         if(this.modified.indexOf(record) == -1){
1024             this.modified.push(record);
1025         }
1026         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1027     },
1028     
1029     // private
1030     afterReject : function(record){
1031         this.modified.remove(record);
1032         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1033     },
1034
1035     // private
1036     afterCommit : function(record){
1037         this.modified.remove(record);
1038         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1039     },
1040
1041     /**
1042      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1043      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1044      */
1045     commitChanges : function(){
1046         var m = this.modified.slice(0);
1047         this.modified = [];
1048         for(var i = 0, len = m.length; i < len; i++){
1049             m[i].commit();
1050         }
1051     },
1052
1053     /**
1054      * Cancel outstanding changes on all changed records.
1055      */
1056     rejectChanges : function(){
1057         var m = this.modified.slice(0);
1058         this.modified = [];
1059         for(var i = 0, len = m.length; i < len; i++){
1060             m[i].reject();
1061         }
1062     },
1063
1064     onMetaChange : function(meta, rtype, o){
1065         this.recordType = rtype;
1066         this.fields = rtype.prototype.fields;
1067         delete this.snapshot;
1068         this.sortInfo = meta.sortInfo || this.sortInfo;
1069         this.modified = [];
1070         this.fireEvent('metachange', this, this.reader.meta);
1071     },
1072     
1073     moveIndex : function(data, type)
1074     {
1075         var index = this.indexOf(data);
1076         
1077         var newIndex = index + type;
1078         
1079         this.remove(data);
1080         
1081         this.insert(newIndex, data);
1082         
1083     }
1084 });/*
1085  * Based on:
1086  * Ext JS Library 1.1.1
1087  * Copyright(c) 2006-2007, Ext JS, LLC.
1088  *
1089  * Originally Released Under LGPL - original licence link has changed is not relivant.
1090  *
1091  * Fork - LGPL
1092  * <script type="text/javascript">
1093  */
1094
1095 /**
1096  * @class Roo.data.SimpleStore
1097  * @extends Roo.data.Store
1098  * Small helper class to make creating Stores from Array data easier.
1099  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1100  * @cfg {Array} fields An array of field definition objects, or field name strings.
1101  * @cfg {Object} an existing reader (eg. copied from another store)
1102  * @cfg {Array} data The multi-dimensional array of data
1103  * @constructor
1104  * @param {Object} config
1105  */
1106 Roo.data.SimpleStore = function(config)
1107 {
1108     Roo.data.SimpleStore.superclass.constructor.call(this, {
1109         isLocal : true,
1110         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1111                 id: config.id
1112             },
1113             Roo.data.Record.create(config.fields)
1114         ),
1115         proxy : new Roo.data.MemoryProxy(config.data)
1116     });
1117     this.load();
1118 };
1119 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1120  * Based on:
1121  * Ext JS Library 1.1.1
1122  * Copyright(c) 2006-2007, Ext JS, LLC.
1123  *
1124  * Originally Released Under LGPL - original licence link has changed is not relivant.
1125  *
1126  * Fork - LGPL
1127  * <script type="text/javascript">
1128  */
1129
1130 /**
1131 /**
1132  * @extends Roo.data.Store
1133  * @class Roo.data.JsonStore
1134  * Small helper class to make creating Stores for JSON data easier. <br/>
1135 <pre><code>
1136 var store = new Roo.data.JsonStore({
1137     url: 'get-images.php',
1138     root: 'images',
1139     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1140 });
1141 </code></pre>
1142  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1143  * JsonReader and HttpProxy (unless inline data is provided).</b>
1144  * @cfg {Array} fields An array of field definition objects, or field name strings.
1145  * @constructor
1146  * @param {Object} config
1147  */
1148 Roo.data.JsonStore = function(c){
1149     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1150         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1151         reader: new Roo.data.JsonReader(c, c.fields)
1152     }));
1153 };
1154 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1155  * Based on:
1156  * Ext JS Library 1.1.1
1157  * Copyright(c) 2006-2007, Ext JS, LLC.
1158  *
1159  * Originally Released Under LGPL - original licence link has changed is not relivant.
1160  *
1161  * Fork - LGPL
1162  * <script type="text/javascript">
1163  */
1164
1165  
1166 Roo.data.Field = function(config){
1167     if(typeof config == "string"){
1168         config = {name: config};
1169     }
1170     Roo.apply(this, config);
1171     
1172     if(!this.type){
1173         this.type = "auto";
1174     }
1175     
1176     var st = Roo.data.SortTypes;
1177     // named sortTypes are supported, here we look them up
1178     if(typeof this.sortType == "string"){
1179         this.sortType = st[this.sortType];
1180     }
1181     
1182     // set default sortType for strings and dates
1183     if(!this.sortType){
1184         switch(this.type){
1185             case "string":
1186                 this.sortType = st.asUCString;
1187                 break;
1188             case "date":
1189                 this.sortType = st.asDate;
1190                 break;
1191             default:
1192                 this.sortType = st.none;
1193         }
1194     }
1195
1196     // define once
1197     var stripRe = /[\$,%]/g;
1198
1199     // prebuilt conversion function for this field, instead of
1200     // switching every time we're reading a value
1201     if(!this.convert){
1202         var cv, dateFormat = this.dateFormat;
1203         switch(this.type){
1204             case "":
1205             case "auto":
1206             case undefined:
1207                 cv = function(v){ return v; };
1208                 break;
1209             case "string":
1210                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1211                 break;
1212             case "int":
1213                 cv = function(v){
1214                     return v !== undefined && v !== null && v !== '' ?
1215                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1216                     };
1217                 break;
1218             case "float":
1219                 cv = function(v){
1220                     return v !== undefined && v !== null && v !== '' ?
1221                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1222                     };
1223                 break;
1224             case "bool":
1225             case "boolean":
1226                 cv = function(v){ return v === true || v === "true" || v == 1; };
1227                 break;
1228             case "date":
1229                 cv = function(v){
1230                     if(!v){
1231                         return '';
1232                     }
1233                     if(v instanceof Date){
1234                         return v;
1235                     }
1236                     if(dateFormat){
1237                         if(dateFormat == "timestamp"){
1238                             return new Date(v*1000);
1239                         }
1240                         return Date.parseDate(v, dateFormat);
1241                     }
1242                     var parsed = Date.parse(v);
1243                     return parsed ? new Date(parsed) : null;
1244                 };
1245              break;
1246             
1247         }
1248         this.convert = cv;
1249     }
1250 };
1251
1252 Roo.data.Field.prototype = {
1253     dateFormat: null,
1254     defaultValue: "",
1255     mapping: null,
1256     sortType : null,
1257     sortDir : "ASC"
1258 };/*
1259  * Based on:
1260  * Ext JS Library 1.1.1
1261  * Copyright(c) 2006-2007, Ext JS, LLC.
1262  *
1263  * Originally Released Under LGPL - original licence link has changed is not relivant.
1264  *
1265  * Fork - LGPL
1266  * <script type="text/javascript">
1267  */
1268  
1269 // Base class for reading structured data from a data source.  This class is intended to be
1270 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1271
1272 /**
1273  * @class Roo.data.DataReader
1274  * Base class for reading structured data from a data source.  This class is intended to be
1275  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1276  */
1277
1278 Roo.data.DataReader = function(meta, recordType){
1279     
1280     this.meta = meta;
1281     
1282     this.recordType = recordType instanceof Array ? 
1283         Roo.data.Record.create(recordType) : recordType;
1284 };
1285
1286 Roo.data.DataReader.prototype = {
1287     
1288     
1289     readerType : 'Data',
1290      /**
1291      * Create an empty record
1292      * @param {Object} data (optional) - overlay some values
1293      * @return {Roo.data.Record} record created.
1294      */
1295     newRow :  function(d) {
1296         var da =  {};
1297         this.recordType.prototype.fields.each(function(c) {
1298             switch( c.type) {
1299                 case 'int' : da[c.name] = 0; break;
1300                 case 'date' : da[c.name] = new Date(); break;
1301                 case 'float' : da[c.name] = 0.0; break;
1302                 case 'boolean' : da[c.name] = false; break;
1303                 default : da[c.name] = ""; break;
1304             }
1305             
1306         });
1307         return new this.recordType(Roo.apply(da, d));
1308     }
1309     
1310     
1311 };/*
1312  * Based on:
1313  * Ext JS Library 1.1.1
1314  * Copyright(c) 2006-2007, Ext JS, LLC.
1315  *
1316  * Originally Released Under LGPL - original licence link has changed is not relivant.
1317  *
1318  * Fork - LGPL
1319  * <script type="text/javascript">
1320  */
1321
1322 /**
1323  * @class Roo.data.DataProxy
1324  * @extends Roo.data.Observable
1325  * This class is an abstract base class for implementations which provide retrieval of
1326  * unformatted data objects.<br>
1327  * <p>
1328  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1329  * (of the appropriate type which knows how to parse the data object) to provide a block of
1330  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1331  * <p>
1332  * Custom implementations must implement the load method as described in
1333  * {@link Roo.data.HttpProxy#load}.
1334  */
1335 Roo.data.DataProxy = function(){
1336     this.addEvents({
1337         /**
1338          * @event beforeload
1339          * Fires before a network request is made to retrieve a data object.
1340          * @param {Object} This DataProxy object.
1341          * @param {Object} params The params parameter to the load function.
1342          */
1343         beforeload : true,
1344         /**
1345          * @event load
1346          * Fires before the load method's callback is called.
1347          * @param {Object} This DataProxy object.
1348          * @param {Object} o The data object.
1349          * @param {Object} arg The callback argument object passed to the load function.
1350          */
1351         load : true,
1352         /**
1353          * @event loadexception
1354          * Fires if an Exception occurs during data retrieval.
1355          * @param {Object} This DataProxy object.
1356          * @param {Object} o The data object.
1357          * @param {Object} arg The callback argument object passed to the load function.
1358          * @param {Object} e The Exception.
1359          */
1360         loadexception : true
1361     });
1362     Roo.data.DataProxy.superclass.constructor.call(this);
1363 };
1364
1365 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1366
1367     /**
1368      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1369      */
1370 /*
1371  * Based on:
1372  * Ext JS Library 1.1.1
1373  * Copyright(c) 2006-2007, Ext JS, LLC.
1374  *
1375  * Originally Released Under LGPL - original licence link has changed is not relivant.
1376  *
1377  * Fork - LGPL
1378  * <script type="text/javascript">
1379  */
1380 /**
1381  * @class Roo.data.MemoryProxy
1382  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1383  * to the Reader when its load method is called.
1384  * @constructor
1385  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1386  */
1387 Roo.data.MemoryProxy = function(data){
1388     if (data.data) {
1389         data = data.data;
1390     }
1391     Roo.data.MemoryProxy.superclass.constructor.call(this);
1392     this.data = data;
1393 };
1394
1395 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1396     
1397     /**
1398      * Load data from the requested source (in this case an in-memory
1399      * data object passed to the constructor), read the data object into
1400      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1401      * process that block using the passed callback.
1402      * @param {Object} params This parameter is not used by the MemoryProxy class.
1403      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1404      * object into a block of Roo.data.Records.
1405      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1406      * The function must be passed <ul>
1407      * <li>The Record block object</li>
1408      * <li>The "arg" argument from the load function</li>
1409      * <li>A boolean success indicator</li>
1410      * </ul>
1411      * @param {Object} scope The scope in which to call the callback
1412      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1413      */
1414     load : function(params, reader, callback, scope, arg){
1415         params = params || {};
1416         var result;
1417         try {
1418             result = reader.readRecords(params.data ? params.data :this.data);
1419         }catch(e){
1420             this.fireEvent("loadexception", this, arg, null, e);
1421             callback.call(scope, null, arg, false);
1422             return;
1423         }
1424         callback.call(scope, result, arg, true);
1425     },
1426     
1427     // private
1428     update : function(params, records){
1429         
1430     }
1431 });/*
1432  * Based on:
1433  * Ext JS Library 1.1.1
1434  * Copyright(c) 2006-2007, Ext JS, LLC.
1435  *
1436  * Originally Released Under LGPL - original licence link has changed is not relivant.
1437  *
1438  * Fork - LGPL
1439  * <script type="text/javascript">
1440  */
1441 /**
1442  * @class Roo.data.HttpProxy
1443  * @extends Roo.data.DataProxy
1444  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1445  * configured to reference a certain URL.<br><br>
1446  * <p>
1447  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1448  * from which the running page was served.<br><br>
1449  * <p>
1450  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1451  * <p>
1452  * Be aware that to enable the browser to parse an XML document, the server must set
1453  * the Content-Type header in the HTTP response to "text/xml".
1454  * @constructor
1455  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1456  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1457  * will be used to make the request.
1458  */
1459 Roo.data.HttpProxy = function(conn){
1460     Roo.data.HttpProxy.superclass.constructor.call(this);
1461     // is conn a conn config or a real conn?
1462     this.conn = conn;
1463     this.useAjax = !conn || !conn.events;
1464   
1465 };
1466
1467 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1468     // thse are take from connection...
1469     
1470     /**
1471      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1472      */
1473     /**
1474      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1475      * extra parameters to each request made by this object. (defaults to undefined)
1476      */
1477     /**
1478      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1479      *  to each request made by this object. (defaults to undefined)
1480      */
1481     /**
1482      * @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)
1483      */
1484     /**
1485      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1486      */
1487      /**
1488      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1489      * @type Boolean
1490      */
1491   
1492
1493     /**
1494      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1495      * @type Boolean
1496      */
1497     /**
1498      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1499      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1500      * a finer-grained basis than the DataProxy events.
1501      */
1502     getConnection : function(){
1503         return this.useAjax ? Roo.Ajax : this.conn;
1504     },
1505
1506     /**
1507      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1508      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1509      * process that block using the passed callback.
1510      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1511      * for the request to the remote server.
1512      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1513      * object into a block of Roo.data.Records.
1514      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1515      * The function must be passed <ul>
1516      * <li>The Record block object</li>
1517      * <li>The "arg" argument from the load function</li>
1518      * <li>A boolean success indicator</li>
1519      * </ul>
1520      * @param {Object} scope The scope in which to call the callback
1521      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1522      */
1523     load : function(params, reader, callback, scope, arg){
1524         if(this.fireEvent("beforeload", this, params) !== false){
1525             var  o = {
1526                 params : params || {},
1527                 request: {
1528                     callback : callback,
1529                     scope : scope,
1530                     arg : arg
1531                 },
1532                 reader: reader,
1533                 callback : this.loadResponse,
1534                 scope: this
1535             };
1536             if(this.useAjax){
1537                 Roo.applyIf(o, this.conn);
1538                 if(this.activeRequest){
1539                     Roo.Ajax.abort(this.activeRequest);
1540                 }
1541                 this.activeRequest = Roo.Ajax.request(o);
1542             }else{
1543                 this.conn.request(o);
1544             }
1545         }else{
1546             callback.call(scope||this, null, arg, false);
1547         }
1548     },
1549
1550     // private
1551     loadResponse : function(o, success, response){
1552         delete this.activeRequest;
1553         if(!success){
1554             this.fireEvent("loadexception", this, o, response);
1555             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1556             return;
1557         }
1558         var result;
1559         try {
1560             result = o.reader.read(response);
1561         }catch(e){
1562             this.fireEvent("loadexception", this, o, response, e);
1563             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1564             return;
1565         }
1566         
1567         this.fireEvent("load", this, o, o.request.arg);
1568         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1569     },
1570
1571     // private
1572     update : function(dataSet){
1573
1574     },
1575
1576     // private
1577     updateResponse : function(dataSet){
1578
1579     }
1580 });/*
1581  * Based on:
1582  * Ext JS Library 1.1.1
1583  * Copyright(c) 2006-2007, Ext JS, LLC.
1584  *
1585  * Originally Released Under LGPL - original licence link has changed is not relivant.
1586  *
1587  * Fork - LGPL
1588  * <script type="text/javascript">
1589  */
1590
1591 /**
1592  * @class Roo.data.ScriptTagProxy
1593  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1594  * other than the originating domain of the running page.<br><br>
1595  * <p>
1596  * <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
1597  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1598  * <p>
1599  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1600  * source code that is used as the source inside a &lt;script> tag.<br><br>
1601  * <p>
1602  * In order for the browser to process the returned data, the server must wrap the data object
1603  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1604  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1605  * depending on whether the callback name was passed:
1606  * <p>
1607  * <pre><code>
1608 boolean scriptTag = false;
1609 String cb = request.getParameter("callback");
1610 if (cb != null) {
1611     scriptTag = true;
1612     response.setContentType("text/javascript");
1613 } else {
1614     response.setContentType("application/x-json");
1615 }
1616 Writer out = response.getWriter();
1617 if (scriptTag) {
1618     out.write(cb + "(");
1619 }
1620 out.print(dataBlock.toJsonString());
1621 if (scriptTag) {
1622     out.write(");");
1623 }
1624 </pre></code>
1625  *
1626  * @constructor
1627  * @param {Object} config A configuration object.
1628  */
1629 Roo.data.ScriptTagProxy = function(config){
1630     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1631     Roo.apply(this, config);
1632     this.head = document.getElementsByTagName("head")[0];
1633 };
1634
1635 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1636
1637 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1638     /**
1639      * @cfg {String} url The URL from which to request the data object.
1640      */
1641     /**
1642      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1643      */
1644     timeout : 30000,
1645     /**
1646      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1647      * the server the name of the callback function set up by the load call to process the returned data object.
1648      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1649      * javascript output which calls this named function passing the data object as its only parameter.
1650      */
1651     callbackParam : "callback",
1652     /**
1653      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1654      * name to the request.
1655      */
1656     nocache : true,
1657
1658     /**
1659      * Load data from the configured URL, read the data object into
1660      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1661      * process that block using the passed callback.
1662      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1663      * for the request to the remote server.
1664      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1665      * object into a block of Roo.data.Records.
1666      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1667      * The function must be passed <ul>
1668      * <li>The Record block object</li>
1669      * <li>The "arg" argument from the load function</li>
1670      * <li>A boolean success indicator</li>
1671      * </ul>
1672      * @param {Object} scope The scope in which to call the callback
1673      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1674      */
1675     load : function(params, reader, callback, scope, arg){
1676         if(this.fireEvent("beforeload", this, params) !== false){
1677
1678             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1679
1680             var url = this.url;
1681             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1682             if(this.nocache){
1683                 url += "&_dc=" + (new Date().getTime());
1684             }
1685             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1686             var trans = {
1687                 id : transId,
1688                 cb : "stcCallback"+transId,
1689                 scriptId : "stcScript"+transId,
1690                 params : params,
1691                 arg : arg,
1692                 url : url,
1693                 callback : callback,
1694                 scope : scope,
1695                 reader : reader
1696             };
1697             var conn = this;
1698
1699             window[trans.cb] = function(o){
1700                 conn.handleResponse(o, trans);
1701             };
1702
1703             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1704
1705             if(this.autoAbort !== false){
1706                 this.abort();
1707             }
1708
1709             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1710
1711             var script = document.createElement("script");
1712             script.setAttribute("src", url);
1713             script.setAttribute("type", "text/javascript");
1714             script.setAttribute("id", trans.scriptId);
1715             this.head.appendChild(script);
1716
1717             this.trans = trans;
1718         }else{
1719             callback.call(scope||this, null, arg, false);
1720         }
1721     },
1722
1723     // private
1724     isLoading : function(){
1725         return this.trans ? true : false;
1726     },
1727
1728     /**
1729      * Abort the current server request.
1730      */
1731     abort : function(){
1732         if(this.isLoading()){
1733             this.destroyTrans(this.trans);
1734         }
1735     },
1736
1737     // private
1738     destroyTrans : function(trans, isLoaded){
1739         this.head.removeChild(document.getElementById(trans.scriptId));
1740         clearTimeout(trans.timeoutId);
1741         if(isLoaded){
1742             window[trans.cb] = undefined;
1743             try{
1744                 delete window[trans.cb];
1745             }catch(e){}
1746         }else{
1747             // if hasn't been loaded, wait for load to remove it to prevent script error
1748             window[trans.cb] = function(){
1749                 window[trans.cb] = undefined;
1750                 try{
1751                     delete window[trans.cb];
1752                 }catch(e){}
1753             };
1754         }
1755     },
1756
1757     // private
1758     handleResponse : function(o, trans){
1759         this.trans = false;
1760         this.destroyTrans(trans, true);
1761         var result;
1762         try {
1763             result = trans.reader.readRecords(o);
1764         }catch(e){
1765             this.fireEvent("loadexception", this, o, trans.arg, e);
1766             trans.callback.call(trans.scope||window, null, trans.arg, false);
1767             return;
1768         }
1769         this.fireEvent("load", this, o, trans.arg);
1770         trans.callback.call(trans.scope||window, result, trans.arg, true);
1771     },
1772
1773     // private
1774     handleFailure : function(trans){
1775         this.trans = false;
1776         this.destroyTrans(trans, false);
1777         this.fireEvent("loadexception", this, null, trans.arg);
1778         trans.callback.call(trans.scope||window, null, trans.arg, false);
1779     }
1780 });/*
1781  * Based on:
1782  * Ext JS Library 1.1.1
1783  * Copyright(c) 2006-2007, Ext JS, LLC.
1784  *
1785  * Originally Released Under LGPL - original licence link has changed is not relivant.
1786  *
1787  * Fork - LGPL
1788  * <script type="text/javascript">
1789  */
1790
1791 /**
1792  * @class Roo.data.JsonReader
1793  * @extends Roo.data.DataReader
1794  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1795  * based on mappings in a provided Roo.data.Record constructor.
1796  * 
1797  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1798  * in the reply previously. 
1799  * 
1800  * <p>
1801  * Example code:
1802  * <pre><code>
1803 var RecordDef = Roo.data.Record.create([
1804     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1805     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1806 ]);
1807 var myReader = new Roo.data.JsonReader({
1808     totalProperty: "results",    // The property which contains the total dataset size (optional)
1809     root: "rows",                // The property which contains an Array of row objects
1810     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1811 }, RecordDef);
1812 </code></pre>
1813  * <p>
1814  * This would consume a JSON file like this:
1815  * <pre><code>
1816 { 'results': 2, 'rows': [
1817     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1818     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1819 }
1820 </code></pre>
1821  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1822  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1823  * paged from the remote server.
1824  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1825  * @cfg {String} root name of the property which contains the Array of row objects.
1826  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1827  * @cfg {Array} fields Array of field definition objects
1828  * @constructor
1829  * Create a new JsonReader
1830  * @param {Object} meta Metadata configuration options
1831  * @param {Object} recordType Either an Array of field definition objects,
1832  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1833  */
1834 Roo.data.JsonReader = function(meta, recordType){
1835     
1836     meta = meta || {};
1837     // set some defaults:
1838     Roo.applyIf(meta, {
1839         totalProperty: 'total',
1840         successProperty : 'success',
1841         root : 'data',
1842         id : 'id'
1843     });
1844     
1845     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1846 };
1847 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1848     
1849     readerType : 'Json',
1850     
1851     /**
1852      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1853      * Used by Store query builder to append _requestMeta to params.
1854      * 
1855      */
1856     metaFromRemote : false,
1857     /**
1858      * This method is only used by a DataProxy which has retrieved data from a remote server.
1859      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1860      * @return {Object} data A data block which is used by an Roo.data.Store object as
1861      * a cache of Roo.data.Records.
1862      */
1863     read : function(response){
1864         var json = response.responseText;
1865        
1866         var o = /* eval:var:o */ eval("("+json+")");
1867         if(!o) {
1868             throw {message: "JsonReader.read: Json object not found"};
1869         }
1870         
1871         if(o.metaData){
1872             
1873             delete this.ef;
1874             this.metaFromRemote = true;
1875             this.meta = o.metaData;
1876             this.recordType = Roo.data.Record.create(o.metaData.fields);
1877             this.onMetaChange(this.meta, this.recordType, o);
1878         }
1879         return this.readRecords(o);
1880     },
1881
1882     // private function a store will implement
1883     onMetaChange : function(meta, recordType, o){
1884
1885     },
1886
1887     /**
1888          * @ignore
1889          */
1890     simpleAccess: function(obj, subsc) {
1891         return obj[subsc];
1892     },
1893
1894         /**
1895          * @ignore
1896          */
1897     getJsonAccessor: function(){
1898         var re = /[\[\.]/;
1899         return function(expr) {
1900             try {
1901                 return(re.test(expr))
1902                     ? new Function("obj", "return obj." + expr)
1903                     : function(obj){
1904                         return obj[expr];
1905                     };
1906             } catch(e){}
1907             return Roo.emptyFn;
1908         };
1909     }(),
1910
1911     /**
1912      * Create a data block containing Roo.data.Records from an XML document.
1913      * @param {Object} o An object which contains an Array of row objects in the property specified
1914      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1915      * which contains the total size of the dataset.
1916      * @return {Object} data A data block which is used by an Roo.data.Store object as
1917      * a cache of Roo.data.Records.
1918      */
1919     readRecords : function(o){
1920         /**
1921          * After any data loads, the raw JSON data is available for further custom processing.
1922          * @type Object
1923          */
1924         this.o = o;
1925         var s = this.meta, Record = this.recordType,
1926             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1927
1928 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1929         if (!this.ef) {
1930             if(s.totalProperty) {
1931                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1932                 }
1933                 if(s.successProperty) {
1934                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1935                 }
1936                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1937                 if (s.id) {
1938                         var g = this.getJsonAccessor(s.id);
1939                         this.getId = function(rec) {
1940                                 var r = g(rec);  
1941                                 return (r === undefined || r === "") ? null : r;
1942                         };
1943                 } else {
1944                         this.getId = function(){return null;};
1945                 }
1946             this.ef = [];
1947             for(var jj = 0; jj < fl; jj++){
1948                 f = fi[jj];
1949                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1950                 this.ef[jj] = this.getJsonAccessor(map);
1951             }
1952         }
1953
1954         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1955         if(s.totalProperty){
1956             var vt = parseInt(this.getTotal(o), 10);
1957             if(!isNaN(vt)){
1958                 totalRecords = vt;
1959             }
1960         }
1961         if(s.successProperty){
1962             var vs = this.getSuccess(o);
1963             if(vs === false || vs === 'false'){
1964                 success = false;
1965             }
1966         }
1967         var records = [];
1968         for(var i = 0; i < c; i++){
1969                 var n = root[i];
1970             var values = {};
1971             var id = this.getId(n);
1972             for(var j = 0; j < fl; j++){
1973                 f = fi[j];
1974             var v = this.ef[j](n);
1975             if (!f.convert) {
1976                 Roo.log('missing convert for ' + f.name);
1977                 Roo.log(f);
1978                 continue;
1979             }
1980             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1981             }
1982             var record = new Record(values, id);
1983             record.json = n;
1984             records[i] = record;
1985         }
1986         return {
1987             raw : o,
1988             success : success,
1989             records : records,
1990             totalRecords : totalRecords
1991         };
1992     },
1993     // used when loading children.. @see loadDataFromChildren
1994     toLoadData: function(rec)
1995     {
1996         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
1997         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
1998         return { data : data, total : data.length };
1999         
2000     }
2001 });/*
2002  * Based on:
2003  * Ext JS Library 1.1.1
2004  * Copyright(c) 2006-2007, Ext JS, LLC.
2005  *
2006  * Originally Released Under LGPL - original licence link has changed is not relivant.
2007  *
2008  * Fork - LGPL
2009  * <script type="text/javascript">
2010  */
2011
2012 /**
2013  * @class Roo.data.XmlReader
2014  * @extends Roo.data.DataReader
2015  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2016  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2017  * <p>
2018  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2019  * header in the HTTP response must be set to "text/xml".</em>
2020  * <p>
2021  * Example code:
2022  * <pre><code>
2023 var RecordDef = Roo.data.Record.create([
2024    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2025    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2026 ]);
2027 var myReader = new Roo.data.XmlReader({
2028    totalRecords: "results", // The element which contains the total dataset size (optional)
2029    record: "row",           // The repeated element which contains row information
2030    id: "id"                 // The element within the row that provides an ID for the record (optional)
2031 }, RecordDef);
2032 </code></pre>
2033  * <p>
2034  * This would consume an XML file like this:
2035  * <pre><code>
2036 &lt;?xml?>
2037 &lt;dataset>
2038  &lt;results>2&lt;/results>
2039  &lt;row>
2040    &lt;id>1&lt;/id>
2041    &lt;name>Bill&lt;/name>
2042    &lt;occupation>Gardener&lt;/occupation>
2043  &lt;/row>
2044  &lt;row>
2045    &lt;id>2&lt;/id>
2046    &lt;name>Ben&lt;/name>
2047    &lt;occupation>Horticulturalist&lt;/occupation>
2048  &lt;/row>
2049 &lt;/dataset>
2050 </code></pre>
2051  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2052  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2053  * paged from the remote server.
2054  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2055  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2056  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2057  * a record identifier value.
2058  * @constructor
2059  * Create a new XmlReader
2060  * @param {Object} meta Metadata configuration options
2061  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2062  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2063  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2064  */
2065 Roo.data.XmlReader = function(meta, recordType){
2066     meta = meta || {};
2067     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2068 };
2069 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2070     
2071     readerType : 'Xml',
2072     
2073     /**
2074      * This method is only used by a DataProxy which has retrieved data from a remote server.
2075          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2076          * to contain a method called 'responseXML' that returns an XML document object.
2077      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2078      * a cache of Roo.data.Records.
2079      */
2080     read : function(response){
2081         var doc = response.responseXML;
2082         if(!doc) {
2083             throw {message: "XmlReader.read: XML Document not available"};
2084         }
2085         return this.readRecords(doc);
2086     },
2087
2088     /**
2089      * Create a data block containing Roo.data.Records from an XML document.
2090          * @param {Object} doc A parsed XML document.
2091      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2092      * a cache of Roo.data.Records.
2093      */
2094     readRecords : function(doc){
2095         /**
2096          * After any data loads/reads, the raw XML Document is available for further custom processing.
2097          * @type XMLDocument
2098          */
2099         this.xmlData = doc;
2100         var root = doc.documentElement || doc;
2101         var q = Roo.DomQuery;
2102         var recordType = this.recordType, fields = recordType.prototype.fields;
2103         var sid = this.meta.id;
2104         var totalRecords = 0, success = true;
2105         if(this.meta.totalRecords){
2106             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2107         }
2108         
2109         if(this.meta.success){
2110             var sv = q.selectValue(this.meta.success, root, true);
2111             success = sv !== false && sv !== 'false';
2112         }
2113         var records = [];
2114         var ns = q.select(this.meta.record, root);
2115         for(var i = 0, len = ns.length; i < len; i++) {
2116                 var n = ns[i];
2117                 var values = {};
2118                 var id = sid ? q.selectValue(sid, n) : undefined;
2119                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2120                     var f = fields.items[j];
2121                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2122                     v = f.convert(v);
2123                     values[f.name] = v;
2124                 }
2125                 var record = new recordType(values, id);
2126                 record.node = n;
2127                 records[records.length] = record;
2128             }
2129
2130             return {
2131                 success : success,
2132                 records : records,
2133                 totalRecords : totalRecords || records.length
2134             };
2135     }
2136 });/*
2137  * Based on:
2138  * Ext JS Library 1.1.1
2139  * Copyright(c) 2006-2007, Ext JS, LLC.
2140  *
2141  * Originally Released Under LGPL - original licence link has changed is not relivant.
2142  *
2143  * Fork - LGPL
2144  * <script type="text/javascript">
2145  */
2146
2147 /**
2148  * @class Roo.data.ArrayReader
2149  * @extends Roo.data.DataReader
2150  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2151  * Each element of that Array represents a row of data fields. The
2152  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2153  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2154  * <p>
2155  * Example code:.
2156  * <pre><code>
2157 var RecordDef = Roo.data.Record.create([
2158     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2159     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2160 ]);
2161 var myReader = new Roo.data.ArrayReader({
2162     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2163 }, RecordDef);
2164 </code></pre>
2165  * <p>
2166  * This would consume an Array like this:
2167  * <pre><code>
2168 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2169   </code></pre>
2170  
2171  * @constructor
2172  * Create a new JsonReader
2173  * @param {Object} meta Metadata configuration options.
2174  * @param {Object|Array} recordType Either an Array of field definition objects
2175  * 
2176  * @cfg {Array} fields Array of field definition objects
2177  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2178  * as specified to {@link Roo.data.Record#create},
2179  * or an {@link Roo.data.Record} object
2180  *
2181  * 
2182  * created using {@link Roo.data.Record#create}.
2183  */
2184 Roo.data.ArrayReader = function(meta, recordType)
2185 {    
2186     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2187 };
2188
2189 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2190     
2191       /**
2192      * Create a data block containing Roo.data.Records from an XML document.
2193      * @param {Object} o An Array of row objects which represents the dataset.
2194      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2195      * a cache of Roo.data.Records.
2196      */
2197     readRecords : function(o)
2198     {
2199         var sid = this.meta ? this.meta.id : null;
2200         var recordType = this.recordType, fields = recordType.prototype.fields;
2201         var records = [];
2202         var root = o;
2203         for(var i = 0; i < root.length; i++){
2204             var n = root[i];
2205             var values = {};
2206             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2207             for(var j = 0, jlen = fields.length; j < jlen; j++){
2208                 var f = fields.items[j];
2209                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2210                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2211                 v = f.convert(v);
2212                 values[f.name] = v;
2213             }
2214             var record = new recordType(values, id);
2215             record.json = n;
2216             records[records.length] = record;
2217         }
2218         return {
2219             records : records,
2220             totalRecords : records.length
2221         };
2222     },
2223     // used when loading children.. @see loadDataFromChildren
2224     toLoadData: function(rec)
2225     {
2226         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2227         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2228         
2229     }
2230     
2231     
2232 });/*
2233  * Based on:
2234  * Ext JS Library 1.1.1
2235  * Copyright(c) 2006-2007, Ext JS, LLC.
2236  *
2237  * Originally Released Under LGPL - original licence link has changed is not relivant.
2238  *
2239  * Fork - LGPL
2240  * <script type="text/javascript">
2241  */
2242
2243
2244 /**
2245  * @class Roo.data.Tree
2246  * @extends Roo.util.Observable
2247  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2248  * in the tree have most standard DOM functionality.
2249  * @constructor
2250  * @param {Node} root (optional) The root node
2251  */
2252 Roo.data.Tree = function(root){
2253    this.nodeHash = {};
2254    /**
2255     * The root node for this tree
2256     * @type Node
2257     */
2258    this.root = null;
2259    if(root){
2260        this.setRootNode(root);
2261    }
2262    this.addEvents({
2263        /**
2264         * @event append
2265         * Fires when a new child node is appended to a node in this tree.
2266         * @param {Tree} tree The owner tree
2267         * @param {Node} parent The parent node
2268         * @param {Node} node The newly appended node
2269         * @param {Number} index The index of the newly appended node
2270         */
2271        "append" : true,
2272        /**
2273         * @event remove
2274         * Fires when a child node is removed from a node in this tree.
2275         * @param {Tree} tree The owner tree
2276         * @param {Node} parent The parent node
2277         * @param {Node} node The child node removed
2278         */
2279        "remove" : true,
2280        /**
2281         * @event move
2282         * Fires when a node is moved to a new location in the tree
2283         * @param {Tree} tree The owner tree
2284         * @param {Node} node The node moved
2285         * @param {Node} oldParent The old parent of this node
2286         * @param {Node} newParent The new parent of this node
2287         * @param {Number} index The index it was moved to
2288         */
2289        "move" : true,
2290        /**
2291         * @event insert
2292         * Fires when a new child node is inserted in a node in this tree.
2293         * @param {Tree} tree The owner tree
2294         * @param {Node} parent The parent node
2295         * @param {Node} node The child node inserted
2296         * @param {Node} refNode The child node the node was inserted before
2297         */
2298        "insert" : true,
2299        /**
2300         * @event beforeappend
2301         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2302         * @param {Tree} tree The owner tree
2303         * @param {Node} parent The parent node
2304         * @param {Node} node The child node to be appended
2305         */
2306        "beforeappend" : true,
2307        /**
2308         * @event beforeremove
2309         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2310         * @param {Tree} tree The owner tree
2311         * @param {Node} parent The parent node
2312         * @param {Node} node The child node to be removed
2313         */
2314        "beforeremove" : true,
2315        /**
2316         * @event beforemove
2317         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2318         * @param {Tree} tree The owner tree
2319         * @param {Node} node The node being moved
2320         * @param {Node} oldParent The parent of the node
2321         * @param {Node} newParent The new parent the node is moving to
2322         * @param {Number} index The index it is being moved to
2323         */
2324        "beforemove" : true,
2325        /**
2326         * @event beforeinsert
2327         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2328         * @param {Tree} tree The owner tree
2329         * @param {Node} parent The parent node
2330         * @param {Node} node The child node to be inserted
2331         * @param {Node} refNode The child node the node is being inserted before
2332         */
2333        "beforeinsert" : true
2334    });
2335
2336     Roo.data.Tree.superclass.constructor.call(this);
2337 };
2338
2339 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2340     pathSeparator: "/",
2341
2342     proxyNodeEvent : function(){
2343         return this.fireEvent.apply(this, arguments);
2344     },
2345
2346     /**
2347      * Returns the root node for this tree.
2348      * @return {Node}
2349      */
2350     getRootNode : function(){
2351         return this.root;
2352     },
2353
2354     /**
2355      * Sets the root node for this tree.
2356      * @param {Node} node
2357      * @return {Node}
2358      */
2359     setRootNode : function(node){
2360         this.root = node;
2361         node.ownerTree = this;
2362         node.isRoot = true;
2363         this.registerNode(node);
2364         return node;
2365     },
2366
2367     /**
2368      * Gets a node in this tree by its id.
2369      * @param {String} id
2370      * @return {Node}
2371      */
2372     getNodeById : function(id){
2373         return this.nodeHash[id];
2374     },
2375
2376     registerNode : function(node){
2377         this.nodeHash[node.id] = node;
2378     },
2379
2380     unregisterNode : function(node){
2381         delete this.nodeHash[node.id];
2382     },
2383
2384     toString : function(){
2385         return "[Tree"+(this.id?" "+this.id:"")+"]";
2386     }
2387 });
2388
2389 /**
2390  * @class Roo.data.Node
2391  * @extends Roo.util.Observable
2392  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2393  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2394  * @constructor
2395  * @param {Object} attributes The attributes/config for the node
2396  */
2397 Roo.data.Node = function(attributes){
2398     /**
2399      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2400      * @type {Object}
2401      */
2402     this.attributes = attributes || {};
2403     this.leaf = this.attributes.leaf;
2404     /**
2405      * The node id. @type String
2406      */
2407     this.id = this.attributes.id;
2408     if(!this.id){
2409         this.id = Roo.id(null, "ynode-");
2410         this.attributes.id = this.id;
2411     }
2412      
2413     
2414     /**
2415      * All child nodes of this node. @type Array
2416      */
2417     this.childNodes = [];
2418     if(!this.childNodes.indexOf){ // indexOf is a must
2419         this.childNodes.indexOf = function(o){
2420             for(var i = 0, len = this.length; i < len; i++){
2421                 if(this[i] == o) {
2422                     return i;
2423                 }
2424             }
2425             return -1;
2426         };
2427     }
2428     /**
2429      * The parent node for this node. @type Node
2430      */
2431     this.parentNode = null;
2432     /**
2433      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2434      */
2435     this.firstChild = null;
2436     /**
2437      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2438      */
2439     this.lastChild = null;
2440     /**
2441      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2442      */
2443     this.previousSibling = null;
2444     /**
2445      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2446      */
2447     this.nextSibling = null;
2448
2449     this.addEvents({
2450        /**
2451         * @event append
2452         * Fires when a new child node is appended
2453         * @param {Tree} tree The owner tree
2454         * @param {Node} this This node
2455         * @param {Node} node The newly appended node
2456         * @param {Number} index The index of the newly appended node
2457         */
2458        "append" : true,
2459        /**
2460         * @event remove
2461         * Fires when a child node is removed
2462         * @param {Tree} tree The owner tree
2463         * @param {Node} this This node
2464         * @param {Node} node The removed node
2465         */
2466        "remove" : true,
2467        /**
2468         * @event move
2469         * Fires when this node is moved to a new location in the tree
2470         * @param {Tree} tree The owner tree
2471         * @param {Node} this This node
2472         * @param {Node} oldParent The old parent of this node
2473         * @param {Node} newParent The new parent of this node
2474         * @param {Number} index The index it was moved to
2475         */
2476        "move" : true,
2477        /**
2478         * @event insert
2479         * Fires when a new child node is inserted.
2480         * @param {Tree} tree The owner tree
2481         * @param {Node} this This node
2482         * @param {Node} node The child node inserted
2483         * @param {Node} refNode The child node the node was inserted before
2484         */
2485        "insert" : true,
2486        /**
2487         * @event beforeappend
2488         * Fires before a new child is appended, return false to cancel the append.
2489         * @param {Tree} tree The owner tree
2490         * @param {Node} this This node
2491         * @param {Node} node The child node to be appended
2492         */
2493        "beforeappend" : true,
2494        /**
2495         * @event beforeremove
2496         * Fires before a child is removed, return false to cancel the remove.
2497         * @param {Tree} tree The owner tree
2498         * @param {Node} this This node
2499         * @param {Node} node The child node to be removed
2500         */
2501        "beforeremove" : true,
2502        /**
2503         * @event beforemove
2504         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2505         * @param {Tree} tree The owner tree
2506         * @param {Node} this This node
2507         * @param {Node} oldParent The parent of this node
2508         * @param {Node} newParent The new parent this node is moving to
2509         * @param {Number} index The index it is being moved to
2510         */
2511        "beforemove" : true,
2512        /**
2513         * @event beforeinsert
2514         * Fires before a new child is inserted, return false to cancel the insert.
2515         * @param {Tree} tree The owner tree
2516         * @param {Node} this This node
2517         * @param {Node} node The child node to be inserted
2518         * @param {Node} refNode The child node the node is being inserted before
2519         */
2520        "beforeinsert" : true
2521    });
2522     this.listeners = this.attributes.listeners;
2523     Roo.data.Node.superclass.constructor.call(this);
2524 };
2525
2526 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2527     fireEvent : function(evtName){
2528         // first do standard event for this node
2529         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2530             return false;
2531         }
2532         // then bubble it up to the tree if the event wasn't cancelled
2533         var ot = this.getOwnerTree();
2534         if(ot){
2535             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2536                 return false;
2537             }
2538         }
2539         return true;
2540     },
2541
2542     /**
2543      * Returns true if this node is a leaf
2544      * @return {Boolean}
2545      */
2546     isLeaf : function(){
2547         return this.leaf === true;
2548     },
2549
2550     // private
2551     setFirstChild : function(node){
2552         this.firstChild = node;
2553     },
2554
2555     //private
2556     setLastChild : function(node){
2557         this.lastChild = node;
2558     },
2559
2560
2561     /**
2562      * Returns true if this node is the last child of its parent
2563      * @return {Boolean}
2564      */
2565     isLast : function(){
2566        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2567     },
2568
2569     /**
2570      * Returns true if this node is the first child of its parent
2571      * @return {Boolean}
2572      */
2573     isFirst : function(){
2574        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2575     },
2576
2577     hasChildNodes : function(){
2578         return !this.isLeaf() && this.childNodes.length > 0;
2579     },
2580
2581     /**
2582      * Insert node(s) as the last child node of this node.
2583      * @param {Node/Array} node The node or Array of nodes to append
2584      * @return {Node} The appended node if single append, or null if an array was passed
2585      */
2586     appendChild : function(node){
2587         var multi = false;
2588         if(node instanceof Array){
2589             multi = node;
2590         }else if(arguments.length > 1){
2591             multi = arguments;
2592         }
2593         
2594         // if passed an array or multiple args do them one by one
2595         if(multi){
2596             for(var i = 0, len = multi.length; i < len; i++) {
2597                 this.appendChild(multi[i]);
2598             }
2599         }else{
2600             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2601                 return false;
2602             }
2603             var index = this.childNodes.length;
2604             var oldParent = node.parentNode;
2605             // it's a move, make sure we move it cleanly
2606             if(oldParent){
2607                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2608                     return false;
2609                 }
2610                 oldParent.removeChild(node);
2611             }
2612             
2613             index = this.childNodes.length;
2614             if(index == 0){
2615                 this.setFirstChild(node);
2616             }
2617             this.childNodes.push(node);
2618             node.parentNode = this;
2619             var ps = this.childNodes[index-1];
2620             if(ps){
2621                 node.previousSibling = ps;
2622                 ps.nextSibling = node;
2623             }else{
2624                 node.previousSibling = null;
2625             }
2626             node.nextSibling = null;
2627             this.setLastChild(node);
2628             node.setOwnerTree(this.getOwnerTree());
2629             this.fireEvent("append", this.ownerTree, this, node, index);
2630             if(this.ownerTree) {
2631                 this.ownerTree.fireEvent("appendnode", this, node, index);
2632             }
2633             if(oldParent){
2634                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2635             }
2636             return node;
2637         }
2638     },
2639
2640     /**
2641      * Removes a child node from this node.
2642      * @param {Node} node The node to remove
2643      * @return {Node} The removed node
2644      */
2645     removeChild : function(node){
2646         var index = this.childNodes.indexOf(node);
2647         if(index == -1){
2648             return false;
2649         }
2650         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2651             return false;
2652         }
2653
2654         // remove it from childNodes collection
2655         this.childNodes.splice(index, 1);
2656
2657         // update siblings
2658         if(node.previousSibling){
2659             node.previousSibling.nextSibling = node.nextSibling;
2660         }
2661         if(node.nextSibling){
2662             node.nextSibling.previousSibling = node.previousSibling;
2663         }
2664
2665         // update child refs
2666         if(this.firstChild == node){
2667             this.setFirstChild(node.nextSibling);
2668         }
2669         if(this.lastChild == node){
2670             this.setLastChild(node.previousSibling);
2671         }
2672
2673         node.setOwnerTree(null);
2674         // clear any references from the node
2675         node.parentNode = null;
2676         node.previousSibling = null;
2677         node.nextSibling = null;
2678         this.fireEvent("remove", this.ownerTree, this, node);
2679         return node;
2680     },
2681
2682     /**
2683      * Inserts the first node before the second node in this nodes childNodes collection.
2684      * @param {Node} node The node to insert
2685      * @param {Node} refNode The node to insert before (if null the node is appended)
2686      * @return {Node} The inserted node
2687      */
2688     insertBefore : function(node, refNode){
2689         if(!refNode){ // like standard Dom, refNode can be null for append
2690             return this.appendChild(node);
2691         }
2692         // nothing to do
2693         if(node == refNode){
2694             return false;
2695         }
2696
2697         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2698             return false;
2699         }
2700         var index = this.childNodes.indexOf(refNode);
2701         var oldParent = node.parentNode;
2702         var refIndex = index;
2703
2704         // when moving internally, indexes will change after remove
2705         if(oldParent == this && this.childNodes.indexOf(node) < index){
2706             refIndex--;
2707         }
2708
2709         // it's a move, make sure we move it cleanly
2710         if(oldParent){
2711             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2712                 return false;
2713             }
2714             oldParent.removeChild(node);
2715         }
2716         if(refIndex == 0){
2717             this.setFirstChild(node);
2718         }
2719         this.childNodes.splice(refIndex, 0, node);
2720         node.parentNode = this;
2721         var ps = this.childNodes[refIndex-1];
2722         if(ps){
2723             node.previousSibling = ps;
2724             ps.nextSibling = node;
2725         }else{
2726             node.previousSibling = null;
2727         }
2728         node.nextSibling = refNode;
2729         refNode.previousSibling = node;
2730         node.setOwnerTree(this.getOwnerTree());
2731         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2732         if(oldParent){
2733             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2734         }
2735         return node;
2736     },
2737
2738     /**
2739      * Returns the child node at the specified index.
2740      * @param {Number} index
2741      * @return {Node}
2742      */
2743     item : function(index){
2744         return this.childNodes[index];
2745     },
2746
2747     /**
2748      * Replaces one child node in this node with another.
2749      * @param {Node} newChild The replacement node
2750      * @param {Node} oldChild The node to replace
2751      * @return {Node} The replaced node
2752      */
2753     replaceChild : function(newChild, oldChild){
2754         this.insertBefore(newChild, oldChild);
2755         this.removeChild(oldChild);
2756         return oldChild;
2757     },
2758
2759     /**
2760      * Returns the index of a child node
2761      * @param {Node} node
2762      * @return {Number} The index of the node or -1 if it was not found
2763      */
2764     indexOf : function(child){
2765         return this.childNodes.indexOf(child);
2766     },
2767
2768     /**
2769      * Returns the tree this node is in.
2770      * @return {Tree}
2771      */
2772     getOwnerTree : function(){
2773         // if it doesn't have one, look for one
2774         if(!this.ownerTree){
2775             var p = this;
2776             while(p){
2777                 if(p.ownerTree){
2778                     this.ownerTree = p.ownerTree;
2779                     break;
2780                 }
2781                 p = p.parentNode;
2782             }
2783         }
2784         return this.ownerTree;
2785     },
2786
2787     /**
2788      * Returns depth of this node (the root node has a depth of 0)
2789      * @return {Number}
2790      */
2791     getDepth : function(){
2792         var depth = 0;
2793         var p = this;
2794         while(p.parentNode){
2795             ++depth;
2796             p = p.parentNode;
2797         }
2798         return depth;
2799     },
2800
2801     // private
2802     setOwnerTree : function(tree){
2803         // if it's move, we need to update everyone
2804         if(tree != this.ownerTree){
2805             if(this.ownerTree){
2806                 this.ownerTree.unregisterNode(this);
2807             }
2808             this.ownerTree = tree;
2809             var cs = this.childNodes;
2810             for(var i = 0, len = cs.length; i < len; i++) {
2811                 cs[i].setOwnerTree(tree);
2812             }
2813             if(tree){
2814                 tree.registerNode(this);
2815             }
2816         }
2817     },
2818
2819     /**
2820      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2821      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2822      * @return {String} The path
2823      */
2824     getPath : function(attr){
2825         attr = attr || "id";
2826         var p = this.parentNode;
2827         var b = [this.attributes[attr]];
2828         while(p){
2829             b.unshift(p.attributes[attr]);
2830             p = p.parentNode;
2831         }
2832         var sep = this.getOwnerTree().pathSeparator;
2833         return sep + b.join(sep);
2834     },
2835
2836     /**
2837      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2838      * function call will be the scope provided or the current node. The arguments to the function
2839      * will be the args provided or the current node. If the function returns false at any point,
2840      * the bubble is stopped.
2841      * @param {Function} fn The function to call
2842      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2843      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2844      */
2845     bubble : function(fn, scope, args){
2846         var p = this;
2847         while(p){
2848             if(fn.call(scope || p, args || p) === false){
2849                 break;
2850             }
2851             p = p.parentNode;
2852         }
2853     },
2854
2855     /**
2856      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2857      * function call will be the scope provided or the current node. The arguments to the function
2858      * will be the args provided or the current node. If the function returns false at any point,
2859      * the cascade is stopped on that branch.
2860      * @param {Function} fn The function to call
2861      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2862      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2863      */
2864     cascade : function(fn, scope, args){
2865         if(fn.call(scope || this, args || this) !== false){
2866             var cs = this.childNodes;
2867             for(var i = 0, len = cs.length; i < len; i++) {
2868                 cs[i].cascade(fn, scope, args);
2869             }
2870         }
2871     },
2872
2873     /**
2874      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2875      * function call will be the scope provided or the current node. The arguments to the function
2876      * will be the args provided or the current node. If the function returns false at any point,
2877      * the iteration stops.
2878      * @param {Function} fn The function to call
2879      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2880      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2881      */
2882     eachChild : function(fn, scope, args){
2883         var cs = this.childNodes;
2884         for(var i = 0, len = cs.length; i < len; i++) {
2885                 if(fn.call(scope || this, args || cs[i]) === false){
2886                     break;
2887                 }
2888         }
2889     },
2890
2891     /**
2892      * Finds the first child that has the attribute with the specified value.
2893      * @param {String} attribute The attribute name
2894      * @param {Mixed} value The value to search for
2895      * @return {Node} The found child or null if none was found
2896      */
2897     findChild : function(attribute, value){
2898         var cs = this.childNodes;
2899         for(var i = 0, len = cs.length; i < len; i++) {
2900                 if(cs[i].attributes[attribute] == value){
2901                     return cs[i];
2902                 }
2903         }
2904         return null;
2905     },
2906
2907     /**
2908      * Finds the first child by a custom function. The child matches if the function passed
2909      * returns true.
2910      * @param {Function} fn
2911      * @param {Object} scope (optional)
2912      * @return {Node} The found child or null if none was found
2913      */
2914     findChildBy : function(fn, scope){
2915         var cs = this.childNodes;
2916         for(var i = 0, len = cs.length; i < len; i++) {
2917                 if(fn.call(scope||cs[i], cs[i]) === true){
2918                     return cs[i];
2919                 }
2920         }
2921         return null;
2922     },
2923
2924     /**
2925      * Sorts this nodes children using the supplied sort function
2926      * @param {Function} fn
2927      * @param {Object} scope (optional)
2928      */
2929     sort : function(fn, scope){
2930         var cs = this.childNodes;
2931         var len = cs.length;
2932         if(len > 0){
2933             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2934             cs.sort(sortFn);
2935             for(var i = 0; i < len; i++){
2936                 var n = cs[i];
2937                 n.previousSibling = cs[i-1];
2938                 n.nextSibling = cs[i+1];
2939                 if(i == 0){
2940                     this.setFirstChild(n);
2941                 }
2942                 if(i == len-1){
2943                     this.setLastChild(n);
2944                 }
2945             }
2946         }
2947     },
2948
2949     /**
2950      * Returns true if this node is an ancestor (at any point) of the passed node.
2951      * @param {Node} node
2952      * @return {Boolean}
2953      */
2954     contains : function(node){
2955         return node.isAncestor(this);
2956     },
2957
2958     /**
2959      * Returns true if the passed node is an ancestor (at any point) of this node.
2960      * @param {Node} node
2961      * @return {Boolean}
2962      */
2963     isAncestor : function(node){
2964         var p = this.parentNode;
2965         while(p){
2966             if(p == node){
2967                 return true;
2968             }
2969             p = p.parentNode;
2970         }
2971         return false;
2972     },
2973
2974     toString : function(){
2975         return "[Node"+(this.id?" "+this.id:"")+"]";
2976     }
2977 });/*
2978  * Based on:
2979  * Ext JS Library 1.1.1
2980  * Copyright(c) 2006-2007, Ext JS, LLC.
2981  *
2982  * Originally Released Under LGPL - original licence link has changed is not relivant.
2983  *
2984  * Fork - LGPL
2985  * <script type="text/javascript">
2986  */
2987
2988
2989 /**
2990  * @class Roo.Shadow
2991  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
2992  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
2993  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
2994  * @constructor
2995  * Create a new Shadow
2996  * @param {Object} config The config object
2997  */
2998 Roo.Shadow = function(config){
2999     Roo.apply(this, config);
3000     if(typeof this.mode != "string"){
3001         this.mode = this.defaultMode;
3002     }
3003     var o = this.offset, a = {h: 0};
3004     var rad = Math.floor(this.offset/2);
3005     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3006         case "drop":
3007             a.w = 0;
3008             a.l = a.t = o;
3009             a.t -= 1;
3010             if(Roo.isIE){
3011                 a.l -= this.offset + rad;
3012                 a.t -= this.offset + rad;
3013                 a.w -= rad;
3014                 a.h -= rad;
3015                 a.t += 1;
3016             }
3017         break;
3018         case "sides":
3019             a.w = (o*2);
3020             a.l = -o;
3021             a.t = o-1;
3022             if(Roo.isIE){
3023                 a.l -= (this.offset - rad);
3024                 a.t -= this.offset + rad;
3025                 a.l += 1;
3026                 a.w -= (this.offset - rad)*2;
3027                 a.w -= rad + 1;
3028                 a.h -= 1;
3029             }
3030         break;
3031         case "frame":
3032             a.w = a.h = (o*2);
3033             a.l = a.t = -o;
3034             a.t += 1;
3035             a.h -= 2;
3036             if(Roo.isIE){
3037                 a.l -= (this.offset - rad);
3038                 a.t -= (this.offset - rad);
3039                 a.l += 1;
3040                 a.w -= (this.offset + rad + 1);
3041                 a.h -= (this.offset + rad);
3042                 a.h += 1;
3043             }
3044         break;
3045     };
3046
3047     this.adjusts = a;
3048 };
3049
3050 Roo.Shadow.prototype = {
3051     /**
3052      * @cfg {String} mode
3053      * The shadow display mode.  Supports the following options:<br />
3054      * sides: Shadow displays on both sides and bottom only<br />
3055      * frame: Shadow displays equally on all four sides<br />
3056      * drop: Traditional bottom-right drop shadow (default)
3057      */
3058     /**
3059      * @cfg {String} offset
3060      * The number of pixels to offset the shadow from the element (defaults to 4)
3061      */
3062     offset: 4,
3063
3064     // private
3065     defaultMode: "drop",
3066
3067     /**
3068      * Displays the shadow under the target element
3069      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3070      */
3071     show : function(target){
3072         target = Roo.get(target);
3073         if(!this.el){
3074             this.el = Roo.Shadow.Pool.pull();
3075             if(this.el.dom.nextSibling != target.dom){
3076                 this.el.insertBefore(target);
3077             }
3078         }
3079         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3080         if(Roo.isIE){
3081             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3082         }
3083         this.realign(
3084             target.getLeft(true),
3085             target.getTop(true),
3086             target.getWidth(),
3087             target.getHeight()
3088         );
3089         this.el.dom.style.display = "block";
3090     },
3091
3092     /**
3093      * Returns true if the shadow is visible, else false
3094      */
3095     isVisible : function(){
3096         return this.el ? true : false;  
3097     },
3098
3099     /**
3100      * Direct alignment when values are already available. Show must be called at least once before
3101      * calling this method to ensure it is initialized.
3102      * @param {Number} left The target element left position
3103      * @param {Number} top The target element top position
3104      * @param {Number} width The target element width
3105      * @param {Number} height The target element height
3106      */
3107     realign : function(l, t, w, h){
3108         if(!this.el){
3109             return;
3110         }
3111         var a = this.adjusts, d = this.el.dom, s = d.style;
3112         var iea = 0;
3113         s.left = (l+a.l)+"px";
3114         s.top = (t+a.t)+"px";
3115         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3116  
3117         if(s.width != sws || s.height != shs){
3118             s.width = sws;
3119             s.height = shs;
3120             if(!Roo.isIE){
3121                 var cn = d.childNodes;
3122                 var sww = Math.max(0, (sw-12))+"px";
3123                 cn[0].childNodes[1].style.width = sww;
3124                 cn[1].childNodes[1].style.width = sww;
3125                 cn[2].childNodes[1].style.width = sww;
3126                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3127             }
3128         }
3129     },
3130
3131     /**
3132      * Hides this shadow
3133      */
3134     hide : function(){
3135         if(this.el){
3136             this.el.dom.style.display = "none";
3137             Roo.Shadow.Pool.push(this.el);
3138             delete this.el;
3139         }
3140     },
3141
3142     /**
3143      * Adjust the z-index of this shadow
3144      * @param {Number} zindex The new z-index
3145      */
3146     setZIndex : function(z){
3147         this.zIndex = z;
3148         if(this.el){
3149             this.el.setStyle("z-index", z);
3150         }
3151     }
3152 };
3153
3154 // Private utility class that manages the internal Shadow cache
3155 Roo.Shadow.Pool = function(){
3156     var p = [];
3157     var markup = Roo.isIE ?
3158                  '<div class="x-ie-shadow"></div>' :
3159                  '<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>';
3160     return {
3161         pull : function(){
3162             var sh = p.shift();
3163             if(!sh){
3164                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3165                 sh.autoBoxAdjust = false;
3166             }
3167             return sh;
3168         },
3169
3170         push : function(sh){
3171             p.push(sh);
3172         }
3173     };
3174 }();/*
3175  * Based on:
3176  * Ext JS Library 1.1.1
3177  * Copyright(c) 2006-2007, Ext JS, LLC.
3178  *
3179  * Originally Released Under LGPL - original licence link has changed is not relivant.
3180  *
3181  * Fork - LGPL
3182  * <script type="text/javascript">
3183  */
3184
3185
3186 /**
3187  * @class Roo.SplitBar
3188  * @extends Roo.util.Observable
3189  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3190  * <br><br>
3191  * Usage:
3192  * <pre><code>
3193 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3194                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3195 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3196 split.minSize = 100;
3197 split.maxSize = 600;
3198 split.animate = true;
3199 split.on('moved', splitterMoved);
3200 </code></pre>
3201  * @constructor
3202  * Create a new SplitBar
3203  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3204  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3205  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3206  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3207                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3208                         position of the SplitBar).
3209  */
3210 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3211     
3212     /** @private */
3213     this.el = Roo.get(dragElement, true);
3214     this.el.dom.unselectable = "on";
3215     /** @private */
3216     this.resizingEl = Roo.get(resizingElement, true);
3217
3218     /**
3219      * @private
3220      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3221      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3222      * @type Number
3223      */
3224     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3225     
3226     /**
3227      * The minimum size of the resizing element. (Defaults to 0)
3228      * @type Number
3229      */
3230     this.minSize = 0;
3231     
3232     /**
3233      * The maximum size of the resizing element. (Defaults to 2000)
3234      * @type Number
3235      */
3236     this.maxSize = 2000;
3237     
3238     /**
3239      * Whether to animate the transition to the new size
3240      * @type Boolean
3241      */
3242     this.animate = false;
3243     
3244     /**
3245      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3246      * @type Boolean
3247      */
3248     this.useShim = false;
3249     
3250     /** @private */
3251     this.shim = null;
3252     
3253     if(!existingProxy){
3254         /** @private */
3255         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3256     }else{
3257         this.proxy = Roo.get(existingProxy).dom;
3258     }
3259     /** @private */
3260     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3261     
3262     /** @private */
3263     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3264     
3265     /** @private */
3266     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3267     
3268     /** @private */
3269     this.dragSpecs = {};
3270     
3271     /**
3272      * @private The adapter to use to positon and resize elements
3273      */
3274     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3275     this.adapter.init(this);
3276     
3277     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3278         /** @private */
3279         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3280         this.el.addClass("x-splitbar-h");
3281     }else{
3282         /** @private */
3283         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3284         this.el.addClass("x-splitbar-v");
3285     }
3286     
3287     this.addEvents({
3288         /**
3289          * @event resize
3290          * Fires when the splitter is moved (alias for {@link #event-moved})
3291          * @param {Roo.SplitBar} this
3292          * @param {Number} newSize the new width or height
3293          */
3294         "resize" : true,
3295         /**
3296          * @event moved
3297          * Fires when the splitter is moved
3298          * @param {Roo.SplitBar} this
3299          * @param {Number} newSize the new width or height
3300          */
3301         "moved" : true,
3302         /**
3303          * @event beforeresize
3304          * Fires before the splitter is dragged
3305          * @param {Roo.SplitBar} this
3306          */
3307         "beforeresize" : true,
3308
3309         "beforeapply" : true
3310     });
3311
3312     Roo.util.Observable.call(this);
3313 };
3314
3315 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3316     onStartProxyDrag : function(x, y){
3317         this.fireEvent("beforeresize", this);
3318         if(!this.overlay){
3319             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3320             o.unselectable();
3321             o.enableDisplayMode("block");
3322             // all splitbars share the same overlay
3323             Roo.SplitBar.prototype.overlay = o;
3324         }
3325         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3326         this.overlay.show();
3327         Roo.get(this.proxy).setDisplayed("block");
3328         var size = this.adapter.getElementSize(this);
3329         this.activeMinSize = this.getMinimumSize();;
3330         this.activeMaxSize = this.getMaximumSize();;
3331         var c1 = size - this.activeMinSize;
3332         var c2 = Math.max(this.activeMaxSize - size, 0);
3333         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3334             this.dd.resetConstraints();
3335             this.dd.setXConstraint(
3336                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3337                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3338             );
3339             this.dd.setYConstraint(0, 0);
3340         }else{
3341             this.dd.resetConstraints();
3342             this.dd.setXConstraint(0, 0);
3343             this.dd.setYConstraint(
3344                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3345                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3346             );
3347          }
3348         this.dragSpecs.startSize = size;
3349         this.dragSpecs.startPoint = [x, y];
3350         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3351     },
3352     
3353     /** 
3354      * @private Called after the drag operation by the DDProxy
3355      */
3356     onEndProxyDrag : function(e){
3357         Roo.get(this.proxy).setDisplayed(false);
3358         var endPoint = Roo.lib.Event.getXY(e);
3359         if(this.overlay){
3360             this.overlay.hide();
3361         }
3362         var newSize;
3363         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3364             newSize = this.dragSpecs.startSize + 
3365                 (this.placement == Roo.SplitBar.LEFT ?
3366                     endPoint[0] - this.dragSpecs.startPoint[0] :
3367                     this.dragSpecs.startPoint[0] - endPoint[0]
3368                 );
3369         }else{
3370             newSize = this.dragSpecs.startSize + 
3371                 (this.placement == Roo.SplitBar.TOP ?
3372                     endPoint[1] - this.dragSpecs.startPoint[1] :
3373                     this.dragSpecs.startPoint[1] - endPoint[1]
3374                 );
3375         }
3376         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3377         if(newSize != this.dragSpecs.startSize){
3378             if(this.fireEvent('beforeapply', this, newSize) !== false){
3379                 this.adapter.setElementSize(this, newSize);
3380                 this.fireEvent("moved", this, newSize);
3381                 this.fireEvent("resize", this, newSize);
3382             }
3383         }
3384     },
3385     
3386     /**
3387      * Get the adapter this SplitBar uses
3388      * @return The adapter object
3389      */
3390     getAdapter : function(){
3391         return this.adapter;
3392     },
3393     
3394     /**
3395      * Set the adapter this SplitBar uses
3396      * @param {Object} adapter A SplitBar adapter object
3397      */
3398     setAdapter : function(adapter){
3399         this.adapter = adapter;
3400         this.adapter.init(this);
3401     },
3402     
3403     /**
3404      * Gets the minimum size for the resizing element
3405      * @return {Number} The minimum size
3406      */
3407     getMinimumSize : function(){
3408         return this.minSize;
3409     },
3410     
3411     /**
3412      * Sets the minimum size for the resizing element
3413      * @param {Number} minSize The minimum size
3414      */
3415     setMinimumSize : function(minSize){
3416         this.minSize = minSize;
3417     },
3418     
3419     /**
3420      * Gets the maximum size for the resizing element
3421      * @return {Number} The maximum size
3422      */
3423     getMaximumSize : function(){
3424         return this.maxSize;
3425     },
3426     
3427     /**
3428      * Sets the maximum size for the resizing element
3429      * @param {Number} maxSize The maximum size
3430      */
3431     setMaximumSize : function(maxSize){
3432         this.maxSize = maxSize;
3433     },
3434     
3435     /**
3436      * Sets the initialize size for the resizing element
3437      * @param {Number} size The initial size
3438      */
3439     setCurrentSize : function(size){
3440         var oldAnimate = this.animate;
3441         this.animate = false;
3442         this.adapter.setElementSize(this, size);
3443         this.animate = oldAnimate;
3444     },
3445     
3446     /**
3447      * Destroy this splitbar. 
3448      * @param {Boolean} removeEl True to remove the element
3449      */
3450     destroy : function(removeEl){
3451         if(this.shim){
3452             this.shim.remove();
3453         }
3454         this.dd.unreg();
3455         this.proxy.parentNode.removeChild(this.proxy);
3456         if(removeEl){
3457             this.el.remove();
3458         }
3459     }
3460 });
3461
3462 /**
3463  * @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.
3464  */
3465 Roo.SplitBar.createProxy = function(dir){
3466     var proxy = new Roo.Element(document.createElement("div"));
3467     proxy.unselectable();
3468     var cls = 'x-splitbar-proxy';
3469     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3470     document.body.appendChild(proxy.dom);
3471     return proxy.dom;
3472 };
3473
3474 /** 
3475  * @class Roo.SplitBar.BasicLayoutAdapter
3476  * Default Adapter. It assumes the splitter and resizing element are not positioned
3477  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3478  */
3479 Roo.SplitBar.BasicLayoutAdapter = function(){
3480 };
3481
3482 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3483     // do nothing for now
3484     init : function(s){
3485     
3486     },
3487     /**
3488      * Called before drag operations to get the current size of the resizing element. 
3489      * @param {Roo.SplitBar} s The SplitBar using this adapter
3490      */
3491      getElementSize : function(s){
3492         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3493             return s.resizingEl.getWidth();
3494         }else{
3495             return s.resizingEl.getHeight();
3496         }
3497     },
3498     
3499     /**
3500      * Called after drag operations to set the size of the resizing element.
3501      * @param {Roo.SplitBar} s The SplitBar using this adapter
3502      * @param {Number} newSize The new size to set
3503      * @param {Function} onComplete A function to be invoked when resizing is complete
3504      */
3505     setElementSize : function(s, newSize, onComplete){
3506         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3507             if(!s.animate){
3508                 s.resizingEl.setWidth(newSize);
3509                 if(onComplete){
3510                     onComplete(s, newSize);
3511                 }
3512             }else{
3513                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3514             }
3515         }else{
3516             
3517             if(!s.animate){
3518                 s.resizingEl.setHeight(newSize);
3519                 if(onComplete){
3520                     onComplete(s, newSize);
3521                 }
3522             }else{
3523                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3524             }
3525         }
3526     }
3527 };
3528
3529 /** 
3530  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3531  * @extends Roo.SplitBar.BasicLayoutAdapter
3532  * Adapter that  moves the splitter element to align with the resized sizing element. 
3533  * Used with an absolute positioned SplitBar.
3534  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3535  * document.body, make sure you assign an id to the body element.
3536  */
3537 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3538     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3539     this.container = Roo.get(container);
3540 };
3541
3542 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3543     init : function(s){
3544         this.basic.init(s);
3545     },
3546     
3547     getElementSize : function(s){
3548         return this.basic.getElementSize(s);
3549     },
3550     
3551     setElementSize : function(s, newSize, onComplete){
3552         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3553     },
3554     
3555     moveSplitter : function(s){
3556         var yes = Roo.SplitBar;
3557         switch(s.placement){
3558             case yes.LEFT:
3559                 s.el.setX(s.resizingEl.getRight());
3560                 break;
3561             case yes.RIGHT:
3562                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3563                 break;
3564             case yes.TOP:
3565                 s.el.setY(s.resizingEl.getBottom());
3566                 break;
3567             case yes.BOTTOM:
3568                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3569                 break;
3570         }
3571     }
3572 };
3573
3574 /**
3575  * Orientation constant - Create a vertical SplitBar
3576  * @static
3577  * @type Number
3578  */
3579 Roo.SplitBar.VERTICAL = 1;
3580
3581 /**
3582  * Orientation constant - Create a horizontal SplitBar
3583  * @static
3584  * @type Number
3585  */
3586 Roo.SplitBar.HORIZONTAL = 2;
3587
3588 /**
3589  * Placement constant - The resizing element is to the left of the splitter element
3590  * @static
3591  * @type Number
3592  */
3593 Roo.SplitBar.LEFT = 1;
3594
3595 /**
3596  * Placement constant - The resizing element is to the right of the splitter element
3597  * @static
3598  * @type Number
3599  */
3600 Roo.SplitBar.RIGHT = 2;
3601
3602 /**
3603  * Placement constant - The resizing element is positioned above the splitter element
3604  * @static
3605  * @type Number
3606  */
3607 Roo.SplitBar.TOP = 3;
3608
3609 /**
3610  * Placement constant - The resizing element is positioned under splitter element
3611  * @static
3612  * @type Number
3613  */
3614 Roo.SplitBar.BOTTOM = 4;
3615 /*
3616  * Based on:
3617  * Ext JS Library 1.1.1
3618  * Copyright(c) 2006-2007, Ext JS, LLC.
3619  *
3620  * Originally Released Under LGPL - original licence link has changed is not relivant.
3621  *
3622  * Fork - LGPL
3623  * <script type="text/javascript">
3624  */
3625
3626 /**
3627  * @class Roo.View
3628  * @extends Roo.util.Observable
3629  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3630  * This class also supports single and multi selection modes. <br>
3631  * Create a data model bound view:
3632  <pre><code>
3633  var store = new Roo.data.Store(...);
3634
3635  var view = new Roo.View({
3636     el : "my-element",
3637     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3638  
3639     singleSelect: true,
3640     selectedClass: "ydataview-selected",
3641     store: store
3642  });
3643
3644  // listen for node click?
3645  view.on("click", function(vw, index, node, e){
3646  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3647  });
3648
3649  // load XML data
3650  dataModel.load("foobar.xml");
3651  </code></pre>
3652  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3653  * <br><br>
3654  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3655  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3656  * 
3657  * Note: old style constructor is still suported (container, template, config)
3658  * 
3659  * @constructor
3660  * Create a new View
3661  * @param {Object} config The config object
3662  * 
3663  */
3664 Roo.View = function(config, depreciated_tpl, depreciated_config){
3665     
3666     this.parent = false;
3667     
3668     if (typeof(depreciated_tpl) == 'undefined') {
3669         // new way.. - universal constructor.
3670         Roo.apply(this, config);
3671         this.el  = Roo.get(this.el);
3672     } else {
3673         // old format..
3674         this.el  = Roo.get(config);
3675         this.tpl = depreciated_tpl;
3676         Roo.apply(this, depreciated_config);
3677     }
3678     this.wrapEl  = this.el.wrap().wrap();
3679     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3680     
3681     
3682     if(typeof(this.tpl) == "string"){
3683         this.tpl = new Roo.Template(this.tpl);
3684     } else {
3685         // support xtype ctors..
3686         this.tpl = new Roo.factory(this.tpl, Roo);
3687     }
3688     
3689     
3690     this.tpl.compile();
3691     
3692     /** @private */
3693     this.addEvents({
3694         /**
3695          * @event beforeclick
3696          * Fires before a click is processed. Returns false to cancel the default action.
3697          * @param {Roo.View} this
3698          * @param {Number} index The index of the target node
3699          * @param {HTMLElement} node The target node
3700          * @param {Roo.EventObject} e The raw event object
3701          */
3702             "beforeclick" : true,
3703         /**
3704          * @event click
3705          * Fires when a template node is clicked.
3706          * @param {Roo.View} this
3707          * @param {Number} index The index of the target node
3708          * @param {HTMLElement} node The target node
3709          * @param {Roo.EventObject} e The raw event object
3710          */
3711             "click" : true,
3712         /**
3713          * @event dblclick
3714          * Fires when a template node is double clicked.
3715          * @param {Roo.View} this
3716          * @param {Number} index The index of the target node
3717          * @param {HTMLElement} node The target node
3718          * @param {Roo.EventObject} e The raw event object
3719          */
3720             "dblclick" : true,
3721         /**
3722          * @event contextmenu
3723          * Fires when a template node is right clicked.
3724          * @param {Roo.View} this
3725          * @param {Number} index The index of the target node
3726          * @param {HTMLElement} node The target node
3727          * @param {Roo.EventObject} e The raw event object
3728          */
3729             "contextmenu" : true,
3730         /**
3731          * @event selectionchange
3732          * Fires when the selected nodes change.
3733          * @param {Roo.View} this
3734          * @param {Array} selections Array of the selected nodes
3735          */
3736             "selectionchange" : true,
3737     
3738         /**
3739          * @event beforeselect
3740          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3741          * @param {Roo.View} this
3742          * @param {HTMLElement} node The node to be selected
3743          * @param {Array} selections Array of currently selected nodes
3744          */
3745             "beforeselect" : true,
3746         /**
3747          * @event preparedata
3748          * Fires on every row to render, to allow you to change the data.
3749          * @param {Roo.View} this
3750          * @param {Object} data to be rendered (change this)
3751          */
3752           "preparedata" : true
3753           
3754           
3755         });
3756
3757
3758
3759     this.el.on({
3760         "click": this.onClick,
3761         "dblclick": this.onDblClick,
3762         "contextmenu": this.onContextMenu,
3763         scope:this
3764     });
3765
3766     this.selections = [];
3767     this.nodes = [];
3768     this.cmp = new Roo.CompositeElementLite([]);
3769     if(this.store){
3770         this.store = Roo.factory(this.store, Roo.data);
3771         this.setStore(this.store, true);
3772     }
3773     
3774     if ( this.footer && this.footer.xtype) {
3775            
3776          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3777         
3778         this.footer.dataSource = this.store;
3779         this.footer.container = fctr;
3780         this.footer = Roo.factory(this.footer, Roo);
3781         fctr.insertFirst(this.el);
3782         
3783         // this is a bit insane - as the paging toolbar seems to detach the el..
3784 //        dom.parentNode.parentNode.parentNode
3785          // they get detached?
3786     }
3787     
3788     
3789     Roo.View.superclass.constructor.call(this);
3790     
3791     
3792 };
3793
3794 Roo.extend(Roo.View, Roo.util.Observable, {
3795     
3796      /**
3797      * @cfg {Roo.data.Store} store Data store to load data from.
3798      */
3799     store : false,
3800     
3801     /**
3802      * @cfg {String|Roo.Element} el The container element.
3803      */
3804     el : '',
3805     
3806     /**
3807      * @cfg {String|Roo.Template} tpl The template used by this View 
3808      */
3809     tpl : false,
3810     /**
3811      * @cfg {String} dataName the named area of the template to use as the data area
3812      *                          Works with domtemplates roo-name="name"
3813      */
3814     dataName: false,
3815     /**
3816      * @cfg {String} selectedClass The css class to add to selected nodes
3817      */
3818     selectedClass : "x-view-selected",
3819      /**
3820      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3821      */
3822     emptyText : "",
3823     
3824     /**
3825      * @cfg {String} text to display on mask (default Loading)
3826      */
3827     mask : false,
3828     /**
3829      * @cfg {Boolean} multiSelect Allow multiple selection
3830      */
3831     multiSelect : false,
3832     /**
3833      * @cfg {Boolean} singleSelect Allow single selection
3834      */
3835     singleSelect:  false,
3836     
3837     /**
3838      * @cfg {Boolean} toggleSelect - selecting 
3839      */
3840     toggleSelect : false,
3841     
3842     /**
3843      * @cfg {Boolean} tickable - selecting 
3844      */
3845     tickable : false,
3846     
3847     /**
3848      * Returns the element this view is bound to.
3849      * @return {Roo.Element}
3850      */
3851     getEl : function(){
3852         return this.wrapEl;
3853     },
3854     
3855     
3856
3857     /**
3858      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3859      */
3860     refresh : function(){
3861         //Roo.log('refresh');
3862         var t = this.tpl;
3863         
3864         // if we are using something like 'domtemplate', then
3865         // the what gets used is:
3866         // t.applySubtemplate(NAME, data, wrapping data..)
3867         // the outer template then get' applied with
3868         //     the store 'extra data'
3869         // and the body get's added to the
3870         //      roo-name="data" node?
3871         //      <span class='roo-tpl-{name}'></span> ?????
3872         
3873         
3874         
3875         this.clearSelections();
3876         this.el.update("");
3877         var html = [];
3878         var records = this.store.getRange();
3879         if(records.length < 1) {
3880             
3881             // is this valid??  = should it render a template??
3882             
3883             this.el.update(this.emptyText);
3884             return;
3885         }
3886         var el = this.el;
3887         if (this.dataName) {
3888             this.el.update(t.apply(this.store.meta)); //????
3889             el = this.el.child('.roo-tpl-' + this.dataName);
3890         }
3891         
3892         for(var i = 0, len = records.length; i < len; i++){
3893             var data = this.prepareData(records[i].data, i, records[i]);
3894             this.fireEvent("preparedata", this, data, i, records[i]);
3895             
3896             var d = Roo.apply({}, data);
3897             
3898             if(this.tickable){
3899                 Roo.apply(d, {'roo-id' : Roo.id()});
3900                 
3901                 var _this = this;
3902             
3903                 Roo.each(this.parent.item, function(item){
3904                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3905                         return;
3906                     }
3907                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3908                 });
3909             }
3910             
3911             html[html.length] = Roo.util.Format.trim(
3912                 this.dataName ?
3913                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3914                     t.apply(d)
3915             );
3916         }
3917         
3918         
3919         
3920         el.update(html.join(""));
3921         this.nodes = el.dom.childNodes;
3922         this.updateIndexes(0);
3923     },
3924     
3925
3926     /**
3927      * Function to override to reformat the data that is sent to
3928      * the template for each node.
3929      * DEPRICATED - use the preparedata event handler.
3930      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3931      * a JSON object for an UpdateManager bound view).
3932      */
3933     prepareData : function(data, index, record)
3934     {
3935         this.fireEvent("preparedata", this, data, index, record);
3936         return data;
3937     },
3938
3939     onUpdate : function(ds, record){
3940         // Roo.log('on update');   
3941         this.clearSelections();
3942         var index = this.store.indexOf(record);
3943         var n = this.nodes[index];
3944         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3945         n.parentNode.removeChild(n);
3946         this.updateIndexes(index, index);
3947     },
3948
3949     
3950     
3951 // --------- FIXME     
3952     onAdd : function(ds, records, index)
3953     {
3954         //Roo.log(['on Add', ds, records, index] );        
3955         this.clearSelections();
3956         if(this.nodes.length == 0){
3957             this.refresh();
3958             return;
3959         }
3960         var n = this.nodes[index];
3961         for(var i = 0, len = records.length; i < len; i++){
3962             var d = this.prepareData(records[i].data, i, records[i]);
3963             if(n){
3964                 this.tpl.insertBefore(n, d);
3965             }else{
3966                 
3967                 this.tpl.append(this.el, d);
3968             }
3969         }
3970         this.updateIndexes(index);
3971     },
3972
3973     onRemove : function(ds, record, index){
3974        // Roo.log('onRemove');
3975         this.clearSelections();
3976         var el = this.dataName  ?
3977             this.el.child('.roo-tpl-' + this.dataName) :
3978             this.el; 
3979         
3980         el.dom.removeChild(this.nodes[index]);
3981         this.updateIndexes(index);
3982     },
3983
3984     /**
3985      * Refresh an individual node.
3986      * @param {Number} index
3987      */
3988     refreshNode : function(index){
3989         this.onUpdate(this.store, this.store.getAt(index));
3990     },
3991
3992     updateIndexes : function(startIndex, endIndex){
3993         var ns = this.nodes;
3994         startIndex = startIndex || 0;
3995         endIndex = endIndex || ns.length - 1;
3996         for(var i = startIndex; i <= endIndex; i++){
3997             ns[i].nodeIndex = i;
3998         }
3999     },
4000
4001     /**
4002      * Changes the data store this view uses and refresh the view.
4003      * @param {Store} store
4004      */
4005     setStore : function(store, initial){
4006         if(!initial && this.store){
4007             this.store.un("datachanged", this.refresh);
4008             this.store.un("add", this.onAdd);
4009             this.store.un("remove", this.onRemove);
4010             this.store.un("update", this.onUpdate);
4011             this.store.un("clear", this.refresh);
4012             this.store.un("beforeload", this.onBeforeLoad);
4013             this.store.un("load", this.onLoad);
4014             this.store.un("loadexception", this.onLoad);
4015         }
4016         if(store){
4017           
4018             store.on("datachanged", this.refresh, this);
4019             store.on("add", this.onAdd, this);
4020             store.on("remove", this.onRemove, this);
4021             store.on("update", this.onUpdate, this);
4022             store.on("clear", this.refresh, this);
4023             store.on("beforeload", this.onBeforeLoad, this);
4024             store.on("load", this.onLoad, this);
4025             store.on("loadexception", this.onLoad, this);
4026         }
4027         
4028         if(store){
4029             this.refresh();
4030         }
4031     },
4032     /**
4033      * onbeforeLoad - masks the loading area.
4034      *
4035      */
4036     onBeforeLoad : function(store,opts)
4037     {
4038          //Roo.log('onBeforeLoad');   
4039         if (!opts.add) {
4040             this.el.update("");
4041         }
4042         this.el.mask(this.mask ? this.mask : "Loading" ); 
4043     },
4044     onLoad : function ()
4045     {
4046         this.el.unmask();
4047     },
4048     
4049
4050     /**
4051      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4052      * @param {HTMLElement} node
4053      * @return {HTMLElement} The template node
4054      */
4055     findItemFromChild : function(node){
4056         var el = this.dataName  ?
4057             this.el.child('.roo-tpl-' + this.dataName,true) :
4058             this.el.dom; 
4059         
4060         if(!node || node.parentNode == el){
4061                     return node;
4062             }
4063             var p = node.parentNode;
4064             while(p && p != el){
4065             if(p.parentNode == el){
4066                 return p;
4067             }
4068             p = p.parentNode;
4069         }
4070             return null;
4071     },
4072
4073     /** @ignore */
4074     onClick : function(e){
4075         var item = this.findItemFromChild(e.getTarget());
4076         if(item){
4077             var index = this.indexOf(item);
4078             if(this.onItemClick(item, index, e) !== false){
4079                 this.fireEvent("click", this, index, item, e);
4080             }
4081         }else{
4082             this.clearSelections();
4083         }
4084     },
4085
4086     /** @ignore */
4087     onContextMenu : function(e){
4088         var item = this.findItemFromChild(e.getTarget());
4089         if(item){
4090             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4091         }
4092     },
4093
4094     /** @ignore */
4095     onDblClick : function(e){
4096         var item = this.findItemFromChild(e.getTarget());
4097         if(item){
4098             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4099         }
4100     },
4101
4102     onItemClick : function(item, index, e)
4103     {
4104         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4105             return false;
4106         }
4107         if (this.toggleSelect) {
4108             var m = this.isSelected(item) ? 'unselect' : 'select';
4109             //Roo.log(m);
4110             var _t = this;
4111             _t[m](item, true, false);
4112             return true;
4113         }
4114         if(this.multiSelect || this.singleSelect){
4115             if(this.multiSelect && e.shiftKey && this.lastSelection){
4116                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4117             }else{
4118                 this.select(item, this.multiSelect && e.ctrlKey);
4119                 this.lastSelection = item;
4120             }
4121             
4122             if(!this.tickable){
4123                 e.preventDefault();
4124             }
4125             
4126         }
4127         return true;
4128     },
4129
4130     /**
4131      * Get the number of selected nodes.
4132      * @return {Number}
4133      */
4134     getSelectionCount : function(){
4135         return this.selections.length;
4136     },
4137
4138     /**
4139      * Get the currently selected nodes.
4140      * @return {Array} An array of HTMLElements
4141      */
4142     getSelectedNodes : function(){
4143         return this.selections;
4144     },
4145
4146     /**
4147      * Get the indexes of the selected nodes.
4148      * @return {Array}
4149      */
4150     getSelectedIndexes : function(){
4151         var indexes = [], s = this.selections;
4152         for(var i = 0, len = s.length; i < len; i++){
4153             indexes.push(s[i].nodeIndex);
4154         }
4155         return indexes;
4156     },
4157
4158     /**
4159      * Clear all selections
4160      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4161      */
4162     clearSelections : function(suppressEvent){
4163         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4164             this.cmp.elements = this.selections;
4165             this.cmp.removeClass(this.selectedClass);
4166             this.selections = [];
4167             if(!suppressEvent){
4168                 this.fireEvent("selectionchange", this, this.selections);
4169             }
4170         }
4171     },
4172
4173     /**
4174      * Returns true if the passed node is selected
4175      * @param {HTMLElement/Number} node The node or node index
4176      * @return {Boolean}
4177      */
4178     isSelected : function(node){
4179         var s = this.selections;
4180         if(s.length < 1){
4181             return false;
4182         }
4183         node = this.getNode(node);
4184         return s.indexOf(node) !== -1;
4185     },
4186
4187     /**
4188      * Selects nodes.
4189      * @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
4190      * @param {Boolean} keepExisting (optional) true to keep existing selections
4191      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4192      */
4193     select : function(nodeInfo, keepExisting, suppressEvent){
4194         if(nodeInfo instanceof Array){
4195             if(!keepExisting){
4196                 this.clearSelections(true);
4197             }
4198             for(var i = 0, len = nodeInfo.length; i < len; i++){
4199                 this.select(nodeInfo[i], true, true);
4200             }
4201             return;
4202         } 
4203         var node = this.getNode(nodeInfo);
4204         if(!node || this.isSelected(node)){
4205             return; // already selected.
4206         }
4207         if(!keepExisting){
4208             this.clearSelections(true);
4209         }
4210         
4211         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4212             Roo.fly(node).addClass(this.selectedClass);
4213             this.selections.push(node);
4214             if(!suppressEvent){
4215                 this.fireEvent("selectionchange", this, this.selections);
4216             }
4217         }
4218         
4219         
4220     },
4221       /**
4222      * Unselects nodes.
4223      * @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
4224      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4225      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4226      */
4227     unselect : function(nodeInfo, keepExisting, suppressEvent)
4228     {
4229         if(nodeInfo instanceof Array){
4230             Roo.each(this.selections, function(s) {
4231                 this.unselect(s, nodeInfo);
4232             }, this);
4233             return;
4234         }
4235         var node = this.getNode(nodeInfo);
4236         if(!node || !this.isSelected(node)){
4237             //Roo.log("not selected");
4238             return; // not selected.
4239         }
4240         // fireevent???
4241         var ns = [];
4242         Roo.each(this.selections, function(s) {
4243             if (s == node ) {
4244                 Roo.fly(node).removeClass(this.selectedClass);
4245
4246                 return;
4247             }
4248             ns.push(s);
4249         },this);
4250         
4251         this.selections= ns;
4252         this.fireEvent("selectionchange", this, this.selections);
4253     },
4254
4255     /**
4256      * Gets a template node.
4257      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4258      * @return {HTMLElement} The node or null if it wasn't found
4259      */
4260     getNode : function(nodeInfo){
4261         if(typeof nodeInfo == "string"){
4262             return document.getElementById(nodeInfo);
4263         }else if(typeof nodeInfo == "number"){
4264             return this.nodes[nodeInfo];
4265         }
4266         return nodeInfo;
4267     },
4268
4269     /**
4270      * Gets a range template nodes.
4271      * @param {Number} startIndex
4272      * @param {Number} endIndex
4273      * @return {Array} An array of nodes
4274      */
4275     getNodes : function(start, end){
4276         var ns = this.nodes;
4277         start = start || 0;
4278         end = typeof end == "undefined" ? ns.length - 1 : end;
4279         var nodes = [];
4280         if(start <= end){
4281             for(var i = start; i <= end; i++){
4282                 nodes.push(ns[i]);
4283             }
4284         } else{
4285             for(var i = start; i >= end; i--){
4286                 nodes.push(ns[i]);
4287             }
4288         }
4289         return nodes;
4290     },
4291
4292     /**
4293      * Finds the index of the passed node
4294      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4295      * @return {Number} The index of the node or -1
4296      */
4297     indexOf : function(node){
4298         node = this.getNode(node);
4299         if(typeof node.nodeIndex == "number"){
4300             return node.nodeIndex;
4301         }
4302         var ns = this.nodes;
4303         for(var i = 0, len = ns.length; i < len; i++){
4304             if(ns[i] == node){
4305                 return i;
4306             }
4307         }
4308         return -1;
4309     }
4310 });
4311 /*
4312  * Based on:
4313  * Ext JS Library 1.1.1
4314  * Copyright(c) 2006-2007, Ext JS, LLC.
4315  *
4316  * Originally Released Under LGPL - original licence link has changed is not relivant.
4317  *
4318  * Fork - LGPL
4319  * <script type="text/javascript">
4320  */
4321
4322 /**
4323  * @class Roo.JsonView
4324  * @extends Roo.View
4325  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4326 <pre><code>
4327 var view = new Roo.JsonView({
4328     container: "my-element",
4329     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4330     multiSelect: true, 
4331     jsonRoot: "data" 
4332 });
4333
4334 // listen for node click?
4335 view.on("click", function(vw, index, node, e){
4336     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4337 });
4338
4339 // direct load of JSON data
4340 view.load("foobar.php");
4341
4342 // Example from my blog list
4343 var tpl = new Roo.Template(
4344     '&lt;div class="entry"&gt;' +
4345     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4346     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4347     "&lt;/div&gt;&lt;hr /&gt;"
4348 );
4349
4350 var moreView = new Roo.JsonView({
4351     container :  "entry-list", 
4352     template : tpl,
4353     jsonRoot: "posts"
4354 });
4355 moreView.on("beforerender", this.sortEntries, this);
4356 moreView.load({
4357     url: "/blog/get-posts.php",
4358     params: "allposts=true",
4359     text: "Loading Blog Entries..."
4360 });
4361 </code></pre>
4362
4363 * Note: old code is supported with arguments : (container, template, config)
4364
4365
4366  * @constructor
4367  * Create a new JsonView
4368  * 
4369  * @param {Object} config The config object
4370  * 
4371  */
4372 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4373     
4374     
4375     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4376
4377     var um = this.el.getUpdateManager();
4378     um.setRenderer(this);
4379     um.on("update", this.onLoad, this);
4380     um.on("failure", this.onLoadException, this);
4381
4382     /**
4383      * @event beforerender
4384      * Fires before rendering of the downloaded JSON data.
4385      * @param {Roo.JsonView} this
4386      * @param {Object} data The JSON data loaded
4387      */
4388     /**
4389      * @event load
4390      * Fires when data is loaded.
4391      * @param {Roo.JsonView} this
4392      * @param {Object} data The JSON data loaded
4393      * @param {Object} response The raw Connect response object
4394      */
4395     /**
4396      * @event loadexception
4397      * Fires when loading fails.
4398      * @param {Roo.JsonView} this
4399      * @param {Object} response The raw Connect response object
4400      */
4401     this.addEvents({
4402         'beforerender' : true,
4403         'load' : true,
4404         'loadexception' : true
4405     });
4406 };
4407 Roo.extend(Roo.JsonView, Roo.View, {
4408     /**
4409      * @type {String} The root property in the loaded JSON object that contains the data
4410      */
4411     jsonRoot : "",
4412
4413     /**
4414      * Refreshes the view.
4415      */
4416     refresh : function(){
4417         this.clearSelections();
4418         this.el.update("");
4419         var html = [];
4420         var o = this.jsonData;
4421         if(o && o.length > 0){
4422             for(var i = 0, len = o.length; i < len; i++){
4423                 var data = this.prepareData(o[i], i, o);
4424                 html[html.length] = this.tpl.apply(data);
4425             }
4426         }else{
4427             html.push(this.emptyText);
4428         }
4429         this.el.update(html.join(""));
4430         this.nodes = this.el.dom.childNodes;
4431         this.updateIndexes(0);
4432     },
4433
4434     /**
4435      * 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.
4436      * @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:
4437      <pre><code>
4438      view.load({
4439          url: "your-url.php",
4440          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4441          callback: yourFunction,
4442          scope: yourObject, //(optional scope)
4443          discardUrl: false,
4444          nocache: false,
4445          text: "Loading...",
4446          timeout: 30,
4447          scripts: false
4448      });
4449      </code></pre>
4450      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4451      * 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.
4452      * @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}
4453      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4454      * @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.
4455      */
4456     load : function(){
4457         var um = this.el.getUpdateManager();
4458         um.update.apply(um, arguments);
4459     },
4460
4461     // note - render is a standard framework call...
4462     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4463     render : function(el, response){
4464         
4465         this.clearSelections();
4466         this.el.update("");
4467         var o;
4468         try{
4469             if (response != '') {
4470                 o = Roo.util.JSON.decode(response.responseText);
4471                 if(this.jsonRoot){
4472                     
4473                     o = o[this.jsonRoot];
4474                 }
4475             }
4476         } catch(e){
4477         }
4478         /**
4479          * The current JSON data or null
4480          */
4481         this.jsonData = o;
4482         this.beforeRender();
4483         this.refresh();
4484     },
4485
4486 /**
4487  * Get the number of records in the current JSON dataset
4488  * @return {Number}
4489  */
4490     getCount : function(){
4491         return this.jsonData ? this.jsonData.length : 0;
4492     },
4493
4494 /**
4495  * Returns the JSON object for the specified node(s)
4496  * @param {HTMLElement/Array} node The node or an array of nodes
4497  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4498  * you get the JSON object for the node
4499  */
4500     getNodeData : function(node){
4501         if(node instanceof Array){
4502             var data = [];
4503             for(var i = 0, len = node.length; i < len; i++){
4504                 data.push(this.getNodeData(node[i]));
4505             }
4506             return data;
4507         }
4508         return this.jsonData[this.indexOf(node)] || null;
4509     },
4510
4511     beforeRender : function(){
4512         this.snapshot = this.jsonData;
4513         if(this.sortInfo){
4514             this.sort.apply(this, this.sortInfo);
4515         }
4516         this.fireEvent("beforerender", this, this.jsonData);
4517     },
4518
4519     onLoad : function(el, o){
4520         this.fireEvent("load", this, this.jsonData, o);
4521     },
4522
4523     onLoadException : function(el, o){
4524         this.fireEvent("loadexception", this, o);
4525     },
4526
4527 /**
4528  * Filter the data by a specific property.
4529  * @param {String} property A property on your JSON objects
4530  * @param {String/RegExp} value Either string that the property values
4531  * should start with, or a RegExp to test against the property
4532  */
4533     filter : function(property, value){
4534         if(this.jsonData){
4535             var data = [];
4536             var ss = this.snapshot;
4537             if(typeof value == "string"){
4538                 var vlen = value.length;
4539                 if(vlen == 0){
4540                     this.clearFilter();
4541                     return;
4542                 }
4543                 value = value.toLowerCase();
4544                 for(var i = 0, len = ss.length; i < len; i++){
4545                     var o = ss[i];
4546                     if(o[property].substr(0, vlen).toLowerCase() == value){
4547                         data.push(o);
4548                     }
4549                 }
4550             } else if(value.exec){ // regex?
4551                 for(var i = 0, len = ss.length; i < len; i++){
4552                     var o = ss[i];
4553                     if(value.test(o[property])){
4554                         data.push(o);
4555                     }
4556                 }
4557             } else{
4558                 return;
4559             }
4560             this.jsonData = data;
4561             this.refresh();
4562         }
4563     },
4564
4565 /**
4566  * Filter by a function. The passed function will be called with each
4567  * object in the current dataset. If the function returns true the value is kept,
4568  * otherwise it is filtered.
4569  * @param {Function} fn
4570  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4571  */
4572     filterBy : function(fn, scope){
4573         if(this.jsonData){
4574             var data = [];
4575             var ss = this.snapshot;
4576             for(var i = 0, len = ss.length; i < len; i++){
4577                 var o = ss[i];
4578                 if(fn.call(scope || this, o)){
4579                     data.push(o);
4580                 }
4581             }
4582             this.jsonData = data;
4583             this.refresh();
4584         }
4585     },
4586
4587 /**
4588  * Clears the current filter.
4589  */
4590     clearFilter : function(){
4591         if(this.snapshot && this.jsonData != this.snapshot){
4592             this.jsonData = this.snapshot;
4593             this.refresh();
4594         }
4595     },
4596
4597
4598 /**
4599  * Sorts the data for this view and refreshes it.
4600  * @param {String} property A property on your JSON objects to sort on
4601  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4602  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4603  */
4604     sort : function(property, dir, sortType){
4605         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4606         if(this.jsonData){
4607             var p = property;
4608             var dsc = dir && dir.toLowerCase() == "desc";
4609             var f = function(o1, o2){
4610                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4611                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4612                 ;
4613                 if(v1 < v2){
4614                     return dsc ? +1 : -1;
4615                 } else if(v1 > v2){
4616                     return dsc ? -1 : +1;
4617                 } else{
4618                     return 0;
4619                 }
4620             };
4621             this.jsonData.sort(f);
4622             this.refresh();
4623             if(this.jsonData != this.snapshot){
4624                 this.snapshot.sort(f);
4625             }
4626         }
4627     }
4628 });/*
4629  * Based on:
4630  * Ext JS Library 1.1.1
4631  * Copyright(c) 2006-2007, Ext JS, LLC.
4632  *
4633  * Originally Released Under LGPL - original licence link has changed is not relivant.
4634  *
4635  * Fork - LGPL
4636  * <script type="text/javascript">
4637  */
4638  
4639
4640 /**
4641  * @class Roo.ColorPalette
4642  * @extends Roo.Component
4643  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4644  * Here's an example of typical usage:
4645  * <pre><code>
4646 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4647 cp.render('my-div');
4648
4649 cp.on('select', function(palette, selColor){
4650     // do something with selColor
4651 });
4652 </code></pre>
4653  * @constructor
4654  * Create a new ColorPalette
4655  * @param {Object} config The config object
4656  */
4657 Roo.ColorPalette = function(config){
4658     Roo.ColorPalette.superclass.constructor.call(this, config);
4659     this.addEvents({
4660         /**
4661              * @event select
4662              * Fires when a color is selected
4663              * @param {ColorPalette} this
4664              * @param {String} color The 6-digit color hex code (without the # symbol)
4665              */
4666         select: true
4667     });
4668
4669     if(this.handler){
4670         this.on("select", this.handler, this.scope, true);
4671     }
4672 };
4673 Roo.extend(Roo.ColorPalette, Roo.Component, {
4674     /**
4675      * @cfg {String} itemCls
4676      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4677      */
4678     itemCls : "x-color-palette",
4679     /**
4680      * @cfg {String} value
4681      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4682      * the hex codes are case-sensitive.
4683      */
4684     value : null,
4685     clickEvent:'click',
4686     // private
4687     ctype: "Roo.ColorPalette",
4688
4689     /**
4690      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4691      */
4692     allowReselect : false,
4693
4694     /**
4695      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4696      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4697      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4698      * of colors with the width setting until the box is symmetrical.</p>
4699      * <p>You can override individual colors if needed:</p>
4700      * <pre><code>
4701 var cp = new Roo.ColorPalette();
4702 cp.colors[0] = "FF0000";  // change the first box to red
4703 </code></pre>
4704
4705 Or you can provide a custom array of your own for complete control:
4706 <pre><code>
4707 var cp = new Roo.ColorPalette();
4708 cp.colors = ["000000", "993300", "333300"];
4709 </code></pre>
4710      * @type Array
4711      */
4712     colors : [
4713         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4714         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4715         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4716         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4717         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4718     ],
4719
4720     // private
4721     onRender : function(container, position){
4722         var t = new Roo.MasterTemplate(
4723             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4724         );
4725         var c = this.colors;
4726         for(var i = 0, len = c.length; i < len; i++){
4727             t.add([c[i]]);
4728         }
4729         var el = document.createElement("div");
4730         el.className = this.itemCls;
4731         t.overwrite(el);
4732         container.dom.insertBefore(el, position);
4733         this.el = Roo.get(el);
4734         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4735         if(this.clickEvent != 'click'){
4736             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4737         }
4738     },
4739
4740     // private
4741     afterRender : function(){
4742         Roo.ColorPalette.superclass.afterRender.call(this);
4743         if(this.value){
4744             var s = this.value;
4745             this.value = null;
4746             this.select(s);
4747         }
4748     },
4749
4750     // private
4751     handleClick : function(e, t){
4752         e.preventDefault();
4753         if(!this.disabled){
4754             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4755             this.select(c.toUpperCase());
4756         }
4757     },
4758
4759     /**
4760      * Selects the specified color in the palette (fires the select event)
4761      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4762      */
4763     select : function(color){
4764         color = color.replace("#", "");
4765         if(color != this.value || this.allowReselect){
4766             var el = this.el;
4767             if(this.value){
4768                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4769             }
4770             el.child("a.color-"+color).addClass("x-color-palette-sel");
4771             this.value = color;
4772             this.fireEvent("select", this, color);
4773         }
4774     }
4775 });/*
4776  * Based on:
4777  * Ext JS Library 1.1.1
4778  * Copyright(c) 2006-2007, Ext JS, LLC.
4779  *
4780  * Originally Released Under LGPL - original licence link has changed is not relivant.
4781  *
4782  * Fork - LGPL
4783  * <script type="text/javascript">
4784  */
4785  
4786 /**
4787  * @class Roo.DatePicker
4788  * @extends Roo.Component
4789  * Simple date picker class.
4790  * @constructor
4791  * Create a new DatePicker
4792  * @param {Object} config The config object
4793  */
4794 Roo.DatePicker = function(config){
4795     Roo.DatePicker.superclass.constructor.call(this, config);
4796
4797     this.value = config && config.value ?
4798                  config.value.clearTime() : new Date().clearTime();
4799
4800     this.addEvents({
4801         /**
4802              * @event select
4803              * Fires when a date is selected
4804              * @param {DatePicker} this
4805              * @param {Date} date The selected date
4806              */
4807         'select': true,
4808         /**
4809              * @event monthchange
4810              * Fires when the displayed month changes 
4811              * @param {DatePicker} this
4812              * @param {Date} date The selected month
4813              */
4814         'monthchange': true
4815     });
4816
4817     if(this.handler){
4818         this.on("select", this.handler,  this.scope || this);
4819     }
4820     // build the disabledDatesRE
4821     if(!this.disabledDatesRE && this.disabledDates){
4822         var dd = this.disabledDates;
4823         var re = "(?:";
4824         for(var i = 0; i < dd.length; i++){
4825             re += dd[i];
4826             if(i != dd.length-1) {
4827                 re += "|";
4828             }
4829         }
4830         this.disabledDatesRE = new RegExp(re + ")");
4831     }
4832 };
4833
4834 Roo.extend(Roo.DatePicker, Roo.Component, {
4835     /**
4836      * @cfg {String} todayText
4837      * The text to display on the button that selects the current date (defaults to "Today")
4838      */
4839     todayText : "Today",
4840     /**
4841      * @cfg {String} okText
4842      * The text to display on the ok button
4843      */
4844     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4845     /**
4846      * @cfg {String} cancelText
4847      * The text to display on the cancel button
4848      */
4849     cancelText : "Cancel",
4850     /**
4851      * @cfg {String} todayTip
4852      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4853      */
4854     todayTip : "{0} (Spacebar)",
4855     /**
4856      * @cfg {Date} minDate
4857      * Minimum allowable date (JavaScript date object, defaults to null)
4858      */
4859     minDate : null,
4860     /**
4861      * @cfg {Date} maxDate
4862      * Maximum allowable date (JavaScript date object, defaults to null)
4863      */
4864     maxDate : null,
4865     /**
4866      * @cfg {String} minText
4867      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4868      */
4869     minText : "This date is before the minimum date",
4870     /**
4871      * @cfg {String} maxText
4872      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4873      */
4874     maxText : "This date is after the maximum date",
4875     /**
4876      * @cfg {String} format
4877      * The default date format string which can be overriden for localization support.  The format must be
4878      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4879      */
4880     format : "m/d/y",
4881     /**
4882      * @cfg {Array} disabledDays
4883      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4884      */
4885     disabledDays : null,
4886     /**
4887      * @cfg {String} disabledDaysText
4888      * The tooltip to display when the date falls on a disabled day (defaults to "")
4889      */
4890     disabledDaysText : "",
4891     /**
4892      * @cfg {RegExp} disabledDatesRE
4893      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4894      */
4895     disabledDatesRE : null,
4896     /**
4897      * @cfg {String} disabledDatesText
4898      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4899      */
4900     disabledDatesText : "",
4901     /**
4902      * @cfg {Boolean} constrainToViewport
4903      * True to constrain the date picker to the viewport (defaults to true)
4904      */
4905     constrainToViewport : true,
4906     /**
4907      * @cfg {Array} monthNames
4908      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4909      */
4910     monthNames : Date.monthNames,
4911     /**
4912      * @cfg {Array} dayNames
4913      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4914      */
4915     dayNames : Date.dayNames,
4916     /**
4917      * @cfg {String} nextText
4918      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4919      */
4920     nextText: 'Next Month (Control+Right)',
4921     /**
4922      * @cfg {String} prevText
4923      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4924      */
4925     prevText: 'Previous Month (Control+Left)',
4926     /**
4927      * @cfg {String} monthYearText
4928      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4929      */
4930     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4931     /**
4932      * @cfg {Number} startDay
4933      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4934      */
4935     startDay : 0,
4936     /**
4937      * @cfg {Bool} showClear
4938      * Show a clear button (usefull for date form elements that can be blank.)
4939      */
4940     
4941     showClear: false,
4942     
4943     /**
4944      * Sets the value of the date field
4945      * @param {Date} value The date to set
4946      */
4947     setValue : function(value){
4948         var old = this.value;
4949         
4950         if (typeof(value) == 'string') {
4951          
4952             value = Date.parseDate(value, this.format);
4953         }
4954         if (!value) {
4955             value = new Date();
4956         }
4957         
4958         this.value = value.clearTime(true);
4959         if(this.el){
4960             this.update(this.value);
4961         }
4962     },
4963
4964     /**
4965      * Gets the current selected value of the date field
4966      * @return {Date} The selected date
4967      */
4968     getValue : function(){
4969         return this.value;
4970     },
4971
4972     // private
4973     focus : function(){
4974         if(this.el){
4975             this.update(this.activeDate);
4976         }
4977     },
4978
4979     // privateval
4980     onRender : function(container, position){
4981         
4982         var m = [
4983              '<table cellspacing="0">',
4984                 '<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>',
4985                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
4986         var dn = this.dayNames;
4987         for(var i = 0; i < 7; i++){
4988             var d = this.startDay+i;
4989             if(d > 6){
4990                 d = d-7;
4991             }
4992             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
4993         }
4994         m[m.length] = "</tr></thead><tbody><tr>";
4995         for(var i = 0; i < 42; i++) {
4996             if(i % 7 == 0 && i != 0){
4997                 m[m.length] = "</tr><tr>";
4998             }
4999             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5000         }
5001         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5002             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5003
5004         var el = document.createElement("div");
5005         el.className = "x-date-picker";
5006         el.innerHTML = m.join("");
5007
5008         container.dom.insertBefore(el, position);
5009
5010         this.el = Roo.get(el);
5011         this.eventEl = Roo.get(el.firstChild);
5012
5013         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5014             handler: this.showPrevMonth,
5015             scope: this,
5016             preventDefault:true,
5017             stopDefault:true
5018         });
5019
5020         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5021             handler: this.showNextMonth,
5022             scope: this,
5023             preventDefault:true,
5024             stopDefault:true
5025         });
5026
5027         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5028
5029         this.monthPicker = this.el.down('div.x-date-mp');
5030         this.monthPicker.enableDisplayMode('block');
5031         
5032         var kn = new Roo.KeyNav(this.eventEl, {
5033             "left" : function(e){
5034                 e.ctrlKey ?
5035                     this.showPrevMonth() :
5036                     this.update(this.activeDate.add("d", -1));
5037             },
5038
5039             "right" : function(e){
5040                 e.ctrlKey ?
5041                     this.showNextMonth() :
5042                     this.update(this.activeDate.add("d", 1));
5043             },
5044
5045             "up" : function(e){
5046                 e.ctrlKey ?
5047                     this.showNextYear() :
5048                     this.update(this.activeDate.add("d", -7));
5049             },
5050
5051             "down" : function(e){
5052                 e.ctrlKey ?
5053                     this.showPrevYear() :
5054                     this.update(this.activeDate.add("d", 7));
5055             },
5056
5057             "pageUp" : function(e){
5058                 this.showNextMonth();
5059             },
5060
5061             "pageDown" : function(e){
5062                 this.showPrevMonth();
5063             },
5064
5065             "enter" : function(e){
5066                 e.stopPropagation();
5067                 return true;
5068             },
5069
5070             scope : this
5071         });
5072
5073         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5074
5075         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5076
5077         this.el.unselectable();
5078         
5079         this.cells = this.el.select("table.x-date-inner tbody td");
5080         this.textNodes = this.el.query("table.x-date-inner tbody span");
5081
5082         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5083             text: "&#160;",
5084             tooltip: this.monthYearText
5085         });
5086
5087         this.mbtn.on('click', this.showMonthPicker, this);
5088         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5089
5090
5091         var today = (new Date()).dateFormat(this.format);
5092         
5093         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5094         if (this.showClear) {
5095             baseTb.add( new Roo.Toolbar.Fill());
5096         }
5097         baseTb.add({
5098             text: String.format(this.todayText, today),
5099             tooltip: String.format(this.todayTip, today),
5100             handler: this.selectToday,
5101             scope: this
5102         });
5103         
5104         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5105             
5106         //});
5107         if (this.showClear) {
5108             
5109             baseTb.add( new Roo.Toolbar.Fill());
5110             baseTb.add({
5111                 text: '&#160;',
5112                 cls: 'x-btn-icon x-btn-clear',
5113                 handler: function() {
5114                     //this.value = '';
5115                     this.fireEvent("select", this, '');
5116                 },
5117                 scope: this
5118             });
5119         }
5120         
5121         
5122         if(Roo.isIE){
5123             this.el.repaint();
5124         }
5125         this.update(this.value);
5126     },
5127
5128     createMonthPicker : function(){
5129         if(!this.monthPicker.dom.firstChild){
5130             var buf = ['<table border="0" cellspacing="0">'];
5131             for(var i = 0; i < 6; i++){
5132                 buf.push(
5133                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5134                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5135                     i == 0 ?
5136                     '<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>' :
5137                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5138                 );
5139             }
5140             buf.push(
5141                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5142                     this.okText,
5143                     '</button><button type="button" class="x-date-mp-cancel">',
5144                     this.cancelText,
5145                     '</button></td></tr>',
5146                 '</table>'
5147             );
5148             this.monthPicker.update(buf.join(''));
5149             this.monthPicker.on('click', this.onMonthClick, this);
5150             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5151
5152             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5153             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5154
5155             this.mpMonths.each(function(m, a, i){
5156                 i += 1;
5157                 if((i%2) == 0){
5158                     m.dom.xmonth = 5 + Math.round(i * .5);
5159                 }else{
5160                     m.dom.xmonth = Math.round((i-1) * .5);
5161                 }
5162             });
5163         }
5164     },
5165
5166     showMonthPicker : function(){
5167         this.createMonthPicker();
5168         var size = this.el.getSize();
5169         this.monthPicker.setSize(size);
5170         this.monthPicker.child('table').setSize(size);
5171
5172         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5173         this.updateMPMonth(this.mpSelMonth);
5174         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5175         this.updateMPYear(this.mpSelYear);
5176
5177         this.monthPicker.slideIn('t', {duration:.2});
5178     },
5179
5180     updateMPYear : function(y){
5181         this.mpyear = y;
5182         var ys = this.mpYears.elements;
5183         for(var i = 1; i <= 10; i++){
5184             var td = ys[i-1], y2;
5185             if((i%2) == 0){
5186                 y2 = y + Math.round(i * .5);
5187                 td.firstChild.innerHTML = y2;
5188                 td.xyear = y2;
5189             }else{
5190                 y2 = y - (5-Math.round(i * .5));
5191                 td.firstChild.innerHTML = y2;
5192                 td.xyear = y2;
5193             }
5194             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5195         }
5196     },
5197
5198     updateMPMonth : function(sm){
5199         this.mpMonths.each(function(m, a, i){
5200             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5201         });
5202     },
5203
5204     selectMPMonth: function(m){
5205         
5206     },
5207
5208     onMonthClick : function(e, t){
5209         e.stopEvent();
5210         var el = new Roo.Element(t), pn;
5211         if(el.is('button.x-date-mp-cancel')){
5212             this.hideMonthPicker();
5213         }
5214         else if(el.is('button.x-date-mp-ok')){
5215             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5216             this.hideMonthPicker();
5217         }
5218         else if(pn = el.up('td.x-date-mp-month', 2)){
5219             this.mpMonths.removeClass('x-date-mp-sel');
5220             pn.addClass('x-date-mp-sel');
5221             this.mpSelMonth = pn.dom.xmonth;
5222         }
5223         else if(pn = el.up('td.x-date-mp-year', 2)){
5224             this.mpYears.removeClass('x-date-mp-sel');
5225             pn.addClass('x-date-mp-sel');
5226             this.mpSelYear = pn.dom.xyear;
5227         }
5228         else if(el.is('a.x-date-mp-prev')){
5229             this.updateMPYear(this.mpyear-10);
5230         }
5231         else if(el.is('a.x-date-mp-next')){
5232             this.updateMPYear(this.mpyear+10);
5233         }
5234     },
5235
5236     onMonthDblClick : function(e, t){
5237         e.stopEvent();
5238         var el = new Roo.Element(t), pn;
5239         if(pn = el.up('td.x-date-mp-month', 2)){
5240             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5241             this.hideMonthPicker();
5242         }
5243         else if(pn = el.up('td.x-date-mp-year', 2)){
5244             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5245             this.hideMonthPicker();
5246         }
5247     },
5248
5249     hideMonthPicker : function(disableAnim){
5250         if(this.monthPicker){
5251             if(disableAnim === true){
5252                 this.monthPicker.hide();
5253             }else{
5254                 this.monthPicker.slideOut('t', {duration:.2});
5255             }
5256         }
5257     },
5258
5259     // private
5260     showPrevMonth : function(e){
5261         this.update(this.activeDate.add("mo", -1));
5262     },
5263
5264     // private
5265     showNextMonth : function(e){
5266         this.update(this.activeDate.add("mo", 1));
5267     },
5268
5269     // private
5270     showPrevYear : function(){
5271         this.update(this.activeDate.add("y", -1));
5272     },
5273
5274     // private
5275     showNextYear : function(){
5276         this.update(this.activeDate.add("y", 1));
5277     },
5278
5279     // private
5280     handleMouseWheel : function(e){
5281         var delta = e.getWheelDelta();
5282         if(delta > 0){
5283             this.showPrevMonth();
5284             e.stopEvent();
5285         } else if(delta < 0){
5286             this.showNextMonth();
5287             e.stopEvent();
5288         }
5289     },
5290
5291     // private
5292     handleDateClick : function(e, t){
5293         e.stopEvent();
5294         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5295             this.setValue(new Date(t.dateValue));
5296             this.fireEvent("select", this, this.value);
5297         }
5298     },
5299
5300     // private
5301     selectToday : function(){
5302         this.setValue(new Date().clearTime());
5303         this.fireEvent("select", this, this.value);
5304     },
5305
5306     // private
5307     update : function(date)
5308     {
5309         var vd = this.activeDate;
5310         this.activeDate = date;
5311         if(vd && this.el){
5312             var t = date.getTime();
5313             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5314                 this.cells.removeClass("x-date-selected");
5315                 this.cells.each(function(c){
5316                    if(c.dom.firstChild.dateValue == t){
5317                        c.addClass("x-date-selected");
5318                        setTimeout(function(){
5319                             try{c.dom.firstChild.focus();}catch(e){}
5320                        }, 50);
5321                        return false;
5322                    }
5323                 });
5324                 return;
5325             }
5326         }
5327         
5328         var days = date.getDaysInMonth();
5329         var firstOfMonth = date.getFirstDateOfMonth();
5330         var startingPos = firstOfMonth.getDay()-this.startDay;
5331
5332         if(startingPos <= this.startDay){
5333             startingPos += 7;
5334         }
5335
5336         var pm = date.add("mo", -1);
5337         var prevStart = pm.getDaysInMonth()-startingPos;
5338
5339         var cells = this.cells.elements;
5340         var textEls = this.textNodes;
5341         days += startingPos;
5342
5343         // convert everything to numbers so it's fast
5344         var day = 86400000;
5345         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5346         var today = new Date().clearTime().getTime();
5347         var sel = date.clearTime().getTime();
5348         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5349         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5350         var ddMatch = this.disabledDatesRE;
5351         var ddText = this.disabledDatesText;
5352         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5353         var ddaysText = this.disabledDaysText;
5354         var format = this.format;
5355
5356         var setCellClass = function(cal, cell){
5357             cell.title = "";
5358             var t = d.getTime();
5359             cell.firstChild.dateValue = t;
5360             if(t == today){
5361                 cell.className += " x-date-today";
5362                 cell.title = cal.todayText;
5363             }
5364             if(t == sel){
5365                 cell.className += " x-date-selected";
5366                 setTimeout(function(){
5367                     try{cell.firstChild.focus();}catch(e){}
5368                 }, 50);
5369             }
5370             // disabling
5371             if(t < min) {
5372                 cell.className = " x-date-disabled";
5373                 cell.title = cal.minText;
5374                 return;
5375             }
5376             if(t > max) {
5377                 cell.className = " x-date-disabled";
5378                 cell.title = cal.maxText;
5379                 return;
5380             }
5381             if(ddays){
5382                 if(ddays.indexOf(d.getDay()) != -1){
5383                     cell.title = ddaysText;
5384                     cell.className = " x-date-disabled";
5385                 }
5386             }
5387             if(ddMatch && format){
5388                 var fvalue = d.dateFormat(format);
5389                 if(ddMatch.test(fvalue)){
5390                     cell.title = ddText.replace("%0", fvalue);
5391                     cell.className = " x-date-disabled";
5392                 }
5393             }
5394         };
5395
5396         var i = 0;
5397         for(; i < startingPos; i++) {
5398             textEls[i].innerHTML = (++prevStart);
5399             d.setDate(d.getDate()+1);
5400             cells[i].className = "x-date-prevday";
5401             setCellClass(this, cells[i]);
5402         }
5403         for(; i < days; i++){
5404             intDay = i - startingPos + 1;
5405             textEls[i].innerHTML = (intDay);
5406             d.setDate(d.getDate()+1);
5407             cells[i].className = "x-date-active";
5408             setCellClass(this, cells[i]);
5409         }
5410         var extraDays = 0;
5411         for(; i < 42; i++) {
5412              textEls[i].innerHTML = (++extraDays);
5413              d.setDate(d.getDate()+1);
5414              cells[i].className = "x-date-nextday";
5415              setCellClass(this, cells[i]);
5416         }
5417
5418         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5419         this.fireEvent('monthchange', this, date);
5420         
5421         if(!this.internalRender){
5422             var main = this.el.dom.firstChild;
5423             var w = main.offsetWidth;
5424             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5425             Roo.fly(main).setWidth(w);
5426             this.internalRender = true;
5427             // opera does not respect the auto grow header center column
5428             // then, after it gets a width opera refuses to recalculate
5429             // without a second pass
5430             if(Roo.isOpera && !this.secondPass){
5431                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5432                 this.secondPass = true;
5433                 this.update.defer(10, this, [date]);
5434             }
5435         }
5436         
5437         
5438     }
5439 });        /*
5440  * Based on:
5441  * Ext JS Library 1.1.1
5442  * Copyright(c) 2006-2007, Ext JS, LLC.
5443  *
5444  * Originally Released Under LGPL - original licence link has changed is not relivant.
5445  *
5446  * Fork - LGPL
5447  * <script type="text/javascript">
5448  */
5449 /**
5450  * @class Roo.TabPanel
5451  * @extends Roo.util.Observable
5452  * A lightweight tab container.
5453  * <br><br>
5454  * Usage:
5455  * <pre><code>
5456 // basic tabs 1, built from existing content
5457 var tabs = new Roo.TabPanel("tabs1");
5458 tabs.addTab("script", "View Script");
5459 tabs.addTab("markup", "View Markup");
5460 tabs.activate("script");
5461
5462 // more advanced tabs, built from javascript
5463 var jtabs = new Roo.TabPanel("jtabs");
5464 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5465
5466 // set up the UpdateManager
5467 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5468 var updater = tab2.getUpdateManager();
5469 updater.setDefaultUrl("ajax1.htm");
5470 tab2.on('activate', updater.refresh, updater, true);
5471
5472 // Use setUrl for Ajax loading
5473 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5474 tab3.setUrl("ajax2.htm", null, true);
5475
5476 // Disabled tab
5477 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5478 tab4.disable();
5479
5480 jtabs.activate("jtabs-1");
5481  * </code></pre>
5482  * @constructor
5483  * Create a new TabPanel.
5484  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5485  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5486  */
5487 Roo.TabPanel = function(container, config){
5488     /**
5489     * The container element for this TabPanel.
5490     * @type Roo.Element
5491     */
5492     this.el = Roo.get(container, true);
5493     if(config){
5494         if(typeof config == "boolean"){
5495             this.tabPosition = config ? "bottom" : "top";
5496         }else{
5497             Roo.apply(this, config);
5498         }
5499     }
5500     if(this.tabPosition == "bottom"){
5501         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5502         this.el.addClass("x-tabs-bottom");
5503     }
5504     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5505     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5506     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5507     if(Roo.isIE){
5508         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5509     }
5510     if(this.tabPosition != "bottom"){
5511         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5512          * @type Roo.Element
5513          */
5514         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5515         this.el.addClass("x-tabs-top");
5516     }
5517     this.items = [];
5518
5519     this.bodyEl.setStyle("position", "relative");
5520
5521     this.active = null;
5522     this.activateDelegate = this.activate.createDelegate(this);
5523
5524     this.addEvents({
5525         /**
5526          * @event tabchange
5527          * Fires when the active tab changes
5528          * @param {Roo.TabPanel} this
5529          * @param {Roo.TabPanelItem} activePanel The new active tab
5530          */
5531         "tabchange": true,
5532         /**
5533          * @event beforetabchange
5534          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5535          * @param {Roo.TabPanel} this
5536          * @param {Object} e Set cancel to true on this object to cancel the tab change
5537          * @param {Roo.TabPanelItem} tab The tab being changed to
5538          */
5539         "beforetabchange" : true
5540     });
5541
5542     Roo.EventManager.onWindowResize(this.onResize, this);
5543     this.cpad = this.el.getPadding("lr");
5544     this.hiddenCount = 0;
5545
5546
5547     // toolbar on the tabbar support...
5548     if (this.toolbar) {
5549         var tcfg = this.toolbar;
5550         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5551         this.toolbar = new Roo.Toolbar(tcfg);
5552         if (Roo.isSafari) {
5553             var tbl = tcfg.container.child('table', true);
5554             tbl.setAttribute('width', '100%');
5555         }
5556         
5557     }
5558    
5559
5560
5561     Roo.TabPanel.superclass.constructor.call(this);
5562 };
5563
5564 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5565     /*
5566      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5567      */
5568     tabPosition : "top",
5569     /*
5570      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5571      */
5572     currentTabWidth : 0,
5573     /*
5574      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5575      */
5576     minTabWidth : 40,
5577     /*
5578      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5579      */
5580     maxTabWidth : 250,
5581     /*
5582      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5583      */
5584     preferredTabWidth : 175,
5585     /*
5586      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5587      */
5588     resizeTabs : false,
5589     /*
5590      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5591      */
5592     monitorResize : true,
5593     /*
5594      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5595      */
5596     toolbar : false,
5597
5598     /**
5599      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5600      * @param {String} id The id of the div to use <b>or create</b>
5601      * @param {String} text The text for the tab
5602      * @param {String} content (optional) Content to put in the TabPanelItem body
5603      * @param {Boolean} closable (optional) True to create a close icon on the tab
5604      * @return {Roo.TabPanelItem} The created TabPanelItem
5605      */
5606     addTab : function(id, text, content, closable){
5607         var item = new Roo.TabPanelItem(this, id, text, closable);
5608         this.addTabItem(item);
5609         if(content){
5610             item.setContent(content);
5611         }
5612         return item;
5613     },
5614
5615     /**
5616      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5617      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5618      * @return {Roo.TabPanelItem}
5619      */
5620     getTab : function(id){
5621         return this.items[id];
5622     },
5623
5624     /**
5625      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5626      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5627      */
5628     hideTab : function(id){
5629         var t = this.items[id];
5630         if(!t.isHidden()){
5631            t.setHidden(true);
5632            this.hiddenCount++;
5633            this.autoSizeTabs();
5634         }
5635     },
5636
5637     /**
5638      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5639      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5640      */
5641     unhideTab : function(id){
5642         var t = this.items[id];
5643         if(t.isHidden()){
5644            t.setHidden(false);
5645            this.hiddenCount--;
5646            this.autoSizeTabs();
5647         }
5648     },
5649
5650     /**
5651      * Adds an existing {@link Roo.TabPanelItem}.
5652      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5653      */
5654     addTabItem : function(item){
5655         this.items[item.id] = item;
5656         this.items.push(item);
5657         if(this.resizeTabs){
5658            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5659            this.autoSizeTabs();
5660         }else{
5661             item.autoSize();
5662         }
5663     },
5664
5665     /**
5666      * Removes a {@link Roo.TabPanelItem}.
5667      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5668      */
5669     removeTab : function(id){
5670         var items = this.items;
5671         var tab = items[id];
5672         if(!tab) { return; }
5673         var index = items.indexOf(tab);
5674         if(this.active == tab && items.length > 1){
5675             var newTab = this.getNextAvailable(index);
5676             if(newTab) {
5677                 newTab.activate();
5678             }
5679         }
5680         this.stripEl.dom.removeChild(tab.pnode.dom);
5681         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5682             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5683         }
5684         items.splice(index, 1);
5685         delete this.items[tab.id];
5686         tab.fireEvent("close", tab);
5687         tab.purgeListeners();
5688         this.autoSizeTabs();
5689     },
5690
5691     getNextAvailable : function(start){
5692         var items = this.items;
5693         var index = start;
5694         // look for a next tab that will slide over to
5695         // replace the one being removed
5696         while(index < items.length){
5697             var item = items[++index];
5698             if(item && !item.isHidden()){
5699                 return item;
5700             }
5701         }
5702         // if one isn't found select the previous tab (on the left)
5703         index = start;
5704         while(index >= 0){
5705             var item = items[--index];
5706             if(item && !item.isHidden()){
5707                 return item;
5708             }
5709         }
5710         return null;
5711     },
5712
5713     /**
5714      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5715      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5716      */
5717     disableTab : function(id){
5718         var tab = this.items[id];
5719         if(tab && this.active != tab){
5720             tab.disable();
5721         }
5722     },
5723
5724     /**
5725      * Enables a {@link Roo.TabPanelItem} that is disabled.
5726      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5727      */
5728     enableTab : function(id){
5729         var tab = this.items[id];
5730         tab.enable();
5731     },
5732
5733     /**
5734      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5735      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5736      * @return {Roo.TabPanelItem} The TabPanelItem.
5737      */
5738     activate : function(id){
5739         var tab = this.items[id];
5740         if(!tab){
5741             return null;
5742         }
5743         if(tab == this.active || tab.disabled){
5744             return tab;
5745         }
5746         var e = {};
5747         this.fireEvent("beforetabchange", this, e, tab);
5748         if(e.cancel !== true && !tab.disabled){
5749             if(this.active){
5750                 this.active.hide();
5751             }
5752             this.active = this.items[id];
5753             this.active.show();
5754             this.fireEvent("tabchange", this, this.active);
5755         }
5756         return tab;
5757     },
5758
5759     /**
5760      * Gets the active {@link Roo.TabPanelItem}.
5761      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5762      */
5763     getActiveTab : function(){
5764         return this.active;
5765     },
5766
5767     /**
5768      * Updates the tab body element to fit the height of the container element
5769      * for overflow scrolling
5770      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5771      */
5772     syncHeight : function(targetHeight){
5773         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5774         var bm = this.bodyEl.getMargins();
5775         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5776         this.bodyEl.setHeight(newHeight);
5777         return newHeight;
5778     },
5779
5780     onResize : function(){
5781         if(this.monitorResize){
5782             this.autoSizeTabs();
5783         }
5784     },
5785
5786     /**
5787      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5788      */
5789     beginUpdate : function(){
5790         this.updating = true;
5791     },
5792
5793     /**
5794      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5795      */
5796     endUpdate : function(){
5797         this.updating = false;
5798         this.autoSizeTabs();
5799     },
5800
5801     /**
5802      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5803      */
5804     autoSizeTabs : function(){
5805         var count = this.items.length;
5806         var vcount = count - this.hiddenCount;
5807         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5808             return;
5809         }
5810         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5811         var availWidth = Math.floor(w / vcount);
5812         var b = this.stripBody;
5813         if(b.getWidth() > w){
5814             var tabs = this.items;
5815             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5816             if(availWidth < this.minTabWidth){
5817                 /*if(!this.sleft){    // incomplete scrolling code
5818                     this.createScrollButtons();
5819                 }
5820                 this.showScroll();
5821                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5822             }
5823         }else{
5824             if(this.currentTabWidth < this.preferredTabWidth){
5825                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5826             }
5827         }
5828     },
5829
5830     /**
5831      * Returns the number of tabs in this TabPanel.
5832      * @return {Number}
5833      */
5834      getCount : function(){
5835          return this.items.length;
5836      },
5837
5838     /**
5839      * Resizes all the tabs to the passed width
5840      * @param {Number} The new width
5841      */
5842     setTabWidth : function(width){
5843         this.currentTabWidth = width;
5844         for(var i = 0, len = this.items.length; i < len; i++) {
5845                 if(!this.items[i].isHidden()) {
5846                 this.items[i].setWidth(width);
5847             }
5848         }
5849     },
5850
5851     /**
5852      * Destroys this TabPanel
5853      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5854      */
5855     destroy : function(removeEl){
5856         Roo.EventManager.removeResizeListener(this.onResize, this);
5857         for(var i = 0, len = this.items.length; i < len; i++){
5858             this.items[i].purgeListeners();
5859         }
5860         if(removeEl === true){
5861             this.el.update("");
5862             this.el.remove();
5863         }
5864     }
5865 });
5866
5867 /**
5868  * @class Roo.TabPanelItem
5869  * @extends Roo.util.Observable
5870  * Represents an individual item (tab plus body) in a TabPanel.
5871  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5872  * @param {String} id The id of this TabPanelItem
5873  * @param {String} text The text for the tab of this TabPanelItem
5874  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5875  */
5876 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5877     /**
5878      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5879      * @type Roo.TabPanel
5880      */
5881     this.tabPanel = tabPanel;
5882     /**
5883      * The id for this TabPanelItem
5884      * @type String
5885      */
5886     this.id = id;
5887     /** @private */
5888     this.disabled = false;
5889     /** @private */
5890     this.text = text;
5891     /** @private */
5892     this.loaded = false;
5893     this.closable = closable;
5894
5895     /**
5896      * The body element for this TabPanelItem.
5897      * @type Roo.Element
5898      */
5899     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5900     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5901     this.bodyEl.setStyle("display", "block");
5902     this.bodyEl.setStyle("zoom", "1");
5903     this.hideAction();
5904
5905     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5906     /** @private */
5907     this.el = Roo.get(els.el, true);
5908     this.inner = Roo.get(els.inner, true);
5909     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5910     this.pnode = Roo.get(els.el.parentNode, true);
5911     this.el.on("mousedown", this.onTabMouseDown, this);
5912     this.el.on("click", this.onTabClick, this);
5913     /** @private */
5914     if(closable){
5915         var c = Roo.get(els.close, true);
5916         c.dom.title = this.closeText;
5917         c.addClassOnOver("close-over");
5918         c.on("click", this.closeClick, this);
5919      }
5920
5921     this.addEvents({
5922          /**
5923          * @event activate
5924          * Fires when this tab becomes the active tab.
5925          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5926          * @param {Roo.TabPanelItem} this
5927          */
5928         "activate": true,
5929         /**
5930          * @event beforeclose
5931          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5932          * @param {Roo.TabPanelItem} this
5933          * @param {Object} e Set cancel to true on this object to cancel the close.
5934          */
5935         "beforeclose": true,
5936         /**
5937          * @event close
5938          * Fires when this tab is closed.
5939          * @param {Roo.TabPanelItem} this
5940          */
5941          "close": true,
5942         /**
5943          * @event deactivate
5944          * Fires when this tab is no longer the active tab.
5945          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5946          * @param {Roo.TabPanelItem} this
5947          */
5948          "deactivate" : true
5949     });
5950     this.hidden = false;
5951
5952     Roo.TabPanelItem.superclass.constructor.call(this);
5953 };
5954
5955 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5956     purgeListeners : function(){
5957        Roo.util.Observable.prototype.purgeListeners.call(this);
5958        this.el.removeAllListeners();
5959     },
5960     /**
5961      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5962      */
5963     show : function(){
5964         this.pnode.addClass("on");
5965         this.showAction();
5966         if(Roo.isOpera){
5967             this.tabPanel.stripWrap.repaint();
5968         }
5969         this.fireEvent("activate", this.tabPanel, this);
5970     },
5971
5972     /**
5973      * Returns true if this tab is the active tab.
5974      * @return {Boolean}
5975      */
5976     isActive : function(){
5977         return this.tabPanel.getActiveTab() == this;
5978     },
5979
5980     /**
5981      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
5982      */
5983     hide : function(){
5984         this.pnode.removeClass("on");
5985         this.hideAction();
5986         this.fireEvent("deactivate", this.tabPanel, this);
5987     },
5988
5989     hideAction : function(){
5990         this.bodyEl.hide();
5991         this.bodyEl.setStyle("position", "absolute");
5992         this.bodyEl.setLeft("-20000px");
5993         this.bodyEl.setTop("-20000px");
5994     },
5995
5996     showAction : function(){
5997         this.bodyEl.setStyle("position", "relative");
5998         this.bodyEl.setTop("");
5999         this.bodyEl.setLeft("");
6000         this.bodyEl.show();
6001     },
6002
6003     /**
6004      * Set the tooltip for the tab.
6005      * @param {String} tooltip The tab's tooltip
6006      */
6007     setTooltip : function(text){
6008         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6009             this.textEl.dom.qtip = text;
6010             this.textEl.dom.removeAttribute('title');
6011         }else{
6012             this.textEl.dom.title = text;
6013         }
6014     },
6015
6016     onTabClick : function(e){
6017         e.preventDefault();
6018         this.tabPanel.activate(this.id);
6019     },
6020
6021     onTabMouseDown : function(e){
6022         e.preventDefault();
6023         this.tabPanel.activate(this.id);
6024     },
6025
6026     getWidth : function(){
6027         return this.inner.getWidth();
6028     },
6029
6030     setWidth : function(width){
6031         var iwidth = width - this.pnode.getPadding("lr");
6032         this.inner.setWidth(iwidth);
6033         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6034         this.pnode.setWidth(width);
6035     },
6036
6037     /**
6038      * Show or hide the tab
6039      * @param {Boolean} hidden True to hide or false to show.
6040      */
6041     setHidden : function(hidden){
6042         this.hidden = hidden;
6043         this.pnode.setStyle("display", hidden ? "none" : "");
6044     },
6045
6046     /**
6047      * Returns true if this tab is "hidden"
6048      * @return {Boolean}
6049      */
6050     isHidden : function(){
6051         return this.hidden;
6052     },
6053
6054     /**
6055      * Returns the text for this tab
6056      * @return {String}
6057      */
6058     getText : function(){
6059         return this.text;
6060     },
6061
6062     autoSize : function(){
6063         //this.el.beginMeasure();
6064         this.textEl.setWidth(1);
6065         /*
6066          *  #2804 [new] Tabs in Roojs
6067          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6068          */
6069         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6070         //this.el.endMeasure();
6071     },
6072
6073     /**
6074      * Sets the text for the tab (Note: this also sets the tooltip text)
6075      * @param {String} text The tab's text and tooltip
6076      */
6077     setText : function(text){
6078         this.text = text;
6079         this.textEl.update(text);
6080         this.setTooltip(text);
6081         if(!this.tabPanel.resizeTabs){
6082             this.autoSize();
6083         }
6084     },
6085     /**
6086      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6087      */
6088     activate : function(){
6089         this.tabPanel.activate(this.id);
6090     },
6091
6092     /**
6093      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6094      */
6095     disable : function(){
6096         if(this.tabPanel.active != this){
6097             this.disabled = true;
6098             this.pnode.addClass("disabled");
6099         }
6100     },
6101
6102     /**
6103      * Enables this TabPanelItem if it was previously disabled.
6104      */
6105     enable : function(){
6106         this.disabled = false;
6107         this.pnode.removeClass("disabled");
6108     },
6109
6110     /**
6111      * Sets the content for this TabPanelItem.
6112      * @param {String} content The content
6113      * @param {Boolean} loadScripts true to look for and load scripts
6114      */
6115     setContent : function(content, loadScripts){
6116         this.bodyEl.update(content, loadScripts);
6117     },
6118
6119     /**
6120      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6121      * @return {Roo.UpdateManager} The UpdateManager
6122      */
6123     getUpdateManager : function(){
6124         return this.bodyEl.getUpdateManager();
6125     },
6126
6127     /**
6128      * Set a URL to be used to load the content for this TabPanelItem.
6129      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6130      * @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)
6131      * @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)
6132      * @return {Roo.UpdateManager} The UpdateManager
6133      */
6134     setUrl : function(url, params, loadOnce){
6135         if(this.refreshDelegate){
6136             this.un('activate', this.refreshDelegate);
6137         }
6138         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6139         this.on("activate", this.refreshDelegate);
6140         return this.bodyEl.getUpdateManager();
6141     },
6142
6143     /** @private */
6144     _handleRefresh : function(url, params, loadOnce){
6145         if(!loadOnce || !this.loaded){
6146             var updater = this.bodyEl.getUpdateManager();
6147             updater.update(url, params, this._setLoaded.createDelegate(this));
6148         }
6149     },
6150
6151     /**
6152      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6153      *   Will fail silently if the setUrl method has not been called.
6154      *   This does not activate the panel, just updates its content.
6155      */
6156     refresh : function(){
6157         if(this.refreshDelegate){
6158            this.loaded = false;
6159            this.refreshDelegate();
6160         }
6161     },
6162
6163     /** @private */
6164     _setLoaded : function(){
6165         this.loaded = true;
6166     },
6167
6168     /** @private */
6169     closeClick : function(e){
6170         var o = {};
6171         e.stopEvent();
6172         this.fireEvent("beforeclose", this, o);
6173         if(o.cancel !== true){
6174             this.tabPanel.removeTab(this.id);
6175         }
6176     },
6177     /**
6178      * The text displayed in the tooltip for the close icon.
6179      * @type String
6180      */
6181     closeText : "Close this tab"
6182 });
6183
6184 /** @private */
6185 Roo.TabPanel.prototype.createStrip = function(container){
6186     var strip = document.createElement("div");
6187     strip.className = "x-tabs-wrap";
6188     container.appendChild(strip);
6189     return strip;
6190 };
6191 /** @private */
6192 Roo.TabPanel.prototype.createStripList = function(strip){
6193     // div wrapper for retard IE
6194     // returns the "tr" element.
6195     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6196         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6197         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6198     return strip.firstChild.firstChild.firstChild.firstChild;
6199 };
6200 /** @private */
6201 Roo.TabPanel.prototype.createBody = function(container){
6202     var body = document.createElement("div");
6203     Roo.id(body, "tab-body");
6204     Roo.fly(body).addClass("x-tabs-body");
6205     container.appendChild(body);
6206     return body;
6207 };
6208 /** @private */
6209 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6210     var body = Roo.getDom(id);
6211     if(!body){
6212         body = document.createElement("div");
6213         body.id = id;
6214     }
6215     Roo.fly(body).addClass("x-tabs-item-body");
6216     bodyEl.insertBefore(body, bodyEl.firstChild);
6217     return body;
6218 };
6219 /** @private */
6220 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6221     var td = document.createElement("td");
6222     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6223     //stripEl.appendChild(td);
6224     if(closable){
6225         td.className = "x-tabs-closable";
6226         if(!this.closeTpl){
6227             this.closeTpl = new Roo.Template(
6228                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6229                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6230                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6231             );
6232         }
6233         var el = this.closeTpl.overwrite(td, {"text": text});
6234         var close = el.getElementsByTagName("div")[0];
6235         var inner = el.getElementsByTagName("em")[0];
6236         return {"el": el, "close": close, "inner": inner};
6237     } else {
6238         if(!this.tabTpl){
6239             this.tabTpl = new Roo.Template(
6240                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6241                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6242             );
6243         }
6244         var el = this.tabTpl.overwrite(td, {"text": text});
6245         var inner = el.getElementsByTagName("em")[0];
6246         return {"el": el, "inner": inner};
6247     }
6248 };/*
6249  * Based on:
6250  * Ext JS Library 1.1.1
6251  * Copyright(c) 2006-2007, Ext JS, LLC.
6252  *
6253  * Originally Released Under LGPL - original licence link has changed is not relivant.
6254  *
6255  * Fork - LGPL
6256  * <script type="text/javascript">
6257  */
6258
6259 /**
6260  * @class Roo.Button
6261  * @extends Roo.util.Observable
6262  * Simple Button class
6263  * @cfg {String} text The button text
6264  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6265  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6266  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6267  * @cfg {Object} scope The scope of the handler
6268  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6269  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6270  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6271  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6272  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6273  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6274    applies if enableToggle = true)
6275  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6276  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6277   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6278  * @constructor
6279  * Create a new button
6280  * @param {Object} config The config object
6281  */
6282 Roo.Button = function(renderTo, config)
6283 {
6284     if (!config) {
6285         config = renderTo;
6286         renderTo = config.renderTo || false;
6287     }
6288     
6289     Roo.apply(this, config);
6290     this.addEvents({
6291         /**
6292              * @event click
6293              * Fires when this button is clicked
6294              * @param {Button} this
6295              * @param {EventObject} e The click event
6296              */
6297             "click" : true,
6298         /**
6299              * @event toggle
6300              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6301              * @param {Button} this
6302              * @param {Boolean} pressed
6303              */
6304             "toggle" : true,
6305         /**
6306              * @event mouseover
6307              * Fires when the mouse hovers over the button
6308              * @param {Button} this
6309              * @param {Event} e The event object
6310              */
6311         'mouseover' : true,
6312         /**
6313              * @event mouseout
6314              * Fires when the mouse exits the button
6315              * @param {Button} this
6316              * @param {Event} e The event object
6317              */
6318         'mouseout': true,
6319          /**
6320              * @event render
6321              * Fires when the button is rendered
6322              * @param {Button} this
6323              */
6324         'render': true
6325     });
6326     if(this.menu){
6327         this.menu = Roo.menu.MenuMgr.get(this.menu);
6328     }
6329     // register listeners first!!  - so render can be captured..
6330     Roo.util.Observable.call(this);
6331     if(renderTo){
6332         this.render(renderTo);
6333     }
6334     
6335   
6336 };
6337
6338 Roo.extend(Roo.Button, Roo.util.Observable, {
6339     /**
6340      * 
6341      */
6342     
6343     /**
6344      * Read-only. True if this button is hidden
6345      * @type Boolean
6346      */
6347     hidden : false,
6348     /**
6349      * Read-only. True if this button is disabled
6350      * @type Boolean
6351      */
6352     disabled : false,
6353     /**
6354      * Read-only. True if this button is pressed (only if enableToggle = true)
6355      * @type Boolean
6356      */
6357     pressed : false,
6358
6359     /**
6360      * @cfg {Number} tabIndex 
6361      * The DOM tabIndex for this button (defaults to undefined)
6362      */
6363     tabIndex : undefined,
6364
6365     /**
6366      * @cfg {Boolean} enableToggle
6367      * True to enable pressed/not pressed toggling (defaults to false)
6368      */
6369     enableToggle: false,
6370     /**
6371      * @cfg {Mixed} menu
6372      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6373      */
6374     menu : undefined,
6375     /**
6376      * @cfg {String} menuAlign
6377      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6378      */
6379     menuAlign : "tl-bl?",
6380
6381     /**
6382      * @cfg {String} iconCls
6383      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6384      */
6385     iconCls : undefined,
6386     /**
6387      * @cfg {String} type
6388      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6389      */
6390     type : 'button',
6391
6392     // private
6393     menuClassTarget: 'tr',
6394
6395     /**
6396      * @cfg {String} clickEvent
6397      * The type of event to map to the button's event handler (defaults to 'click')
6398      */
6399     clickEvent : 'click',
6400
6401     /**
6402      * @cfg {Boolean} handleMouseEvents
6403      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6404      */
6405     handleMouseEvents : true,
6406
6407     /**
6408      * @cfg {String} tooltipType
6409      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6410      */
6411     tooltipType : 'qtip',
6412
6413     /**
6414      * @cfg {String} cls
6415      * A CSS class to apply to the button's main element.
6416      */
6417     
6418     /**
6419      * @cfg {Roo.Template} template (Optional)
6420      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6421      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6422      * require code modifications if required elements (e.g. a button) aren't present.
6423      */
6424
6425     // private
6426     render : function(renderTo){
6427         var btn;
6428         if(this.hideParent){
6429             this.parentEl = Roo.get(renderTo);
6430         }
6431         if(!this.dhconfig){
6432             if(!this.template){
6433                 if(!Roo.Button.buttonTemplate){
6434                     // hideous table template
6435                     Roo.Button.buttonTemplate = new Roo.Template(
6436                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6437                         '<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>',
6438                         "</tr></tbody></table>");
6439                 }
6440                 this.template = Roo.Button.buttonTemplate;
6441             }
6442             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6443             var btnEl = btn.child("button:first");
6444             btnEl.on('focus', this.onFocus, this);
6445             btnEl.on('blur', this.onBlur, this);
6446             if(this.cls){
6447                 btn.addClass(this.cls);
6448             }
6449             if(this.icon){
6450                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6451             }
6452             if(this.iconCls){
6453                 btnEl.addClass(this.iconCls);
6454                 if(!this.cls){
6455                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6456                 }
6457             }
6458             if(this.tabIndex !== undefined){
6459                 btnEl.dom.tabIndex = this.tabIndex;
6460             }
6461             if(this.tooltip){
6462                 if(typeof this.tooltip == 'object'){
6463                     Roo.QuickTips.tips(Roo.apply({
6464                           target: btnEl.id
6465                     }, this.tooltip));
6466                 } else {
6467                     btnEl.dom[this.tooltipType] = this.tooltip;
6468                 }
6469             }
6470         }else{
6471             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6472         }
6473         this.el = btn;
6474         if(this.id){
6475             this.el.dom.id = this.el.id = this.id;
6476         }
6477         if(this.menu){
6478             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6479             this.menu.on("show", this.onMenuShow, this);
6480             this.menu.on("hide", this.onMenuHide, this);
6481         }
6482         btn.addClass("x-btn");
6483         if(Roo.isIE && !Roo.isIE7){
6484             this.autoWidth.defer(1, this);
6485         }else{
6486             this.autoWidth();
6487         }
6488         if(this.handleMouseEvents){
6489             btn.on("mouseover", this.onMouseOver, this);
6490             btn.on("mouseout", this.onMouseOut, this);
6491             btn.on("mousedown", this.onMouseDown, this);
6492         }
6493         btn.on(this.clickEvent, this.onClick, this);
6494         //btn.on("mouseup", this.onMouseUp, this);
6495         if(this.hidden){
6496             this.hide();
6497         }
6498         if(this.disabled){
6499             this.disable();
6500         }
6501         Roo.ButtonToggleMgr.register(this);
6502         if(this.pressed){
6503             this.el.addClass("x-btn-pressed");
6504         }
6505         if(this.repeat){
6506             var repeater = new Roo.util.ClickRepeater(btn,
6507                 typeof this.repeat == "object" ? this.repeat : {}
6508             );
6509             repeater.on("click", this.onClick,  this);
6510         }
6511         
6512         this.fireEvent('render', this);
6513         
6514     },
6515     /**
6516      * Returns the button's underlying element
6517      * @return {Roo.Element} The element
6518      */
6519     getEl : function(){
6520         return this.el;  
6521     },
6522     
6523     /**
6524      * Destroys this Button and removes any listeners.
6525      */
6526     destroy : function(){
6527         Roo.ButtonToggleMgr.unregister(this);
6528         this.el.removeAllListeners();
6529         this.purgeListeners();
6530         this.el.remove();
6531     },
6532
6533     // private
6534     autoWidth : function(){
6535         if(this.el){
6536             this.el.setWidth("auto");
6537             if(Roo.isIE7 && Roo.isStrict){
6538                 var ib = this.el.child('button');
6539                 if(ib && ib.getWidth() > 20){
6540                     ib.clip();
6541                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6542                 }
6543             }
6544             if(this.minWidth){
6545                 if(this.hidden){
6546                     this.el.beginMeasure();
6547                 }
6548                 if(this.el.getWidth() < this.minWidth){
6549                     this.el.setWidth(this.minWidth);
6550                 }
6551                 if(this.hidden){
6552                     this.el.endMeasure();
6553                 }
6554             }
6555         }
6556     },
6557
6558     /**
6559      * Assigns this button's click handler
6560      * @param {Function} handler The function to call when the button is clicked
6561      * @param {Object} scope (optional) Scope for the function passed in
6562      */
6563     setHandler : function(handler, scope){
6564         this.handler = handler;
6565         this.scope = scope;  
6566     },
6567     
6568     /**
6569      * Sets this button's text
6570      * @param {String} text The button text
6571      */
6572     setText : function(text){
6573         this.text = text;
6574         if(this.el){
6575             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6576         }
6577         this.autoWidth();
6578     },
6579     
6580     /**
6581      * Gets the text for this button
6582      * @return {String} The button text
6583      */
6584     getText : function(){
6585         return this.text;  
6586     },
6587     
6588     /**
6589      * Show this button
6590      */
6591     show: function(){
6592         this.hidden = false;
6593         if(this.el){
6594             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6595         }
6596     },
6597     
6598     /**
6599      * Hide this button
6600      */
6601     hide: function(){
6602         this.hidden = true;
6603         if(this.el){
6604             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6605         }
6606     },
6607     
6608     /**
6609      * Convenience function for boolean show/hide
6610      * @param {Boolean} visible True to show, false to hide
6611      */
6612     setVisible: function(visible){
6613         if(visible) {
6614             this.show();
6615         }else{
6616             this.hide();
6617         }
6618     },
6619     
6620     /**
6621      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6622      * @param {Boolean} state (optional) Force a particular state
6623      */
6624     toggle : function(state){
6625         state = state === undefined ? !this.pressed : state;
6626         if(state != this.pressed){
6627             if(state){
6628                 this.el.addClass("x-btn-pressed");
6629                 this.pressed = true;
6630                 this.fireEvent("toggle", this, true);
6631             }else{
6632                 this.el.removeClass("x-btn-pressed");
6633                 this.pressed = false;
6634                 this.fireEvent("toggle", this, false);
6635             }
6636             if(this.toggleHandler){
6637                 this.toggleHandler.call(this.scope || this, this, state);
6638             }
6639         }
6640     },
6641     
6642     /**
6643      * Focus the button
6644      */
6645     focus : function(){
6646         this.el.child('button:first').focus();
6647     },
6648     
6649     /**
6650      * Disable this button
6651      */
6652     disable : function(){
6653         if(this.el){
6654             this.el.addClass("x-btn-disabled");
6655         }
6656         this.disabled = true;
6657     },
6658     
6659     /**
6660      * Enable this button
6661      */
6662     enable : function(){
6663         if(this.el){
6664             this.el.removeClass("x-btn-disabled");
6665         }
6666         this.disabled = false;
6667     },
6668
6669     /**
6670      * Convenience function for boolean enable/disable
6671      * @param {Boolean} enabled True to enable, false to disable
6672      */
6673     setDisabled : function(v){
6674         this[v !== true ? "enable" : "disable"]();
6675     },
6676
6677     // private
6678     onClick : function(e)
6679     {
6680         if(e){
6681             e.preventDefault();
6682         }
6683         if(e.button != 0){
6684             return;
6685         }
6686         if(!this.disabled){
6687             if(this.enableToggle){
6688                 this.toggle();
6689             }
6690             if(this.menu && !this.menu.isVisible()){
6691                 this.menu.show(this.el, this.menuAlign);
6692             }
6693             this.fireEvent("click", this, e);
6694             if(this.handler){
6695                 this.el.removeClass("x-btn-over");
6696                 this.handler.call(this.scope || this, this, e);
6697             }
6698         }
6699     },
6700     // private
6701     onMouseOver : function(e){
6702         if(!this.disabled){
6703             this.el.addClass("x-btn-over");
6704             this.fireEvent('mouseover', this, e);
6705         }
6706     },
6707     // private
6708     onMouseOut : function(e){
6709         if(!e.within(this.el,  true)){
6710             this.el.removeClass("x-btn-over");
6711             this.fireEvent('mouseout', this, e);
6712         }
6713     },
6714     // private
6715     onFocus : function(e){
6716         if(!this.disabled){
6717             this.el.addClass("x-btn-focus");
6718         }
6719     },
6720     // private
6721     onBlur : function(e){
6722         this.el.removeClass("x-btn-focus");
6723     },
6724     // private
6725     onMouseDown : function(e){
6726         if(!this.disabled && e.button == 0){
6727             this.el.addClass("x-btn-click");
6728             Roo.get(document).on('mouseup', this.onMouseUp, this);
6729         }
6730     },
6731     // private
6732     onMouseUp : function(e){
6733         if(e.button == 0){
6734             this.el.removeClass("x-btn-click");
6735             Roo.get(document).un('mouseup', this.onMouseUp, this);
6736         }
6737     },
6738     // private
6739     onMenuShow : function(e){
6740         this.el.addClass("x-btn-menu-active");
6741     },
6742     // private
6743     onMenuHide : function(e){
6744         this.el.removeClass("x-btn-menu-active");
6745     }   
6746 });
6747
6748 // Private utility class used by Button
6749 Roo.ButtonToggleMgr = function(){
6750    var groups = {};
6751    
6752    function toggleGroup(btn, state){
6753        if(state){
6754            var g = groups[btn.toggleGroup];
6755            for(var i = 0, l = g.length; i < l; i++){
6756                if(g[i] != btn){
6757                    g[i].toggle(false);
6758                }
6759            }
6760        }
6761    }
6762    
6763    return {
6764        register : function(btn){
6765            if(!btn.toggleGroup){
6766                return;
6767            }
6768            var g = groups[btn.toggleGroup];
6769            if(!g){
6770                g = groups[btn.toggleGroup] = [];
6771            }
6772            g.push(btn);
6773            btn.on("toggle", toggleGroup);
6774        },
6775        
6776        unregister : function(btn){
6777            if(!btn.toggleGroup){
6778                return;
6779            }
6780            var g = groups[btn.toggleGroup];
6781            if(g){
6782                g.remove(btn);
6783                btn.un("toggle", toggleGroup);
6784            }
6785        }
6786    };
6787 }();/*
6788  * Based on:
6789  * Ext JS Library 1.1.1
6790  * Copyright(c) 2006-2007, Ext JS, LLC.
6791  *
6792  * Originally Released Under LGPL - original licence link has changed is not relivant.
6793  *
6794  * Fork - LGPL
6795  * <script type="text/javascript">
6796  */
6797  
6798 /**
6799  * @class Roo.SplitButton
6800  * @extends Roo.Button
6801  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6802  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6803  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6804  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6805  * @cfg {String} arrowTooltip The title attribute of the arrow
6806  * @constructor
6807  * Create a new menu button
6808  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6809  * @param {Object} config The config object
6810  */
6811 Roo.SplitButton = function(renderTo, config){
6812     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6813     /**
6814      * @event arrowclick
6815      * Fires when this button's arrow is clicked
6816      * @param {SplitButton} this
6817      * @param {EventObject} e The click event
6818      */
6819     this.addEvents({"arrowclick":true});
6820 };
6821
6822 Roo.extend(Roo.SplitButton, Roo.Button, {
6823     render : function(renderTo){
6824         // this is one sweet looking template!
6825         var tpl = new Roo.Template(
6826             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6827             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6828             '<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>',
6829             "</tbody></table></td><td>",
6830             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6831             '<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>',
6832             "</tbody></table></td></tr></table>"
6833         );
6834         var btn = tpl.append(renderTo, [this.text, this.type], true);
6835         var btnEl = btn.child("button");
6836         if(this.cls){
6837             btn.addClass(this.cls);
6838         }
6839         if(this.icon){
6840             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6841         }
6842         if(this.iconCls){
6843             btnEl.addClass(this.iconCls);
6844             if(!this.cls){
6845                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6846             }
6847         }
6848         this.el = btn;
6849         if(this.handleMouseEvents){
6850             btn.on("mouseover", this.onMouseOver, this);
6851             btn.on("mouseout", this.onMouseOut, this);
6852             btn.on("mousedown", this.onMouseDown, this);
6853             btn.on("mouseup", this.onMouseUp, this);
6854         }
6855         btn.on(this.clickEvent, this.onClick, this);
6856         if(this.tooltip){
6857             if(typeof this.tooltip == 'object'){
6858                 Roo.QuickTips.tips(Roo.apply({
6859                       target: btnEl.id
6860                 }, this.tooltip));
6861             } else {
6862                 btnEl.dom[this.tooltipType] = this.tooltip;
6863             }
6864         }
6865         if(this.arrowTooltip){
6866             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6867         }
6868         if(this.hidden){
6869             this.hide();
6870         }
6871         if(this.disabled){
6872             this.disable();
6873         }
6874         if(this.pressed){
6875             this.el.addClass("x-btn-pressed");
6876         }
6877         if(Roo.isIE && !Roo.isIE7){
6878             this.autoWidth.defer(1, this);
6879         }else{
6880             this.autoWidth();
6881         }
6882         if(this.menu){
6883             this.menu.on("show", this.onMenuShow, this);
6884             this.menu.on("hide", this.onMenuHide, this);
6885         }
6886         this.fireEvent('render', this);
6887     },
6888
6889     // private
6890     autoWidth : function(){
6891         if(this.el){
6892             var tbl = this.el.child("table:first");
6893             var tbl2 = this.el.child("table:last");
6894             this.el.setWidth("auto");
6895             tbl.setWidth("auto");
6896             if(Roo.isIE7 && Roo.isStrict){
6897                 var ib = this.el.child('button:first');
6898                 if(ib && ib.getWidth() > 20){
6899                     ib.clip();
6900                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6901                 }
6902             }
6903             if(this.minWidth){
6904                 if(this.hidden){
6905                     this.el.beginMeasure();
6906                 }
6907                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6908                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6909                 }
6910                 if(this.hidden){
6911                     this.el.endMeasure();
6912                 }
6913             }
6914             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6915         } 
6916     },
6917     /**
6918      * Sets this button's click handler
6919      * @param {Function} handler The function to call when the button is clicked
6920      * @param {Object} scope (optional) Scope for the function passed above
6921      */
6922     setHandler : function(handler, scope){
6923         this.handler = handler;
6924         this.scope = scope;  
6925     },
6926     
6927     /**
6928      * Sets this button's arrow click handler
6929      * @param {Function} handler The function to call when the arrow is clicked
6930      * @param {Object} scope (optional) Scope for the function passed above
6931      */
6932     setArrowHandler : function(handler, scope){
6933         this.arrowHandler = handler;
6934         this.scope = scope;  
6935     },
6936     
6937     /**
6938      * Focus the button
6939      */
6940     focus : function(){
6941         if(this.el){
6942             this.el.child("button:first").focus();
6943         }
6944     },
6945
6946     // private
6947     onClick : function(e){
6948         e.preventDefault();
6949         if(!this.disabled){
6950             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6951                 if(this.menu && !this.menu.isVisible()){
6952                     this.menu.show(this.el, this.menuAlign);
6953                 }
6954                 this.fireEvent("arrowclick", this, e);
6955                 if(this.arrowHandler){
6956                     this.arrowHandler.call(this.scope || this, this, e);
6957                 }
6958             }else{
6959                 this.fireEvent("click", this, e);
6960                 if(this.handler){
6961                     this.handler.call(this.scope || this, this, e);
6962                 }
6963             }
6964         }
6965     },
6966     // private
6967     onMouseDown : function(e){
6968         if(!this.disabled){
6969             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6970         }
6971     },
6972     // private
6973     onMouseUp : function(e){
6974         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
6975     }   
6976 });
6977
6978
6979 // backwards compat
6980 Roo.MenuButton = Roo.SplitButton;/*
6981  * Based on:
6982  * Ext JS Library 1.1.1
6983  * Copyright(c) 2006-2007, Ext JS, LLC.
6984  *
6985  * Originally Released Under LGPL - original licence link has changed is not relivant.
6986  *
6987  * Fork - LGPL
6988  * <script type="text/javascript">
6989  */
6990
6991 /**
6992  * @class Roo.Toolbar
6993  * Basic Toolbar class.
6994  * @constructor
6995  * Creates a new Toolbar
6996  * @param {Object} container The config object
6997  */ 
6998 Roo.Toolbar = function(container, buttons, config)
6999 {
7000     /// old consturctor format still supported..
7001     if(container instanceof Array){ // omit the container for later rendering
7002         buttons = container;
7003         config = buttons;
7004         container = null;
7005     }
7006     if (typeof(container) == 'object' && container.xtype) {
7007         config = container;
7008         container = config.container;
7009         buttons = config.buttons || []; // not really - use items!!
7010     }
7011     var xitems = [];
7012     if (config && config.items) {
7013         xitems = config.items;
7014         delete config.items;
7015     }
7016     Roo.apply(this, config);
7017     this.buttons = buttons;
7018     
7019     if(container){
7020         this.render(container);
7021     }
7022     this.xitems = xitems;
7023     Roo.each(xitems, function(b) {
7024         this.add(b);
7025     }, this);
7026     
7027 };
7028
7029 Roo.Toolbar.prototype = {
7030     /**
7031      * @cfg {Array} items
7032      * array of button configs or elements to add (will be converted to a MixedCollection)
7033      */
7034     
7035     /**
7036      * @cfg {String/HTMLElement/Element} container
7037      * The id or element that will contain the toolbar
7038      */
7039     // private
7040     render : function(ct){
7041         this.el = Roo.get(ct);
7042         if(this.cls){
7043             this.el.addClass(this.cls);
7044         }
7045         // using a table allows for vertical alignment
7046         // 100% width is needed by Safari...
7047         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7048         this.tr = this.el.child("tr", true);
7049         var autoId = 0;
7050         this.items = new Roo.util.MixedCollection(false, function(o){
7051             return o.id || ("item" + (++autoId));
7052         });
7053         if(this.buttons){
7054             this.add.apply(this, this.buttons);
7055             delete this.buttons;
7056         }
7057     },
7058
7059     /**
7060      * Adds element(s) to the toolbar -- this function takes a variable number of 
7061      * arguments of mixed type and adds them to the toolbar.
7062      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7063      * <ul>
7064      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7065      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7066      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7067      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7068      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7069      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7070      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7071      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7072      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7073      * </ul>
7074      * @param {Mixed} arg2
7075      * @param {Mixed} etc.
7076      */
7077     add : function(){
7078         var a = arguments, l = a.length;
7079         for(var i = 0; i < l; i++){
7080             this._add(a[i]);
7081         }
7082     },
7083     // private..
7084     _add : function(el) {
7085         
7086         if (el.xtype) {
7087             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7088         }
7089         
7090         if (el.applyTo){ // some kind of form field
7091             return this.addField(el);
7092         } 
7093         if (el.render){ // some kind of Toolbar.Item
7094             return this.addItem(el);
7095         }
7096         if (typeof el == "string"){ // string
7097             if(el == "separator" || el == "-"){
7098                 return this.addSeparator();
7099             }
7100             if (el == " "){
7101                 return this.addSpacer();
7102             }
7103             if(el == "->"){
7104                 return this.addFill();
7105             }
7106             return this.addText(el);
7107             
7108         }
7109         if(el.tagName){ // element
7110             return this.addElement(el);
7111         }
7112         if(typeof el == "object"){ // must be button config?
7113             return this.addButton(el);
7114         }
7115         // and now what?!?!
7116         return false;
7117         
7118     },
7119     
7120     /**
7121      * Add an Xtype element
7122      * @param {Object} xtype Xtype Object
7123      * @return {Object} created Object
7124      */
7125     addxtype : function(e){
7126         return this.add(e);  
7127     },
7128     
7129     /**
7130      * Returns the Element for this toolbar.
7131      * @return {Roo.Element}
7132      */
7133     getEl : function(){
7134         return this.el;  
7135     },
7136     
7137     /**
7138      * Adds a separator
7139      * @return {Roo.Toolbar.Item} The separator item
7140      */
7141     addSeparator : function(){
7142         return this.addItem(new Roo.Toolbar.Separator());
7143     },
7144
7145     /**
7146      * Adds a spacer element
7147      * @return {Roo.Toolbar.Spacer} The spacer item
7148      */
7149     addSpacer : function(){
7150         return this.addItem(new Roo.Toolbar.Spacer());
7151     },
7152
7153     /**
7154      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7155      * @return {Roo.Toolbar.Fill} The fill item
7156      */
7157     addFill : function(){
7158         return this.addItem(new Roo.Toolbar.Fill());
7159     },
7160
7161     /**
7162      * Adds any standard HTML element to the toolbar
7163      * @param {String/HTMLElement/Element} el The element or id of the element to add
7164      * @return {Roo.Toolbar.Item} The element's item
7165      */
7166     addElement : function(el){
7167         return this.addItem(new Roo.Toolbar.Item(el));
7168     },
7169     /**
7170      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7171      * @type Roo.util.MixedCollection  
7172      */
7173     items : false,
7174      
7175     /**
7176      * Adds any Toolbar.Item or subclass
7177      * @param {Roo.Toolbar.Item} item
7178      * @return {Roo.Toolbar.Item} The item
7179      */
7180     addItem : function(item){
7181         var td = this.nextBlock();
7182         item.render(td);
7183         this.items.add(item);
7184         return item;
7185     },
7186     
7187     /**
7188      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7189      * @param {Object/Array} config A button config or array of configs
7190      * @return {Roo.Toolbar.Button/Array}
7191      */
7192     addButton : function(config){
7193         if(config instanceof Array){
7194             var buttons = [];
7195             for(var i = 0, len = config.length; i < len; i++) {
7196                 buttons.push(this.addButton(config[i]));
7197             }
7198             return buttons;
7199         }
7200         var b = config;
7201         if(!(config instanceof Roo.Toolbar.Button)){
7202             b = config.split ?
7203                 new Roo.Toolbar.SplitButton(config) :
7204                 new Roo.Toolbar.Button(config);
7205         }
7206         var td = this.nextBlock();
7207         b.render(td);
7208         this.items.add(b);
7209         return b;
7210     },
7211     
7212     /**
7213      * Adds text to the toolbar
7214      * @param {String} text The text to add
7215      * @return {Roo.Toolbar.Item} The element's item
7216      */
7217     addText : function(text){
7218         return this.addItem(new Roo.Toolbar.TextItem(text));
7219     },
7220     
7221     /**
7222      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7223      * @param {Number} index The index where the item is to be inserted
7224      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7225      * @return {Roo.Toolbar.Button/Item}
7226      */
7227     insertButton : function(index, item){
7228         if(item instanceof Array){
7229             var buttons = [];
7230             for(var i = 0, len = item.length; i < len; i++) {
7231                buttons.push(this.insertButton(index + i, item[i]));
7232             }
7233             return buttons;
7234         }
7235         if (!(item instanceof Roo.Toolbar.Button)){
7236            item = new Roo.Toolbar.Button(item);
7237         }
7238         var td = document.createElement("td");
7239         this.tr.insertBefore(td, this.tr.childNodes[index]);
7240         item.render(td);
7241         this.items.insert(index, item);
7242         return item;
7243     },
7244     
7245     /**
7246      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7247      * @param {Object} config
7248      * @return {Roo.Toolbar.Item} The element's item
7249      */
7250     addDom : function(config, returnEl){
7251         var td = this.nextBlock();
7252         Roo.DomHelper.overwrite(td, config);
7253         var ti = new Roo.Toolbar.Item(td.firstChild);
7254         ti.render(td);
7255         this.items.add(ti);
7256         return ti;
7257     },
7258
7259     /**
7260      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7261      * @type Roo.util.MixedCollection  
7262      */
7263     fields : false,
7264     
7265     /**
7266      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7267      * Note: the field should not have been rendered yet. For a field that has already been
7268      * rendered, use {@link #addElement}.
7269      * @param {Roo.form.Field} field
7270      * @return {Roo.ToolbarItem}
7271      */
7272      
7273       
7274     addField : function(field) {
7275         if (!this.fields) {
7276             var autoId = 0;
7277             this.fields = new Roo.util.MixedCollection(false, function(o){
7278                 return o.id || ("item" + (++autoId));
7279             });
7280
7281         }
7282         
7283         var td = this.nextBlock();
7284         field.render(td);
7285         var ti = new Roo.Toolbar.Item(td.firstChild);
7286         ti.render(td);
7287         this.items.add(ti);
7288         this.fields.add(field);
7289         return ti;
7290     },
7291     /**
7292      * Hide the toolbar
7293      * @method hide
7294      */
7295      
7296       
7297     hide : function()
7298     {
7299         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7300         this.el.child('div').hide();
7301     },
7302     /**
7303      * Show the toolbar
7304      * @method show
7305      */
7306     show : function()
7307     {
7308         this.el.child('div').show();
7309     },
7310       
7311     // private
7312     nextBlock : function(){
7313         var td = document.createElement("td");
7314         this.tr.appendChild(td);
7315         return td;
7316     },
7317
7318     // private
7319     destroy : function(){
7320         if(this.items){ // rendered?
7321             Roo.destroy.apply(Roo, this.items.items);
7322         }
7323         if(this.fields){ // rendered?
7324             Roo.destroy.apply(Roo, this.fields.items);
7325         }
7326         Roo.Element.uncache(this.el, this.tr);
7327     }
7328 };
7329
7330 /**
7331  * @class Roo.Toolbar.Item
7332  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7333  * @constructor
7334  * Creates a new Item
7335  * @param {HTMLElement} el 
7336  */
7337 Roo.Toolbar.Item = function(el){
7338     var cfg = {};
7339     if (typeof (el.xtype) != 'undefined') {
7340         cfg = el;
7341         el = cfg.el;
7342     }
7343     
7344     this.el = Roo.getDom(el);
7345     this.id = Roo.id(this.el);
7346     this.hidden = false;
7347     
7348     this.addEvents({
7349          /**
7350              * @event render
7351              * Fires when the button is rendered
7352              * @param {Button} this
7353              */
7354         'render': true
7355     });
7356     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7357 };
7358 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7359 //Roo.Toolbar.Item.prototype = {
7360     
7361     /**
7362      * Get this item's HTML Element
7363      * @return {HTMLElement}
7364      */
7365     getEl : function(){
7366        return this.el;  
7367     },
7368
7369     // private
7370     render : function(td){
7371         
7372          this.td = td;
7373         td.appendChild(this.el);
7374         
7375         this.fireEvent('render', this);
7376     },
7377     
7378     /**
7379      * Removes and destroys this item.
7380      */
7381     destroy : function(){
7382         this.td.parentNode.removeChild(this.td);
7383     },
7384     
7385     /**
7386      * Shows this item.
7387      */
7388     show: function(){
7389         this.hidden = false;
7390         this.td.style.display = "";
7391     },
7392     
7393     /**
7394      * Hides this item.
7395      */
7396     hide: function(){
7397         this.hidden = true;
7398         this.td.style.display = "none";
7399     },
7400     
7401     /**
7402      * Convenience function for boolean show/hide.
7403      * @param {Boolean} visible true to show/false to hide
7404      */
7405     setVisible: function(visible){
7406         if(visible) {
7407             this.show();
7408         }else{
7409             this.hide();
7410         }
7411     },
7412     
7413     /**
7414      * Try to focus this item.
7415      */
7416     focus : function(){
7417         Roo.fly(this.el).focus();
7418     },
7419     
7420     /**
7421      * Disables this item.
7422      */
7423     disable : function(){
7424         Roo.fly(this.td).addClass("x-item-disabled");
7425         this.disabled = true;
7426         this.el.disabled = true;
7427     },
7428     
7429     /**
7430      * Enables this item.
7431      */
7432     enable : function(){
7433         Roo.fly(this.td).removeClass("x-item-disabled");
7434         this.disabled = false;
7435         this.el.disabled = false;
7436     }
7437 });
7438
7439
7440 /**
7441  * @class Roo.Toolbar.Separator
7442  * @extends Roo.Toolbar.Item
7443  * A simple toolbar separator class
7444  * @constructor
7445  * Creates a new Separator
7446  */
7447 Roo.Toolbar.Separator = function(cfg){
7448     
7449     var s = document.createElement("span");
7450     s.className = "ytb-sep";
7451     if (cfg) {
7452         cfg.el = s;
7453     }
7454     
7455     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7456 };
7457 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7458     enable:Roo.emptyFn,
7459     disable:Roo.emptyFn,
7460     focus:Roo.emptyFn
7461 });
7462
7463 /**
7464  * @class Roo.Toolbar.Spacer
7465  * @extends Roo.Toolbar.Item
7466  * A simple element that adds extra horizontal space to a toolbar.
7467  * @constructor
7468  * Creates a new Spacer
7469  */
7470 Roo.Toolbar.Spacer = function(cfg){
7471     var s = document.createElement("div");
7472     s.className = "ytb-spacer";
7473     if (cfg) {
7474         cfg.el = s;
7475     }
7476     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7477 };
7478 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7479     enable:Roo.emptyFn,
7480     disable:Roo.emptyFn,
7481     focus:Roo.emptyFn
7482 });
7483
7484 /**
7485  * @class Roo.Toolbar.Fill
7486  * @extends Roo.Toolbar.Spacer
7487  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7488  * @constructor
7489  * Creates a new Spacer
7490  */
7491 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7492     // private
7493     render : function(td){
7494         td.style.width = '100%';
7495         Roo.Toolbar.Fill.superclass.render.call(this, td);
7496     }
7497 });
7498
7499 /**
7500  * @class Roo.Toolbar.TextItem
7501  * @extends Roo.Toolbar.Item
7502  * A simple class that renders text directly into a toolbar.
7503  * @constructor
7504  * Creates a new TextItem
7505  * @cfg {string} text 
7506  */
7507 Roo.Toolbar.TextItem = function(cfg){
7508     var  text = cfg || "";
7509     if (typeof(cfg) == 'object') {
7510         text = cfg.text || "";
7511     }  else {
7512         cfg = null;
7513     }
7514     var s = document.createElement("span");
7515     s.className = "ytb-text";
7516     s.innerHTML = text;
7517     if (cfg) {
7518         cfg.el  = s;
7519     }
7520     
7521     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7522 };
7523 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7524     
7525      
7526     enable:Roo.emptyFn,
7527     disable:Roo.emptyFn,
7528     focus:Roo.emptyFn
7529 });
7530
7531 /**
7532  * @class Roo.Toolbar.Button
7533  * @extends Roo.Button
7534  * A button that renders into a toolbar.
7535  * @constructor
7536  * Creates a new Button
7537  * @param {Object} config A standard {@link Roo.Button} config object
7538  */
7539 Roo.Toolbar.Button = function(config){
7540     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7541 };
7542 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7543     render : function(td){
7544         this.td = td;
7545         Roo.Toolbar.Button.superclass.render.call(this, td);
7546     },
7547     
7548     /**
7549      * Removes and destroys this button
7550      */
7551     destroy : function(){
7552         Roo.Toolbar.Button.superclass.destroy.call(this);
7553         this.td.parentNode.removeChild(this.td);
7554     },
7555     
7556     /**
7557      * Shows this button
7558      */
7559     show: function(){
7560         this.hidden = false;
7561         this.td.style.display = "";
7562     },
7563     
7564     /**
7565      * Hides this button
7566      */
7567     hide: function(){
7568         this.hidden = true;
7569         this.td.style.display = "none";
7570     },
7571
7572     /**
7573      * Disables this item
7574      */
7575     disable : function(){
7576         Roo.fly(this.td).addClass("x-item-disabled");
7577         this.disabled = true;
7578     },
7579
7580     /**
7581      * Enables this item
7582      */
7583     enable : function(){
7584         Roo.fly(this.td).removeClass("x-item-disabled");
7585         this.disabled = false;
7586     }
7587 });
7588 // backwards compat
7589 Roo.ToolbarButton = Roo.Toolbar.Button;
7590
7591 /**
7592  * @class Roo.Toolbar.SplitButton
7593  * @extends Roo.SplitButton
7594  * A menu button that renders into a toolbar.
7595  * @constructor
7596  * Creates a new SplitButton
7597  * @param {Object} config A standard {@link Roo.SplitButton} config object
7598  */
7599 Roo.Toolbar.SplitButton = function(config){
7600     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7601 };
7602 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7603     render : function(td){
7604         this.td = td;
7605         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7606     },
7607     
7608     /**
7609      * Removes and destroys this button
7610      */
7611     destroy : function(){
7612         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7613         this.td.parentNode.removeChild(this.td);
7614     },
7615     
7616     /**
7617      * Shows this button
7618      */
7619     show: function(){
7620         this.hidden = false;
7621         this.td.style.display = "";
7622     },
7623     
7624     /**
7625      * Hides this button
7626      */
7627     hide: function(){
7628         this.hidden = true;
7629         this.td.style.display = "none";
7630     }
7631 });
7632
7633 // backwards compat
7634 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7635  * Based on:
7636  * Ext JS Library 1.1.1
7637  * Copyright(c) 2006-2007, Ext JS, LLC.
7638  *
7639  * Originally Released Under LGPL - original licence link has changed is not relivant.
7640  *
7641  * Fork - LGPL
7642  * <script type="text/javascript">
7643  */
7644  
7645 /**
7646  * @class Roo.PagingToolbar
7647  * @extends Roo.Toolbar
7648  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7649  * @constructor
7650  * Create a new PagingToolbar
7651  * @param {Object} config The config object
7652  */
7653 Roo.PagingToolbar = function(el, ds, config)
7654 {
7655     // old args format still supported... - xtype is prefered..
7656     if (typeof(el) == 'object' && el.xtype) {
7657         // created from xtype...
7658         config = el;
7659         ds = el.dataSource;
7660         el = config.container;
7661     }
7662     var items = [];
7663     if (config.items) {
7664         items = config.items;
7665         config.items = [];
7666     }
7667     
7668     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7669     this.ds = ds;
7670     this.cursor = 0;
7671     this.renderButtons(this.el);
7672     this.bind(ds);
7673     
7674     // supprot items array.
7675    
7676     Roo.each(items, function(e) {
7677         this.add(Roo.factory(e));
7678     },this);
7679     
7680 };
7681
7682 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7683     /**
7684      * @cfg {Roo.data.Store} dataSource
7685      * The underlying data store providing the paged data
7686      */
7687     /**
7688      * @cfg {String/HTMLElement/Element} container
7689      * container The id or element that will contain the toolbar
7690      */
7691     /**
7692      * @cfg {Boolean} displayInfo
7693      * True to display the displayMsg (defaults to false)
7694      */
7695     /**
7696      * @cfg {Number} pageSize
7697      * The number of records to display per page (defaults to 20)
7698      */
7699     pageSize: 20,
7700     /**
7701      * @cfg {String} displayMsg
7702      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7703      */
7704     displayMsg : 'Displaying {0} - {1} of {2}',
7705     /**
7706      * @cfg {String} emptyMsg
7707      * The message to display when no records are found (defaults to "No data to display")
7708      */
7709     emptyMsg : 'No data to display',
7710     /**
7711      * Customizable piece of the default paging text (defaults to "Page")
7712      * @type String
7713      */
7714     beforePageText : "Page",
7715     /**
7716      * Customizable piece of the default paging text (defaults to "of %0")
7717      * @type String
7718      */
7719     afterPageText : "of {0}",
7720     /**
7721      * Customizable piece of the default paging text (defaults to "First Page")
7722      * @type String
7723      */
7724     firstText : "First Page",
7725     /**
7726      * Customizable piece of the default paging text (defaults to "Previous Page")
7727      * @type String
7728      */
7729     prevText : "Previous Page",
7730     /**
7731      * Customizable piece of the default paging text (defaults to "Next Page")
7732      * @type String
7733      */
7734     nextText : "Next Page",
7735     /**
7736      * Customizable piece of the default paging text (defaults to "Last Page")
7737      * @type String
7738      */
7739     lastText : "Last Page",
7740     /**
7741      * Customizable piece of the default paging text (defaults to "Refresh")
7742      * @type String
7743      */
7744     refreshText : "Refresh",
7745
7746     // private
7747     renderButtons : function(el){
7748         Roo.PagingToolbar.superclass.render.call(this, el);
7749         this.first = this.addButton({
7750             tooltip: this.firstText,
7751             cls: "x-btn-icon x-grid-page-first",
7752             disabled: true,
7753             handler: this.onClick.createDelegate(this, ["first"])
7754         });
7755         this.prev = this.addButton({
7756             tooltip: this.prevText,
7757             cls: "x-btn-icon x-grid-page-prev",
7758             disabled: true,
7759             handler: this.onClick.createDelegate(this, ["prev"])
7760         });
7761         //this.addSeparator();
7762         this.add(this.beforePageText);
7763         this.field = Roo.get(this.addDom({
7764            tag: "input",
7765            type: "text",
7766            size: "3",
7767            value: "1",
7768            cls: "x-grid-page-number"
7769         }).el);
7770         this.field.on("keydown", this.onPagingKeydown, this);
7771         this.field.on("focus", function(){this.dom.select();});
7772         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7773         this.field.setHeight(18);
7774         //this.addSeparator();
7775         this.next = this.addButton({
7776             tooltip: this.nextText,
7777             cls: "x-btn-icon x-grid-page-next",
7778             disabled: true,
7779             handler: this.onClick.createDelegate(this, ["next"])
7780         });
7781         this.last = this.addButton({
7782             tooltip: this.lastText,
7783             cls: "x-btn-icon x-grid-page-last",
7784             disabled: true,
7785             handler: this.onClick.createDelegate(this, ["last"])
7786         });
7787         //this.addSeparator();
7788         this.loading = this.addButton({
7789             tooltip: this.refreshText,
7790             cls: "x-btn-icon x-grid-loading",
7791             handler: this.onClick.createDelegate(this, ["refresh"])
7792         });
7793
7794         if(this.displayInfo){
7795             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7796         }
7797     },
7798
7799     // private
7800     updateInfo : function(){
7801         if(this.displayEl){
7802             var count = this.ds.getCount();
7803             var msg = count == 0 ?
7804                 this.emptyMsg :
7805                 String.format(
7806                     this.displayMsg,
7807                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7808                 );
7809             this.displayEl.update(msg);
7810         }
7811     },
7812
7813     // private
7814     onLoad : function(ds, r, o){
7815        this.cursor = o.params ? o.params.start : 0;
7816        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7817
7818        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7819        this.field.dom.value = ap;
7820        this.first.setDisabled(ap == 1);
7821        this.prev.setDisabled(ap == 1);
7822        this.next.setDisabled(ap == ps);
7823        this.last.setDisabled(ap == ps);
7824        this.loading.enable();
7825        this.updateInfo();
7826     },
7827
7828     // private
7829     getPageData : function(){
7830         var total = this.ds.getTotalCount();
7831         return {
7832             total : total,
7833             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7834             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7835         };
7836     },
7837
7838     // private
7839     onLoadError : function(){
7840         this.loading.enable();
7841     },
7842
7843     // private
7844     onPagingKeydown : function(e){
7845         var k = e.getKey();
7846         var d = this.getPageData();
7847         if(k == e.RETURN){
7848             var v = this.field.dom.value, pageNum;
7849             if(!v || isNaN(pageNum = parseInt(v, 10))){
7850                 this.field.dom.value = d.activePage;
7851                 return;
7852             }
7853             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7854             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7855             e.stopEvent();
7856         }
7857         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))
7858         {
7859           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7860           this.field.dom.value = pageNum;
7861           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7862           e.stopEvent();
7863         }
7864         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7865         {
7866           var v = this.field.dom.value, pageNum; 
7867           var increment = (e.shiftKey) ? 10 : 1;
7868           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7869             increment *= -1;
7870           }
7871           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7872             this.field.dom.value = d.activePage;
7873             return;
7874           }
7875           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7876           {
7877             this.field.dom.value = parseInt(v, 10) + increment;
7878             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7879             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7880           }
7881           e.stopEvent();
7882         }
7883     },
7884
7885     // private
7886     beforeLoad : function(){
7887         if(this.loading){
7888             this.loading.disable();
7889         }
7890     },
7891
7892     // private
7893     onClick : function(which){
7894         var ds = this.ds;
7895         switch(which){
7896             case "first":
7897                 ds.load({params:{start: 0, limit: this.pageSize}});
7898             break;
7899             case "prev":
7900                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7901             break;
7902             case "next":
7903                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7904             break;
7905             case "last":
7906                 var total = ds.getTotalCount();
7907                 var extra = total % this.pageSize;
7908                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7909                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7910             break;
7911             case "refresh":
7912                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7913             break;
7914         }
7915     },
7916
7917     /**
7918      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7919      * @param {Roo.data.Store} store The data store to unbind
7920      */
7921     unbind : function(ds){
7922         ds.un("beforeload", this.beforeLoad, this);
7923         ds.un("load", this.onLoad, this);
7924         ds.un("loadexception", this.onLoadError, this);
7925         ds.un("remove", this.updateInfo, this);
7926         ds.un("add", this.updateInfo, this);
7927         this.ds = undefined;
7928     },
7929
7930     /**
7931      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7932      * @param {Roo.data.Store} store The data store to bind
7933      */
7934     bind : function(ds){
7935         ds.on("beforeload", this.beforeLoad, this);
7936         ds.on("load", this.onLoad, this);
7937         ds.on("loadexception", this.onLoadError, this);
7938         ds.on("remove", this.updateInfo, this);
7939         ds.on("add", this.updateInfo, this);
7940         this.ds = ds;
7941     }
7942 });/*
7943  * Based on:
7944  * Ext JS Library 1.1.1
7945  * Copyright(c) 2006-2007, Ext JS, LLC.
7946  *
7947  * Originally Released Under LGPL - original licence link has changed is not relivant.
7948  *
7949  * Fork - LGPL
7950  * <script type="text/javascript">
7951  */
7952
7953 /**
7954  * @class Roo.Resizable
7955  * @extends Roo.util.Observable
7956  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
7957  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
7958  * 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
7959  * the element will be wrapped for you automatically.</p>
7960  * <p>Here is the list of valid resize handles:</p>
7961  * <pre>
7962 Value   Description
7963 ------  -------------------
7964  'n'     north
7965  's'     south
7966  'e'     east
7967  'w'     west
7968  'nw'    northwest
7969  'sw'    southwest
7970  'se'    southeast
7971  'ne'    northeast
7972  'hd'    horizontal drag
7973  'all'   all
7974 </pre>
7975  * <p>Here's an example showing the creation of a typical Resizable:</p>
7976  * <pre><code>
7977 var resizer = new Roo.Resizable("element-id", {
7978     handles: 'all',
7979     minWidth: 200,
7980     minHeight: 100,
7981     maxWidth: 500,
7982     maxHeight: 400,
7983     pinned: true
7984 });
7985 resizer.on("resize", myHandler);
7986 </code></pre>
7987  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
7988  * resizer.east.setDisplayed(false);</p>
7989  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
7990  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
7991  * resize operation's new size (defaults to [0, 0])
7992  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
7993  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
7994  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
7995  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
7996  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
7997  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
7998  * @cfg {Number} width The width of the element in pixels (defaults to null)
7999  * @cfg {Number} height The height of the element in pixels (defaults to null)
8000  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8001  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8002  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8003  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8004  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8005  * in favor of the handles config option (defaults to false)
8006  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8007  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8008  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8009  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8010  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8011  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8012  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8013  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8014  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8015  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8016  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8017  * @constructor
8018  * Create a new resizable component
8019  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8020  * @param {Object} config configuration options
8021   */
8022 Roo.Resizable = function(el, config)
8023 {
8024     this.el = Roo.get(el);
8025
8026     if(config && config.wrap){
8027         config.resizeChild = this.el;
8028         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8029         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8030         this.el.setStyle("overflow", "hidden");
8031         this.el.setPositioning(config.resizeChild.getPositioning());
8032         config.resizeChild.clearPositioning();
8033         if(!config.width || !config.height){
8034             var csize = config.resizeChild.getSize();
8035             this.el.setSize(csize.width, csize.height);
8036         }
8037         if(config.pinned && !config.adjustments){
8038             config.adjustments = "auto";
8039         }
8040     }
8041
8042     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8043     this.proxy.unselectable();
8044     this.proxy.enableDisplayMode('block');
8045
8046     Roo.apply(this, config);
8047
8048     if(this.pinned){
8049         this.disableTrackOver = true;
8050         this.el.addClass("x-resizable-pinned");
8051     }
8052     // if the element isn't positioned, make it relative
8053     var position = this.el.getStyle("position");
8054     if(position != "absolute" && position != "fixed"){
8055         this.el.setStyle("position", "relative");
8056     }
8057     if(!this.handles){ // no handles passed, must be legacy style
8058         this.handles = 's,e,se';
8059         if(this.multiDirectional){
8060             this.handles += ',n,w';
8061         }
8062     }
8063     if(this.handles == "all"){
8064         this.handles = "n s e w ne nw se sw";
8065     }
8066     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8067     var ps = Roo.Resizable.positions;
8068     for(var i = 0, len = hs.length; i < len; i++){
8069         if(hs[i] && ps[hs[i]]){
8070             var pos = ps[hs[i]];
8071             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8072         }
8073     }
8074     // legacy
8075     this.corner = this.southeast;
8076     
8077     // updateBox = the box can move..
8078     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8079         this.updateBox = true;
8080     }
8081
8082     this.activeHandle = null;
8083
8084     if(this.resizeChild){
8085         if(typeof this.resizeChild == "boolean"){
8086             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8087         }else{
8088             this.resizeChild = Roo.get(this.resizeChild, true);
8089         }
8090     }
8091     
8092     if(this.adjustments == "auto"){
8093         var rc = this.resizeChild;
8094         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8095         if(rc && (hw || hn)){
8096             rc.position("relative");
8097             rc.setLeft(hw ? hw.el.getWidth() : 0);
8098             rc.setTop(hn ? hn.el.getHeight() : 0);
8099         }
8100         this.adjustments = [
8101             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8102             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8103         ];
8104     }
8105
8106     if(this.draggable){
8107         this.dd = this.dynamic ?
8108             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8109         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8110     }
8111
8112     // public events
8113     this.addEvents({
8114         /**
8115          * @event beforeresize
8116          * Fired before resize is allowed. Set enabled to false to cancel resize.
8117          * @param {Roo.Resizable} this
8118          * @param {Roo.EventObject} e The mousedown event
8119          */
8120         "beforeresize" : true,
8121         /**
8122          * @event resizing
8123          * Fired a resizing.
8124          * @param {Roo.Resizable} this
8125          * @param {Number} x The new x position
8126          * @param {Number} y The new y position
8127          * @param {Number} w The new w width
8128          * @param {Number} h The new h hight
8129          * @param {Roo.EventObject} e The mouseup event
8130          */
8131         "resizing" : true,
8132         /**
8133          * @event resize
8134          * Fired after a resize.
8135          * @param {Roo.Resizable} this
8136          * @param {Number} width The new width
8137          * @param {Number} height The new height
8138          * @param {Roo.EventObject} e The mouseup event
8139          */
8140         "resize" : true
8141     });
8142
8143     if(this.width !== null && this.height !== null){
8144         this.resizeTo(this.width, this.height);
8145     }else{
8146         this.updateChildSize();
8147     }
8148     if(Roo.isIE){
8149         this.el.dom.style.zoom = 1;
8150     }
8151     Roo.Resizable.superclass.constructor.call(this);
8152 };
8153
8154 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8155         resizeChild : false,
8156         adjustments : [0, 0],
8157         minWidth : 5,
8158         minHeight : 5,
8159         maxWidth : 10000,
8160         maxHeight : 10000,
8161         enabled : true,
8162         animate : false,
8163         duration : .35,
8164         dynamic : false,
8165         handles : false,
8166         multiDirectional : false,
8167         disableTrackOver : false,
8168         easing : 'easeOutStrong',
8169         widthIncrement : 0,
8170         heightIncrement : 0,
8171         pinned : false,
8172         width : null,
8173         height : null,
8174         preserveRatio : false,
8175         transparent: false,
8176         minX: 0,
8177         minY: 0,
8178         draggable: false,
8179
8180         /**
8181          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8182          */
8183         constrainTo: undefined,
8184         /**
8185          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8186          */
8187         resizeRegion: undefined,
8188
8189
8190     /**
8191      * Perform a manual resize
8192      * @param {Number} width
8193      * @param {Number} height
8194      */
8195     resizeTo : function(width, height){
8196         this.el.setSize(width, height);
8197         this.updateChildSize();
8198         this.fireEvent("resize", this, width, height, null);
8199     },
8200
8201     // private
8202     startSizing : function(e, handle){
8203         this.fireEvent("beforeresize", this, e);
8204         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8205
8206             if(!this.overlay){
8207                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8208                 this.overlay.unselectable();
8209                 this.overlay.enableDisplayMode("block");
8210                 this.overlay.on("mousemove", this.onMouseMove, this);
8211                 this.overlay.on("mouseup", this.onMouseUp, this);
8212             }
8213             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8214
8215             this.resizing = true;
8216             this.startBox = this.el.getBox();
8217             this.startPoint = e.getXY();
8218             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8219                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8220
8221             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8222             this.overlay.show();
8223
8224             if(this.constrainTo) {
8225                 var ct = Roo.get(this.constrainTo);
8226                 this.resizeRegion = ct.getRegion().adjust(
8227                     ct.getFrameWidth('t'),
8228                     ct.getFrameWidth('l'),
8229                     -ct.getFrameWidth('b'),
8230                     -ct.getFrameWidth('r')
8231                 );
8232             }
8233
8234             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8235             this.proxy.show();
8236             this.proxy.setBox(this.startBox);
8237             if(!this.dynamic){
8238                 this.proxy.setStyle('visibility', 'visible');
8239             }
8240         }
8241     },
8242
8243     // private
8244     onMouseDown : function(handle, e){
8245         if(this.enabled){
8246             e.stopEvent();
8247             this.activeHandle = handle;
8248             this.startSizing(e, handle);
8249         }
8250     },
8251
8252     // private
8253     onMouseUp : function(e){
8254         var size = this.resizeElement();
8255         this.resizing = false;
8256         this.handleOut();
8257         this.overlay.hide();
8258         this.proxy.hide();
8259         this.fireEvent("resize", this, size.width, size.height, e);
8260     },
8261
8262     // private
8263     updateChildSize : function(){
8264         
8265         if(this.resizeChild){
8266             var el = this.el;
8267             var child = this.resizeChild;
8268             var adj = this.adjustments;
8269             if(el.dom.offsetWidth){
8270                 var b = el.getSize(true);
8271                 child.setSize(b.width+adj[0], b.height+adj[1]);
8272             }
8273             // Second call here for IE
8274             // The first call enables instant resizing and
8275             // the second call corrects scroll bars if they
8276             // exist
8277             if(Roo.isIE){
8278                 setTimeout(function(){
8279                     if(el.dom.offsetWidth){
8280                         var b = el.getSize(true);
8281                         child.setSize(b.width+adj[0], b.height+adj[1]);
8282                     }
8283                 }, 10);
8284             }
8285         }
8286     },
8287
8288     // private
8289     snap : function(value, inc, min){
8290         if(!inc || !value) {
8291             return value;
8292         }
8293         var newValue = value;
8294         var m = value % inc;
8295         if(m > 0){
8296             if(m > (inc/2)){
8297                 newValue = value + (inc-m);
8298             }else{
8299                 newValue = value - m;
8300             }
8301         }
8302         return Math.max(min, newValue);
8303     },
8304
8305     // private
8306     resizeElement : function(){
8307         var box = this.proxy.getBox();
8308         if(this.updateBox){
8309             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8310         }else{
8311             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8312         }
8313         this.updateChildSize();
8314         if(!this.dynamic){
8315             this.proxy.hide();
8316         }
8317         return box;
8318     },
8319
8320     // private
8321     constrain : function(v, diff, m, mx){
8322         if(v - diff < m){
8323             diff = v - m;
8324         }else if(v - diff > mx){
8325             diff = mx - v;
8326         }
8327         return diff;
8328     },
8329
8330     // private
8331     onMouseMove : function(e){
8332         
8333         if(this.enabled){
8334             try{// try catch so if something goes wrong the user doesn't get hung
8335
8336             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8337                 return;
8338             }
8339
8340             //var curXY = this.startPoint;
8341             var curSize = this.curSize || this.startBox;
8342             var x = this.startBox.x, y = this.startBox.y;
8343             var ox = x, oy = y;
8344             var w = curSize.width, h = curSize.height;
8345             var ow = w, oh = h;
8346             var mw = this.minWidth, mh = this.minHeight;
8347             var mxw = this.maxWidth, mxh = this.maxHeight;
8348             var wi = this.widthIncrement;
8349             var hi = this.heightIncrement;
8350
8351             var eventXY = e.getXY();
8352             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8353             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8354
8355             var pos = this.activeHandle.position;
8356
8357             switch(pos){
8358                 case "east":
8359                     w += diffX;
8360                     w = Math.min(Math.max(mw, w), mxw);
8361                     break;
8362              
8363                 case "south":
8364                     h += diffY;
8365                     h = Math.min(Math.max(mh, h), mxh);
8366                     break;
8367                 case "southeast":
8368                     w += diffX;
8369                     h += diffY;
8370                     w = Math.min(Math.max(mw, w), mxw);
8371                     h = Math.min(Math.max(mh, h), mxh);
8372                     break;
8373                 case "north":
8374                     diffY = this.constrain(h, diffY, mh, mxh);
8375                     y += diffY;
8376                     h -= diffY;
8377                     break;
8378                 case "hdrag":
8379                     
8380                     if (wi) {
8381                         var adiffX = Math.abs(diffX);
8382                         var sub = (adiffX % wi); // how much 
8383                         if (sub > (wi/2)) { // far enough to snap
8384                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8385                         } else {
8386                             // remove difference.. 
8387                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8388                         }
8389                     }
8390                     x += diffX;
8391                     x = Math.max(this.minX, x);
8392                     break;
8393                 case "west":
8394                     diffX = this.constrain(w, diffX, mw, mxw);
8395                     x += diffX;
8396                     w -= diffX;
8397                     break;
8398                 case "northeast":
8399                     w += diffX;
8400                     w = Math.min(Math.max(mw, w), mxw);
8401                     diffY = this.constrain(h, diffY, mh, mxh);
8402                     y += diffY;
8403                     h -= diffY;
8404                     break;
8405                 case "northwest":
8406                     diffX = this.constrain(w, diffX, mw, mxw);
8407                     diffY = this.constrain(h, diffY, mh, mxh);
8408                     y += diffY;
8409                     h -= diffY;
8410                     x += diffX;
8411                     w -= diffX;
8412                     break;
8413                case "southwest":
8414                     diffX = this.constrain(w, diffX, mw, mxw);
8415                     h += diffY;
8416                     h = Math.min(Math.max(mh, h), mxh);
8417                     x += diffX;
8418                     w -= diffX;
8419                     break;
8420             }
8421
8422             var sw = this.snap(w, wi, mw);
8423             var sh = this.snap(h, hi, mh);
8424             if(sw != w || sh != h){
8425                 switch(pos){
8426                     case "northeast":
8427                         y -= sh - h;
8428                     break;
8429                     case "north":
8430                         y -= sh - h;
8431                         break;
8432                     case "southwest":
8433                         x -= sw - w;
8434                     break;
8435                     case "west":
8436                         x -= sw - w;
8437                         break;
8438                     case "northwest":
8439                         x -= sw - w;
8440                         y -= sh - h;
8441                     break;
8442                 }
8443                 w = sw;
8444                 h = sh;
8445             }
8446
8447             if(this.preserveRatio){
8448                 switch(pos){
8449                     case "southeast":
8450                     case "east":
8451                         h = oh * (w/ow);
8452                         h = Math.min(Math.max(mh, h), mxh);
8453                         w = ow * (h/oh);
8454                        break;
8455                     case "south":
8456                         w = ow * (h/oh);
8457                         w = Math.min(Math.max(mw, w), mxw);
8458                         h = oh * (w/ow);
8459                         break;
8460                     case "northeast":
8461                         w = ow * (h/oh);
8462                         w = Math.min(Math.max(mw, w), mxw);
8463                         h = oh * (w/ow);
8464                     break;
8465                     case "north":
8466                         var tw = w;
8467                         w = ow * (h/oh);
8468                         w = Math.min(Math.max(mw, w), mxw);
8469                         h = oh * (w/ow);
8470                         x += (tw - w) / 2;
8471                         break;
8472                     case "southwest":
8473                         h = oh * (w/ow);
8474                         h = Math.min(Math.max(mh, h), mxh);
8475                         var tw = w;
8476                         w = ow * (h/oh);
8477                         x += tw - w;
8478                         break;
8479                     case "west":
8480                         var th = h;
8481                         h = oh * (w/ow);
8482                         h = Math.min(Math.max(mh, h), mxh);
8483                         y += (th - h) / 2;
8484                         var tw = w;
8485                         w = ow * (h/oh);
8486                         x += tw - w;
8487                        break;
8488                     case "northwest":
8489                         var tw = w;
8490                         var th = h;
8491                         h = oh * (w/ow);
8492                         h = Math.min(Math.max(mh, h), mxh);
8493                         w = ow * (h/oh);
8494                         y += th - h;
8495                         x += tw - w;
8496                        break;
8497
8498                 }
8499             }
8500             if (pos == 'hdrag') {
8501                 w = ow;
8502             }
8503             this.proxy.setBounds(x, y, w, h);
8504             if(this.dynamic){
8505                 this.resizeElement();
8506             }
8507             }catch(e){}
8508         }
8509         this.fireEvent("resizing", this, x, y, w, h, e);
8510     },
8511
8512     // private
8513     handleOver : function(){
8514         if(this.enabled){
8515             this.el.addClass("x-resizable-over");
8516         }
8517     },
8518
8519     // private
8520     handleOut : function(){
8521         if(!this.resizing){
8522             this.el.removeClass("x-resizable-over");
8523         }
8524     },
8525
8526     /**
8527      * Returns the element this component is bound to.
8528      * @return {Roo.Element}
8529      */
8530     getEl : function(){
8531         return this.el;
8532     },
8533
8534     /**
8535      * Returns the resizeChild element (or null).
8536      * @return {Roo.Element}
8537      */
8538     getResizeChild : function(){
8539         return this.resizeChild;
8540     },
8541     groupHandler : function()
8542     {
8543         
8544     },
8545     /**
8546      * Destroys this resizable. If the element was wrapped and
8547      * removeEl is not true then the element remains.
8548      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8549      */
8550     destroy : function(removeEl){
8551         this.proxy.remove();
8552         if(this.overlay){
8553             this.overlay.removeAllListeners();
8554             this.overlay.remove();
8555         }
8556         var ps = Roo.Resizable.positions;
8557         for(var k in ps){
8558             if(typeof ps[k] != "function" && this[ps[k]]){
8559                 var h = this[ps[k]];
8560                 h.el.removeAllListeners();
8561                 h.el.remove();
8562             }
8563         }
8564         if(removeEl){
8565             this.el.update("");
8566             this.el.remove();
8567         }
8568     }
8569 });
8570
8571 // private
8572 // hash to map config positions to true positions
8573 Roo.Resizable.positions = {
8574     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8575     hd: "hdrag"
8576 };
8577
8578 // private
8579 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8580     if(!this.tpl){
8581         // only initialize the template if resizable is used
8582         var tpl = Roo.DomHelper.createTemplate(
8583             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8584         );
8585         tpl.compile();
8586         Roo.Resizable.Handle.prototype.tpl = tpl;
8587     }
8588     this.position = pos;
8589     this.rz = rz;
8590     // show north drag fro topdra
8591     var handlepos = pos == 'hdrag' ? 'north' : pos;
8592     
8593     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8594     if (pos == 'hdrag') {
8595         this.el.setStyle('cursor', 'pointer');
8596     }
8597     this.el.unselectable();
8598     if(transparent){
8599         this.el.setOpacity(0);
8600     }
8601     this.el.on("mousedown", this.onMouseDown, this);
8602     if(!disableTrackOver){
8603         this.el.on("mouseover", this.onMouseOver, this);
8604         this.el.on("mouseout", this.onMouseOut, this);
8605     }
8606 };
8607
8608 // private
8609 Roo.Resizable.Handle.prototype = {
8610     afterResize : function(rz){
8611         Roo.log('after?');
8612         // do nothing
8613     },
8614     // private
8615     onMouseDown : function(e){
8616         this.rz.onMouseDown(this, e);
8617     },
8618     // private
8619     onMouseOver : function(e){
8620         this.rz.handleOver(this, e);
8621     },
8622     // private
8623     onMouseOut : function(e){
8624         this.rz.handleOut(this, e);
8625     }
8626 };/*
8627  * Based on:
8628  * Ext JS Library 1.1.1
8629  * Copyright(c) 2006-2007, Ext JS, LLC.
8630  *
8631  * Originally Released Under LGPL - original licence link has changed is not relivant.
8632  *
8633  * Fork - LGPL
8634  * <script type="text/javascript">
8635  */
8636
8637 /**
8638  * @class Roo.Editor
8639  * @extends Roo.Component
8640  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8641  * @constructor
8642  * Create a new Editor
8643  * @param {Roo.form.Field} field The Field object (or descendant)
8644  * @param {Object} config The config object
8645  */
8646 Roo.Editor = function(field, config){
8647     Roo.Editor.superclass.constructor.call(this, config);
8648     this.field = field;
8649     this.addEvents({
8650         /**
8651              * @event beforestartedit
8652              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8653              * false from the handler of this event.
8654              * @param {Editor} this
8655              * @param {Roo.Element} boundEl The underlying element bound to this editor
8656              * @param {Mixed} value The field value being set
8657              */
8658         "beforestartedit" : true,
8659         /**
8660              * @event startedit
8661              * Fires when this editor is displayed
8662              * @param {Roo.Element} boundEl The underlying element bound to this editor
8663              * @param {Mixed} value The starting field value
8664              */
8665         "startedit" : true,
8666         /**
8667              * @event beforecomplete
8668              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8669              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8670              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8671              * event will not fire since no edit actually occurred.
8672              * @param {Editor} this
8673              * @param {Mixed} value The current field value
8674              * @param {Mixed} startValue The original field value
8675              */
8676         "beforecomplete" : true,
8677         /**
8678              * @event complete
8679              * Fires after editing is complete and any changed value has been written to the underlying field.
8680              * @param {Editor} this
8681              * @param {Mixed} value The current field value
8682              * @param {Mixed} startValue The original field value
8683              */
8684         "complete" : true,
8685         /**
8686          * @event specialkey
8687          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8688          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8689          * @param {Roo.form.Field} this
8690          * @param {Roo.EventObject} e The event object
8691          */
8692         "specialkey" : true
8693     });
8694 };
8695
8696 Roo.extend(Roo.Editor, Roo.Component, {
8697     /**
8698      * @cfg {Boolean/String} autosize
8699      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8700      * or "height" to adopt the height only (defaults to false)
8701      */
8702     /**
8703      * @cfg {Boolean} revertInvalid
8704      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8705      * validation fails (defaults to true)
8706      */
8707     /**
8708      * @cfg {Boolean} ignoreNoChange
8709      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8710      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8711      * will never be ignored.
8712      */
8713     /**
8714      * @cfg {Boolean} hideEl
8715      * False to keep the bound element visible while the editor is displayed (defaults to true)
8716      */
8717     /**
8718      * @cfg {Mixed} value
8719      * The data value of the underlying field (defaults to "")
8720      */
8721     value : "",
8722     /**
8723      * @cfg {String} alignment
8724      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8725      */
8726     alignment: "c-c?",
8727     /**
8728      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8729      * for bottom-right shadow (defaults to "frame")
8730      */
8731     shadow : "frame",
8732     /**
8733      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8734      */
8735     constrain : false,
8736     /**
8737      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8738      */
8739     completeOnEnter : false,
8740     /**
8741      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8742      */
8743     cancelOnEsc : false,
8744     /**
8745      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8746      */
8747     updateEl : false,
8748
8749     // private
8750     onRender : function(ct, position){
8751         this.el = new Roo.Layer({
8752             shadow: this.shadow,
8753             cls: "x-editor",
8754             parentEl : ct,
8755             shim : this.shim,
8756             shadowOffset:4,
8757             id: this.id,
8758             constrain: this.constrain
8759         });
8760         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8761         if(this.field.msgTarget != 'title'){
8762             this.field.msgTarget = 'qtip';
8763         }
8764         this.field.render(this.el);
8765         if(Roo.isGecko){
8766             this.field.el.dom.setAttribute('autocomplete', 'off');
8767         }
8768         this.field.on("specialkey", this.onSpecialKey, this);
8769         if(this.swallowKeys){
8770             this.field.el.swallowEvent(['keydown','keypress']);
8771         }
8772         this.field.show();
8773         this.field.on("blur", this.onBlur, this);
8774         if(this.field.grow){
8775             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8776         }
8777     },
8778
8779     onSpecialKey : function(field, e)
8780     {
8781         //Roo.log('editor onSpecialKey');
8782         if(this.completeOnEnter && e.getKey() == e.ENTER){
8783             e.stopEvent();
8784             this.completeEdit();
8785             return;
8786         }
8787         // do not fire special key otherwise it might hide close the editor...
8788         if(e.getKey() == e.ENTER){    
8789             return;
8790         }
8791         if(this.cancelOnEsc && e.getKey() == e.ESC){
8792             this.cancelEdit();
8793             return;
8794         } 
8795         this.fireEvent('specialkey', field, e);
8796     
8797     },
8798
8799     /**
8800      * Starts the editing process and shows the editor.
8801      * @param {String/HTMLElement/Element} el The element to edit
8802      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8803       * to the innerHTML of el.
8804      */
8805     startEdit : function(el, value){
8806         if(this.editing){
8807             this.completeEdit();
8808         }
8809         this.boundEl = Roo.get(el);
8810         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8811         if(!this.rendered){
8812             this.render(this.parentEl || document.body);
8813         }
8814         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8815             return;
8816         }
8817         this.startValue = v;
8818         this.field.setValue(v);
8819         if(this.autoSize){
8820             var sz = this.boundEl.getSize();
8821             switch(this.autoSize){
8822                 case "width":
8823                 this.setSize(sz.width,  "");
8824                 break;
8825                 case "height":
8826                 this.setSize("",  sz.height);
8827                 break;
8828                 default:
8829                 this.setSize(sz.width,  sz.height);
8830             }
8831         }
8832         this.el.alignTo(this.boundEl, this.alignment);
8833         this.editing = true;
8834         if(Roo.QuickTips){
8835             Roo.QuickTips.disable();
8836         }
8837         this.show();
8838     },
8839
8840     /**
8841      * Sets the height and width of this editor.
8842      * @param {Number} width The new width
8843      * @param {Number} height The new height
8844      */
8845     setSize : function(w, h){
8846         this.field.setSize(w, h);
8847         if(this.el){
8848             this.el.sync();
8849         }
8850     },
8851
8852     /**
8853      * Realigns the editor to the bound field based on the current alignment config value.
8854      */
8855     realign : function(){
8856         this.el.alignTo(this.boundEl, this.alignment);
8857     },
8858
8859     /**
8860      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8861      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8862      */
8863     completeEdit : function(remainVisible){
8864         if(!this.editing){
8865             return;
8866         }
8867         var v = this.getValue();
8868         if(this.revertInvalid !== false && !this.field.isValid()){
8869             v = this.startValue;
8870             this.cancelEdit(true);
8871         }
8872         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8873             this.editing = false;
8874             this.hide();
8875             return;
8876         }
8877         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8878             this.editing = false;
8879             if(this.updateEl && this.boundEl){
8880                 this.boundEl.update(v);
8881             }
8882             if(remainVisible !== true){
8883                 this.hide();
8884             }
8885             this.fireEvent("complete", this, v, this.startValue);
8886         }
8887     },
8888
8889     // private
8890     onShow : function(){
8891         this.el.show();
8892         if(this.hideEl !== false){
8893             this.boundEl.hide();
8894         }
8895         this.field.show();
8896         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8897             this.fixIEFocus = true;
8898             this.deferredFocus.defer(50, this);
8899         }else{
8900             this.field.focus();
8901         }
8902         this.fireEvent("startedit", this.boundEl, this.startValue);
8903     },
8904
8905     deferredFocus : function(){
8906         if(this.editing){
8907             this.field.focus();
8908         }
8909     },
8910
8911     /**
8912      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8913      * reverted to the original starting value.
8914      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8915      * cancel (defaults to false)
8916      */
8917     cancelEdit : function(remainVisible){
8918         if(this.editing){
8919             this.setValue(this.startValue);
8920             if(remainVisible !== true){
8921                 this.hide();
8922             }
8923         }
8924     },
8925
8926     // private
8927     onBlur : function(){
8928         if(this.allowBlur !== true && this.editing){
8929             this.completeEdit();
8930         }
8931     },
8932
8933     // private
8934     onHide : function(){
8935         if(this.editing){
8936             this.completeEdit();
8937             return;
8938         }
8939         this.field.blur();
8940         if(this.field.collapse){
8941             this.field.collapse();
8942         }
8943         this.el.hide();
8944         if(this.hideEl !== false){
8945             this.boundEl.show();
8946         }
8947         if(Roo.QuickTips){
8948             Roo.QuickTips.enable();
8949         }
8950     },
8951
8952     /**
8953      * Sets the data value of the editor
8954      * @param {Mixed} value Any valid value supported by the underlying field
8955      */
8956     setValue : function(v){
8957         this.field.setValue(v);
8958     },
8959
8960     /**
8961      * Gets the data value of the editor
8962      * @return {Mixed} The data value
8963      */
8964     getValue : function(){
8965         return this.field.getValue();
8966     }
8967 });/*
8968  * Based on:
8969  * Ext JS Library 1.1.1
8970  * Copyright(c) 2006-2007, Ext JS, LLC.
8971  *
8972  * Originally Released Under LGPL - original licence link has changed is not relivant.
8973  *
8974  * Fork - LGPL
8975  * <script type="text/javascript">
8976  */
8977  
8978 /**
8979  * @class Roo.BasicDialog
8980  * @extends Roo.util.Observable
8981  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
8982  * <pre><code>
8983 var dlg = new Roo.BasicDialog("my-dlg", {
8984     height: 200,
8985     width: 300,
8986     minHeight: 100,
8987     minWidth: 150,
8988     modal: true,
8989     proxyDrag: true,
8990     shadow: true
8991 });
8992 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
8993 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
8994 dlg.addButton('Cancel', dlg.hide, dlg);
8995 dlg.show();
8996 </code></pre>
8997   <b>A Dialog should always be a direct child of the body element.</b>
8998  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
8999  * @cfg {String} title Default text to display in the title bar (defaults to null)
9000  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9001  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9002  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9003  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9004  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9005  * (defaults to null with no animation)
9006  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9007  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9008  * property for valid values (defaults to 'all')
9009  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9010  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9011  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9012  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9013  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9014  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9015  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9016  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9017  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9018  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9019  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9020  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9021  * draggable = true (defaults to false)
9022  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9023  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9024  * shadow (defaults to false)
9025  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9026  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9027  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9028  * @cfg {Array} buttons Array of buttons
9029  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9030  * @constructor
9031  * Create a new BasicDialog.
9032  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9033  * @param {Object} config Configuration options
9034  */
9035 Roo.BasicDialog = function(el, config){
9036     this.el = Roo.get(el);
9037     var dh = Roo.DomHelper;
9038     if(!this.el && config && config.autoCreate){
9039         if(typeof config.autoCreate == "object"){
9040             if(!config.autoCreate.id){
9041                 config.autoCreate.id = el;
9042             }
9043             this.el = dh.append(document.body,
9044                         config.autoCreate, true);
9045         }else{
9046             this.el = dh.append(document.body,
9047                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9048         }
9049     }
9050     el = this.el;
9051     el.setDisplayed(true);
9052     el.hide = this.hideAction;
9053     this.id = el.id;
9054     el.addClass("x-dlg");
9055
9056     Roo.apply(this, config);
9057
9058     this.proxy = el.createProxy("x-dlg-proxy");
9059     this.proxy.hide = this.hideAction;
9060     this.proxy.setOpacity(.5);
9061     this.proxy.hide();
9062
9063     if(config.width){
9064         el.setWidth(config.width);
9065     }
9066     if(config.height){
9067         el.setHeight(config.height);
9068     }
9069     this.size = el.getSize();
9070     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9071         this.xy = [config.x,config.y];
9072     }else{
9073         this.xy = el.getCenterXY(true);
9074     }
9075     /** The header element @type Roo.Element */
9076     this.header = el.child("> .x-dlg-hd");
9077     /** The body element @type Roo.Element */
9078     this.body = el.child("> .x-dlg-bd");
9079     /** The footer element @type Roo.Element */
9080     this.footer = el.child("> .x-dlg-ft");
9081
9082     if(!this.header){
9083         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9084     }
9085     if(!this.body){
9086         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9087     }
9088
9089     this.header.unselectable();
9090     if(this.title){
9091         this.header.update(this.title);
9092     }
9093     // this element allows the dialog to be focused for keyboard event
9094     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9095     this.focusEl.swallowEvent("click", true);
9096
9097     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9098
9099     // wrap the body and footer for special rendering
9100     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9101     if(this.footer){
9102         this.bwrap.dom.appendChild(this.footer.dom);
9103     }
9104
9105     this.bg = this.el.createChild({
9106         tag: "div", cls:"x-dlg-bg",
9107         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9108     });
9109     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9110
9111
9112     if(this.autoScroll !== false && !this.autoTabs){
9113         this.body.setStyle("overflow", "auto");
9114     }
9115
9116     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9117
9118     if(this.closable !== false){
9119         this.el.addClass("x-dlg-closable");
9120         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9121         this.close.on("click", this.closeClick, this);
9122         this.close.addClassOnOver("x-dlg-close-over");
9123     }
9124     if(this.collapsible !== false){
9125         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9126         this.collapseBtn.on("click", this.collapseClick, this);
9127         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9128         this.header.on("dblclick", this.collapseClick, this);
9129     }
9130     if(this.resizable !== false){
9131         this.el.addClass("x-dlg-resizable");
9132         this.resizer = new Roo.Resizable(el, {
9133             minWidth: this.minWidth || 80,
9134             minHeight:this.minHeight || 80,
9135             handles: this.resizeHandles || "all",
9136             pinned: true
9137         });
9138         this.resizer.on("beforeresize", this.beforeResize, this);
9139         this.resizer.on("resize", this.onResize, this);
9140     }
9141     if(this.draggable !== false){
9142         el.addClass("x-dlg-draggable");
9143         if (!this.proxyDrag) {
9144             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9145         }
9146         else {
9147             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9148         }
9149         dd.setHandleElId(this.header.id);
9150         dd.endDrag = this.endMove.createDelegate(this);
9151         dd.startDrag = this.startMove.createDelegate(this);
9152         dd.onDrag = this.onDrag.createDelegate(this);
9153         dd.scroll = false;
9154         this.dd = dd;
9155     }
9156     if(this.modal){
9157         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9158         this.mask.enableDisplayMode("block");
9159         this.mask.hide();
9160         this.el.addClass("x-dlg-modal");
9161     }
9162     if(this.shadow){
9163         this.shadow = new Roo.Shadow({
9164             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9165             offset : this.shadowOffset
9166         });
9167     }else{
9168         this.shadowOffset = 0;
9169     }
9170     if(Roo.useShims && this.shim !== false){
9171         this.shim = this.el.createShim();
9172         this.shim.hide = this.hideAction;
9173         this.shim.hide();
9174     }else{
9175         this.shim = false;
9176     }
9177     if(this.autoTabs){
9178         this.initTabs();
9179     }
9180     if (this.buttons) { 
9181         var bts= this.buttons;
9182         this.buttons = [];
9183         Roo.each(bts, function(b) {
9184             this.addButton(b);
9185         }, this);
9186     }
9187     
9188     
9189     this.addEvents({
9190         /**
9191          * @event keydown
9192          * Fires when a key is pressed
9193          * @param {Roo.BasicDialog} this
9194          * @param {Roo.EventObject} e
9195          */
9196         "keydown" : true,
9197         /**
9198          * @event move
9199          * Fires when this dialog is moved by the user.
9200          * @param {Roo.BasicDialog} this
9201          * @param {Number} x The new page X
9202          * @param {Number} y The new page Y
9203          */
9204         "move" : true,
9205         /**
9206          * @event resize
9207          * Fires when this dialog is resized by the user.
9208          * @param {Roo.BasicDialog} this
9209          * @param {Number} width The new width
9210          * @param {Number} height The new height
9211          */
9212         "resize" : true,
9213         /**
9214          * @event beforehide
9215          * Fires before this dialog is hidden.
9216          * @param {Roo.BasicDialog} this
9217          */
9218         "beforehide" : true,
9219         /**
9220          * @event hide
9221          * Fires when this dialog is hidden.
9222          * @param {Roo.BasicDialog} this
9223          */
9224         "hide" : true,
9225         /**
9226          * @event beforeshow
9227          * Fires before this dialog is shown.
9228          * @param {Roo.BasicDialog} this
9229          */
9230         "beforeshow" : true,
9231         /**
9232          * @event show
9233          * Fires when this dialog is shown.
9234          * @param {Roo.BasicDialog} this
9235          */
9236         "show" : true
9237     });
9238     el.on("keydown", this.onKeyDown, this);
9239     el.on("mousedown", this.toFront, this);
9240     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9241     this.el.hide();
9242     Roo.DialogManager.register(this);
9243     Roo.BasicDialog.superclass.constructor.call(this);
9244 };
9245
9246 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9247     shadowOffset: Roo.isIE ? 6 : 5,
9248     minHeight: 80,
9249     minWidth: 200,
9250     minButtonWidth: 75,
9251     defaultButton: null,
9252     buttonAlign: "right",
9253     tabTag: 'div',
9254     firstShow: true,
9255
9256     /**
9257      * Sets the dialog title text
9258      * @param {String} text The title text to display
9259      * @return {Roo.BasicDialog} this
9260      */
9261     setTitle : function(text){
9262         this.header.update(text);
9263         return this;
9264     },
9265
9266     // private
9267     closeClick : function(){
9268         this.hide();
9269     },
9270
9271     // private
9272     collapseClick : function(){
9273         this[this.collapsed ? "expand" : "collapse"]();
9274     },
9275
9276     /**
9277      * Collapses the dialog to its minimized state (only the title bar is visible).
9278      * Equivalent to the user clicking the collapse dialog button.
9279      */
9280     collapse : function(){
9281         if(!this.collapsed){
9282             this.collapsed = true;
9283             this.el.addClass("x-dlg-collapsed");
9284             this.restoreHeight = this.el.getHeight();
9285             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9286         }
9287     },
9288
9289     /**
9290      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9291      * clicking the expand dialog button.
9292      */
9293     expand : function(){
9294         if(this.collapsed){
9295             this.collapsed = false;
9296             this.el.removeClass("x-dlg-collapsed");
9297             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9298         }
9299     },
9300
9301     /**
9302      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9303      * @return {Roo.TabPanel} The tabs component
9304      */
9305     initTabs : function(){
9306         var tabs = this.getTabs();
9307         while(tabs.getTab(0)){
9308             tabs.removeTab(0);
9309         }
9310         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9311             var dom = el.dom;
9312             tabs.addTab(Roo.id(dom), dom.title);
9313             dom.title = "";
9314         });
9315         tabs.activate(0);
9316         return tabs;
9317     },
9318
9319     // private
9320     beforeResize : function(){
9321         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9322     },
9323
9324     // private
9325     onResize : function(){
9326         this.refreshSize();
9327         this.syncBodyHeight();
9328         this.adjustAssets();
9329         this.focus();
9330         this.fireEvent("resize", this, this.size.width, this.size.height);
9331     },
9332
9333     // private
9334     onKeyDown : function(e){
9335         if(this.isVisible()){
9336             this.fireEvent("keydown", this, e);
9337         }
9338     },
9339
9340     /**
9341      * Resizes the dialog.
9342      * @param {Number} width
9343      * @param {Number} height
9344      * @return {Roo.BasicDialog} this
9345      */
9346     resizeTo : function(width, height){
9347         this.el.setSize(width, height);
9348         this.size = {width: width, height: height};
9349         this.syncBodyHeight();
9350         if(this.fixedcenter){
9351             this.center();
9352         }
9353         if(this.isVisible()){
9354             this.constrainXY();
9355             this.adjustAssets();
9356         }
9357         this.fireEvent("resize", this, width, height);
9358         return this;
9359     },
9360
9361
9362     /**
9363      * Resizes the dialog to fit the specified content size.
9364      * @param {Number} width
9365      * @param {Number} height
9366      * @return {Roo.BasicDialog} this
9367      */
9368     setContentSize : function(w, h){
9369         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9370         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9371         //if(!this.el.isBorderBox()){
9372             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9373             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9374         //}
9375         if(this.tabs){
9376             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9377             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9378         }
9379         this.resizeTo(w, h);
9380         return this;
9381     },
9382
9383     /**
9384      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9385      * executed in response to a particular key being pressed while the dialog is active.
9386      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9387      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9388      * @param {Function} fn The function to call
9389      * @param {Object} scope (optional) The scope of the function
9390      * @return {Roo.BasicDialog} this
9391      */
9392     addKeyListener : function(key, fn, scope){
9393         var keyCode, shift, ctrl, alt;
9394         if(typeof key == "object" && !(key instanceof Array)){
9395             keyCode = key["key"];
9396             shift = key["shift"];
9397             ctrl = key["ctrl"];
9398             alt = key["alt"];
9399         }else{
9400             keyCode = key;
9401         }
9402         var handler = function(dlg, e){
9403             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9404                 var k = e.getKey();
9405                 if(keyCode instanceof Array){
9406                     for(var i = 0, len = keyCode.length; i < len; i++){
9407                         if(keyCode[i] == k){
9408                           fn.call(scope || window, dlg, k, e);
9409                           return;
9410                         }
9411                     }
9412                 }else{
9413                     if(k == keyCode){
9414                         fn.call(scope || window, dlg, k, e);
9415                     }
9416                 }
9417             }
9418         };
9419         this.on("keydown", handler);
9420         return this;
9421     },
9422
9423     /**
9424      * Returns the TabPanel component (creates it if it doesn't exist).
9425      * Note: If you wish to simply check for the existence of tabs without creating them,
9426      * check for a null 'tabs' property.
9427      * @return {Roo.TabPanel} The tabs component
9428      */
9429     getTabs : function(){
9430         if(!this.tabs){
9431             this.el.addClass("x-dlg-auto-tabs");
9432             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9433             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9434         }
9435         return this.tabs;
9436     },
9437
9438     /**
9439      * Adds a button to the footer section of the dialog.
9440      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9441      * object or a valid Roo.DomHelper element config
9442      * @param {Function} handler The function called when the button is clicked
9443      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9444      * @return {Roo.Button} The new button
9445      */
9446     addButton : function(config, handler, scope){
9447         var dh = Roo.DomHelper;
9448         if(!this.footer){
9449             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9450         }
9451         if(!this.btnContainer){
9452             var tb = this.footer.createChild({
9453
9454                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9455                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9456             }, null, true);
9457             this.btnContainer = tb.firstChild.firstChild.firstChild;
9458         }
9459         var bconfig = {
9460             handler: handler,
9461             scope: scope,
9462             minWidth: this.minButtonWidth,
9463             hideParent:true
9464         };
9465         if(typeof config == "string"){
9466             bconfig.text = config;
9467         }else{
9468             if(config.tag){
9469                 bconfig.dhconfig = config;
9470             }else{
9471                 Roo.apply(bconfig, config);
9472             }
9473         }
9474         var fc = false;
9475         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9476             bconfig.position = Math.max(0, bconfig.position);
9477             fc = this.btnContainer.childNodes[bconfig.position];
9478         }
9479          
9480         var btn = new Roo.Button(
9481             fc ? 
9482                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9483                 : this.btnContainer.appendChild(document.createElement("td")),
9484             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9485             bconfig
9486         );
9487         this.syncBodyHeight();
9488         if(!this.buttons){
9489             /**
9490              * Array of all the buttons that have been added to this dialog via addButton
9491              * @type Array
9492              */
9493             this.buttons = [];
9494         }
9495         this.buttons.push(btn);
9496         return btn;
9497     },
9498
9499     /**
9500      * Sets the default button to be focused when the dialog is displayed.
9501      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9502      * @return {Roo.BasicDialog} this
9503      */
9504     setDefaultButton : function(btn){
9505         this.defaultButton = btn;
9506         return this;
9507     },
9508
9509     // private
9510     getHeaderFooterHeight : function(safe){
9511         var height = 0;
9512         if(this.header){
9513            height += this.header.getHeight();
9514         }
9515         if(this.footer){
9516            var fm = this.footer.getMargins();
9517             height += (this.footer.getHeight()+fm.top+fm.bottom);
9518         }
9519         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9520         height += this.centerBg.getPadding("tb");
9521         return height;
9522     },
9523
9524     // private
9525     syncBodyHeight : function()
9526     {
9527         var bd = this.body, // the text
9528             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9529             bw = this.bwrap;
9530         var height = this.size.height - this.getHeaderFooterHeight(false);
9531         bd.setHeight(height-bd.getMargins("tb"));
9532         var hh = this.header.getHeight();
9533         var h = this.size.height-hh;
9534         cb.setHeight(h);
9535         
9536         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9537         bw.setHeight(h-cb.getPadding("tb"));
9538         
9539         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9540         bd.setWidth(bw.getWidth(true));
9541         if(this.tabs){
9542             this.tabs.syncHeight();
9543             if(Roo.isIE){
9544                 this.tabs.el.repaint();
9545             }
9546         }
9547     },
9548
9549     /**
9550      * Restores the previous state of the dialog if Roo.state is configured.
9551      * @return {Roo.BasicDialog} this
9552      */
9553     restoreState : function(){
9554         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9555         if(box && box.width){
9556             this.xy = [box.x, box.y];
9557             this.resizeTo(box.width, box.height);
9558         }
9559         return this;
9560     },
9561
9562     // private
9563     beforeShow : function(){
9564         this.expand();
9565         if(this.fixedcenter){
9566             this.xy = this.el.getCenterXY(true);
9567         }
9568         if(this.modal){
9569             Roo.get(document.body).addClass("x-body-masked");
9570             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9571             this.mask.show();
9572         }
9573         this.constrainXY();
9574     },
9575
9576     // private
9577     animShow : function(){
9578         var b = Roo.get(this.animateTarget).getBox();
9579         this.proxy.setSize(b.width, b.height);
9580         this.proxy.setLocation(b.x, b.y);
9581         this.proxy.show();
9582         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9583                     true, .35, this.showEl.createDelegate(this));
9584     },
9585
9586     /**
9587      * Shows the dialog.
9588      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9589      * @return {Roo.BasicDialog} this
9590      */
9591     show : function(animateTarget){
9592         if (this.fireEvent("beforeshow", this) === false){
9593             return;
9594         }
9595         if(this.syncHeightBeforeShow){
9596             this.syncBodyHeight();
9597         }else if(this.firstShow){
9598             this.firstShow = false;
9599             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9600         }
9601         this.animateTarget = animateTarget || this.animateTarget;
9602         if(!this.el.isVisible()){
9603             this.beforeShow();
9604             if(this.animateTarget && Roo.get(this.animateTarget)){
9605                 this.animShow();
9606             }else{
9607                 this.showEl();
9608             }
9609         }
9610         return this;
9611     },
9612
9613     // private
9614     showEl : function(){
9615         this.proxy.hide();
9616         this.el.setXY(this.xy);
9617         this.el.show();
9618         this.adjustAssets(true);
9619         this.toFront();
9620         this.focus();
9621         // IE peekaboo bug - fix found by Dave Fenwick
9622         if(Roo.isIE){
9623             this.el.repaint();
9624         }
9625         this.fireEvent("show", this);
9626     },
9627
9628     /**
9629      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9630      * dialog itself will receive focus.
9631      */
9632     focus : function(){
9633         if(this.defaultButton){
9634             this.defaultButton.focus();
9635         }else{
9636             this.focusEl.focus();
9637         }
9638     },
9639
9640     // private
9641     constrainXY : function(){
9642         if(this.constraintoviewport !== false){
9643             if(!this.viewSize){
9644                 if(this.container){
9645                     var s = this.container.getSize();
9646                     this.viewSize = [s.width, s.height];
9647                 }else{
9648                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9649                 }
9650             }
9651             var s = Roo.get(this.container||document).getScroll();
9652
9653             var x = this.xy[0], y = this.xy[1];
9654             var w = this.size.width, h = this.size.height;
9655             var vw = this.viewSize[0], vh = this.viewSize[1];
9656             // only move it if it needs it
9657             var moved = false;
9658             // first validate right/bottom
9659             if(x + w > vw+s.left){
9660                 x = vw - w;
9661                 moved = true;
9662             }
9663             if(y + h > vh+s.top){
9664                 y = vh - h;
9665                 moved = true;
9666             }
9667             // then make sure top/left isn't negative
9668             if(x < s.left){
9669                 x = s.left;
9670                 moved = true;
9671             }
9672             if(y < s.top){
9673                 y = s.top;
9674                 moved = true;
9675             }
9676             if(moved){
9677                 // cache xy
9678                 this.xy = [x, y];
9679                 if(this.isVisible()){
9680                     this.el.setLocation(x, y);
9681                     this.adjustAssets();
9682                 }
9683             }
9684         }
9685     },
9686
9687     // private
9688     onDrag : function(){
9689         if(!this.proxyDrag){
9690             this.xy = this.el.getXY();
9691             this.adjustAssets();
9692         }
9693     },
9694
9695     // private
9696     adjustAssets : function(doShow){
9697         var x = this.xy[0], y = this.xy[1];
9698         var w = this.size.width, h = this.size.height;
9699         if(doShow === true){
9700             if(this.shadow){
9701                 this.shadow.show(this.el);
9702             }
9703             if(this.shim){
9704                 this.shim.show();
9705             }
9706         }
9707         if(this.shadow && this.shadow.isVisible()){
9708             this.shadow.show(this.el);
9709         }
9710         if(this.shim && this.shim.isVisible()){
9711             this.shim.setBounds(x, y, w, h);
9712         }
9713     },
9714
9715     // private
9716     adjustViewport : function(w, h){
9717         if(!w || !h){
9718             w = Roo.lib.Dom.getViewWidth();
9719             h = Roo.lib.Dom.getViewHeight();
9720         }
9721         // cache the size
9722         this.viewSize = [w, h];
9723         if(this.modal && this.mask.isVisible()){
9724             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9725             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9726         }
9727         if(this.isVisible()){
9728             this.constrainXY();
9729         }
9730     },
9731
9732     /**
9733      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9734      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9735      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9736      */
9737     destroy : function(removeEl){
9738         if(this.isVisible()){
9739             this.animateTarget = null;
9740             this.hide();
9741         }
9742         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9743         if(this.tabs){
9744             this.tabs.destroy(removeEl);
9745         }
9746         Roo.destroy(
9747              this.shim,
9748              this.proxy,
9749              this.resizer,
9750              this.close,
9751              this.mask
9752         );
9753         if(this.dd){
9754             this.dd.unreg();
9755         }
9756         if(this.buttons){
9757            for(var i = 0, len = this.buttons.length; i < len; i++){
9758                this.buttons[i].destroy();
9759            }
9760         }
9761         this.el.removeAllListeners();
9762         if(removeEl === true){
9763             this.el.update("");
9764             this.el.remove();
9765         }
9766         Roo.DialogManager.unregister(this);
9767     },
9768
9769     // private
9770     startMove : function(){
9771         if(this.proxyDrag){
9772             this.proxy.show();
9773         }
9774         if(this.constraintoviewport !== false){
9775             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9776         }
9777     },
9778
9779     // private
9780     endMove : function(){
9781         if(!this.proxyDrag){
9782             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9783         }else{
9784             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9785             this.proxy.hide();
9786         }
9787         this.refreshSize();
9788         this.adjustAssets();
9789         this.focus();
9790         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9791     },
9792
9793     /**
9794      * Brings this dialog to the front of any other visible dialogs
9795      * @return {Roo.BasicDialog} this
9796      */
9797     toFront : function(){
9798         Roo.DialogManager.bringToFront(this);
9799         return this;
9800     },
9801
9802     /**
9803      * Sends this dialog to the back (under) of any other visible dialogs
9804      * @return {Roo.BasicDialog} this
9805      */
9806     toBack : function(){
9807         Roo.DialogManager.sendToBack(this);
9808         return this;
9809     },
9810
9811     /**
9812      * Centers this dialog in the viewport
9813      * @return {Roo.BasicDialog} this
9814      */
9815     center : function(){
9816         var xy = this.el.getCenterXY(true);
9817         this.moveTo(xy[0], xy[1]);
9818         return this;
9819     },
9820
9821     /**
9822      * Moves the dialog's top-left corner to the specified point
9823      * @param {Number} x
9824      * @param {Number} y
9825      * @return {Roo.BasicDialog} this
9826      */
9827     moveTo : function(x, y){
9828         this.xy = [x,y];
9829         if(this.isVisible()){
9830             this.el.setXY(this.xy);
9831             this.adjustAssets();
9832         }
9833         return this;
9834     },
9835
9836     /**
9837      * Aligns the dialog to the specified element
9838      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9839      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9840      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9841      * @return {Roo.BasicDialog} this
9842      */
9843     alignTo : function(element, position, offsets){
9844         this.xy = this.el.getAlignToXY(element, position, offsets);
9845         if(this.isVisible()){
9846             this.el.setXY(this.xy);
9847             this.adjustAssets();
9848         }
9849         return this;
9850     },
9851
9852     /**
9853      * Anchors an element to another element and realigns it when the window is resized.
9854      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9855      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9856      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9857      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9858      * is a number, it is used as the buffer delay (defaults to 50ms).
9859      * @return {Roo.BasicDialog} this
9860      */
9861     anchorTo : function(el, alignment, offsets, monitorScroll){
9862         var action = function(){
9863             this.alignTo(el, alignment, offsets);
9864         };
9865         Roo.EventManager.onWindowResize(action, this);
9866         var tm = typeof monitorScroll;
9867         if(tm != 'undefined'){
9868             Roo.EventManager.on(window, 'scroll', action, this,
9869                 {buffer: tm == 'number' ? monitorScroll : 50});
9870         }
9871         action.call(this);
9872         return this;
9873     },
9874
9875     /**
9876      * Returns true if the dialog is visible
9877      * @return {Boolean}
9878      */
9879     isVisible : function(){
9880         return this.el.isVisible();
9881     },
9882
9883     // private
9884     animHide : function(callback){
9885         var b = Roo.get(this.animateTarget).getBox();
9886         this.proxy.show();
9887         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9888         this.el.hide();
9889         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9890                     this.hideEl.createDelegate(this, [callback]));
9891     },
9892
9893     /**
9894      * Hides the dialog.
9895      * @param {Function} callback (optional) Function to call when the dialog is hidden
9896      * @return {Roo.BasicDialog} this
9897      */
9898     hide : function(callback){
9899         if (this.fireEvent("beforehide", this) === false){
9900             return;
9901         }
9902         if(this.shadow){
9903             this.shadow.hide();
9904         }
9905         if(this.shim) {
9906           this.shim.hide();
9907         }
9908         // sometimes animateTarget seems to get set.. causing problems...
9909         // this just double checks..
9910         if(this.animateTarget && Roo.get(this.animateTarget)) {
9911            this.animHide(callback);
9912         }else{
9913             this.el.hide();
9914             this.hideEl(callback);
9915         }
9916         return this;
9917     },
9918
9919     // private
9920     hideEl : function(callback){
9921         this.proxy.hide();
9922         if(this.modal){
9923             this.mask.hide();
9924             Roo.get(document.body).removeClass("x-body-masked");
9925         }
9926         this.fireEvent("hide", this);
9927         if(typeof callback == "function"){
9928             callback();
9929         }
9930     },
9931
9932     // private
9933     hideAction : function(){
9934         this.setLeft("-10000px");
9935         this.setTop("-10000px");
9936         this.setStyle("visibility", "hidden");
9937     },
9938
9939     // private
9940     refreshSize : function(){
9941         this.size = this.el.getSize();
9942         this.xy = this.el.getXY();
9943         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9944     },
9945
9946     // private
9947     // z-index is managed by the DialogManager and may be overwritten at any time
9948     setZIndex : function(index){
9949         if(this.modal){
9950             this.mask.setStyle("z-index", index);
9951         }
9952         if(this.shim){
9953             this.shim.setStyle("z-index", ++index);
9954         }
9955         if(this.shadow){
9956             this.shadow.setZIndex(++index);
9957         }
9958         this.el.setStyle("z-index", ++index);
9959         if(this.proxy){
9960             this.proxy.setStyle("z-index", ++index);
9961         }
9962         if(this.resizer){
9963             this.resizer.proxy.setStyle("z-index", ++index);
9964         }
9965
9966         this.lastZIndex = index;
9967     },
9968
9969     /**
9970      * Returns the element for this dialog
9971      * @return {Roo.Element} The underlying dialog Element
9972      */
9973     getEl : function(){
9974         return this.el;
9975     }
9976 });
9977
9978 /**
9979  * @class Roo.DialogManager
9980  * Provides global access to BasicDialogs that have been created and
9981  * support for z-indexing (layering) multiple open dialogs.
9982  */
9983 Roo.DialogManager = function(){
9984     var list = {};
9985     var accessList = [];
9986     var front = null;
9987
9988     // private
9989     var sortDialogs = function(d1, d2){
9990         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
9991     };
9992
9993     // private
9994     var orderDialogs = function(){
9995         accessList.sort(sortDialogs);
9996         var seed = Roo.DialogManager.zseed;
9997         for(var i = 0, len = accessList.length; i < len; i++){
9998             var dlg = accessList[i];
9999             if(dlg){
10000                 dlg.setZIndex(seed + (i*10));
10001             }
10002         }
10003     };
10004
10005     return {
10006         /**
10007          * The starting z-index for BasicDialogs (defaults to 9000)
10008          * @type Number The z-index value
10009          */
10010         zseed : 9000,
10011
10012         // private
10013         register : function(dlg){
10014             list[dlg.id] = dlg;
10015             accessList.push(dlg);
10016         },
10017
10018         // private
10019         unregister : function(dlg){
10020             delete list[dlg.id];
10021             var i=0;
10022             var len=0;
10023             if(!accessList.indexOf){
10024                 for(  i = 0, len = accessList.length; i < len; i++){
10025                     if(accessList[i] == dlg){
10026                         accessList.splice(i, 1);
10027                         return;
10028                     }
10029                 }
10030             }else{
10031                  i = accessList.indexOf(dlg);
10032                 if(i != -1){
10033                     accessList.splice(i, 1);
10034                 }
10035             }
10036         },
10037
10038         /**
10039          * Gets a registered dialog by id
10040          * @param {String/Object} id The id of the dialog or a dialog
10041          * @return {Roo.BasicDialog} this
10042          */
10043         get : function(id){
10044             return typeof id == "object" ? id : list[id];
10045         },
10046
10047         /**
10048          * Brings the specified dialog to the front
10049          * @param {String/Object} dlg The id of the dialog or a dialog
10050          * @return {Roo.BasicDialog} this
10051          */
10052         bringToFront : function(dlg){
10053             dlg = this.get(dlg);
10054             if(dlg != front){
10055                 front = dlg;
10056                 dlg._lastAccess = new Date().getTime();
10057                 orderDialogs();
10058             }
10059             return dlg;
10060         },
10061
10062         /**
10063          * Sends the specified dialog to the back
10064          * @param {String/Object} dlg The id of the dialog or a dialog
10065          * @return {Roo.BasicDialog} this
10066          */
10067         sendToBack : function(dlg){
10068             dlg = this.get(dlg);
10069             dlg._lastAccess = -(new Date().getTime());
10070             orderDialogs();
10071             return dlg;
10072         },
10073
10074         /**
10075          * Hides all dialogs
10076          */
10077         hideAll : function(){
10078             for(var id in list){
10079                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10080                     list[id].hide();
10081                 }
10082             }
10083         }
10084     };
10085 }();
10086
10087 /**
10088  * @class Roo.LayoutDialog
10089  * @extends Roo.BasicDialog
10090  * Dialog which provides adjustments for working with a layout in a Dialog.
10091  * Add your necessary layout config options to the dialog's config.<br>
10092  * Example usage (including a nested layout):
10093  * <pre><code>
10094 if(!dialog){
10095     dialog = new Roo.LayoutDialog("download-dlg", {
10096         modal: true,
10097         width:600,
10098         height:450,
10099         shadow:true,
10100         minWidth:500,
10101         minHeight:350,
10102         autoTabs:true,
10103         proxyDrag:true,
10104         // layout config merges with the dialog config
10105         center:{
10106             tabPosition: "top",
10107             alwaysShowTabs: true
10108         }
10109     });
10110     dialog.addKeyListener(27, dialog.hide, dialog);
10111     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10112     dialog.addButton("Build It!", this.getDownload, this);
10113
10114     // we can even add nested layouts
10115     var innerLayout = new Roo.BorderLayout("dl-inner", {
10116         east: {
10117             initialSize: 200,
10118             autoScroll:true,
10119             split:true
10120         },
10121         center: {
10122             autoScroll:true
10123         }
10124     });
10125     innerLayout.beginUpdate();
10126     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10127     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10128     innerLayout.endUpdate(true);
10129
10130     var layout = dialog.getLayout();
10131     layout.beginUpdate();
10132     layout.add("center", new Roo.ContentPanel("standard-panel",
10133                         {title: "Download the Source", fitToFrame:true}));
10134     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10135                {title: "Build your own roo.js"}));
10136     layout.getRegion("center").showPanel(sp);
10137     layout.endUpdate();
10138 }
10139 </code></pre>
10140     * @constructor
10141     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10142     * @param {Object} config configuration options
10143   */
10144 Roo.LayoutDialog = function(el, cfg){
10145     
10146     var config=  cfg;
10147     if (typeof(cfg) == 'undefined') {
10148         config = Roo.apply({}, el);
10149         // not sure why we use documentElement here.. - it should always be body.
10150         // IE7 borks horribly if we use documentElement.
10151         // webkit also does not like documentElement - it creates a body element...
10152         el = Roo.get( document.body || document.documentElement ).createChild();
10153         //config.autoCreate = true;
10154     }
10155     
10156     
10157     config.autoTabs = false;
10158     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10159     this.body.setStyle({overflow:"hidden", position:"relative"});
10160     this.layout = new Roo.BorderLayout(this.body.dom, config);
10161     this.layout.monitorWindowResize = false;
10162     this.el.addClass("x-dlg-auto-layout");
10163     // fix case when center region overwrites center function
10164     this.center = Roo.BasicDialog.prototype.center;
10165     this.on("show", this.layout.layout, this.layout, true);
10166     if (config.items) {
10167         var xitems = config.items;
10168         delete config.items;
10169         Roo.each(xitems, this.addxtype, this);
10170     }
10171     
10172     
10173 };
10174 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10175     /**
10176      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10177      * @deprecated
10178      */
10179     endUpdate : function(){
10180         this.layout.endUpdate();
10181     },
10182
10183     /**
10184      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10185      *  @deprecated
10186      */
10187     beginUpdate : function(){
10188         this.layout.beginUpdate();
10189     },
10190
10191     /**
10192      * Get the BorderLayout for this dialog
10193      * @return {Roo.BorderLayout}
10194      */
10195     getLayout : function(){
10196         return this.layout;
10197     },
10198
10199     showEl : function(){
10200         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10201         if(Roo.isIE7){
10202             this.layout.layout();
10203         }
10204     },
10205
10206     // private
10207     // Use the syncHeightBeforeShow config option to control this automatically
10208     syncBodyHeight : function(){
10209         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10210         if(this.layout){this.layout.layout();}
10211     },
10212     
10213       /**
10214      * Add an xtype element (actually adds to the layout.)
10215      * @return {Object} xdata xtype object data.
10216      */
10217     
10218     addxtype : function(c) {
10219         return this.layout.addxtype(c);
10220     }
10221 });/*
10222  * Based on:
10223  * Ext JS Library 1.1.1
10224  * Copyright(c) 2006-2007, Ext JS, LLC.
10225  *
10226  * Originally Released Under LGPL - original licence link has changed is not relivant.
10227  *
10228  * Fork - LGPL
10229  * <script type="text/javascript">
10230  */
10231  
10232 /**
10233  * @class Roo.MessageBox
10234  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10235  * Example usage:
10236  *<pre><code>
10237 // Basic alert:
10238 Roo.Msg.alert('Status', 'Changes saved successfully.');
10239
10240 // Prompt for user data:
10241 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10242     if (btn == 'ok'){
10243         // process text value...
10244     }
10245 });
10246
10247 // Show a dialog using config options:
10248 Roo.Msg.show({
10249    title:'Save Changes?',
10250    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10251    buttons: Roo.Msg.YESNOCANCEL,
10252    fn: processResult,
10253    animEl: 'elId'
10254 });
10255 </code></pre>
10256  * @singleton
10257  */
10258 Roo.MessageBox = function(){
10259     var dlg, opt, mask, waitTimer;
10260     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10261     var buttons, activeTextEl, bwidth;
10262
10263     // private
10264     var handleButton = function(button){
10265         dlg.hide();
10266         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10267     };
10268
10269     // private
10270     var handleHide = function(){
10271         if(opt && opt.cls){
10272             dlg.el.removeClass(opt.cls);
10273         }
10274         if(waitTimer){
10275             Roo.TaskMgr.stop(waitTimer);
10276             waitTimer = null;
10277         }
10278     };
10279
10280     // private
10281     var updateButtons = function(b){
10282         var width = 0;
10283         if(!b){
10284             buttons["ok"].hide();
10285             buttons["cancel"].hide();
10286             buttons["yes"].hide();
10287             buttons["no"].hide();
10288             dlg.footer.dom.style.display = 'none';
10289             return width;
10290         }
10291         dlg.footer.dom.style.display = '';
10292         for(var k in buttons){
10293             if(typeof buttons[k] != "function"){
10294                 if(b[k]){
10295                     buttons[k].show();
10296                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10297                     width += buttons[k].el.getWidth()+15;
10298                 }else{
10299                     buttons[k].hide();
10300                 }
10301             }
10302         }
10303         return width;
10304     };
10305
10306     // private
10307     var handleEsc = function(d, k, e){
10308         if(opt && opt.closable !== false){
10309             dlg.hide();
10310         }
10311         if(e){
10312             e.stopEvent();
10313         }
10314     };
10315
10316     return {
10317         /**
10318          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10319          * @return {Roo.BasicDialog} The BasicDialog element
10320          */
10321         getDialog : function(){
10322            if(!dlg){
10323                 dlg = new Roo.BasicDialog("x-msg-box", {
10324                     autoCreate : true,
10325                     shadow: true,
10326                     draggable: true,
10327                     resizable:false,
10328                     constraintoviewport:false,
10329                     fixedcenter:true,
10330                     collapsible : false,
10331                     shim:true,
10332                     modal: true,
10333                     width:400, height:100,
10334                     buttonAlign:"center",
10335                     closeClick : function(){
10336                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10337                             handleButton("no");
10338                         }else{
10339                             handleButton("cancel");
10340                         }
10341                     }
10342                 });
10343                 dlg.on("hide", handleHide);
10344                 mask = dlg.mask;
10345                 dlg.addKeyListener(27, handleEsc);
10346                 buttons = {};
10347                 var bt = this.buttonText;
10348                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10349                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10350                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10351                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10352                 bodyEl = dlg.body.createChild({
10353
10354                     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>'
10355                 });
10356                 msgEl = bodyEl.dom.firstChild;
10357                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10358                 textboxEl.enableDisplayMode();
10359                 textboxEl.addKeyListener([10,13], function(){
10360                     if(dlg.isVisible() && opt && opt.buttons){
10361                         if(opt.buttons.ok){
10362                             handleButton("ok");
10363                         }else if(opt.buttons.yes){
10364                             handleButton("yes");
10365                         }
10366                     }
10367                 });
10368                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10369                 textareaEl.enableDisplayMode();
10370                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10371                 progressEl.enableDisplayMode();
10372                 var pf = progressEl.dom.firstChild;
10373                 if (pf) {
10374                     pp = Roo.get(pf.firstChild);
10375                     pp.setHeight(pf.offsetHeight);
10376                 }
10377                 
10378             }
10379             return dlg;
10380         },
10381
10382         /**
10383          * Updates the message box body text
10384          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10385          * the XHTML-compliant non-breaking space character '&amp;#160;')
10386          * @return {Roo.MessageBox} This message box
10387          */
10388         updateText : function(text){
10389             if(!dlg.isVisible() && !opt.width){
10390                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10391             }
10392             msgEl.innerHTML = text || '&#160;';
10393       
10394             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10395             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10396             var w = Math.max(
10397                     Math.min(opt.width || cw , this.maxWidth), 
10398                     Math.max(opt.minWidth || this.minWidth, bwidth)
10399             );
10400             if(opt.prompt){
10401                 activeTextEl.setWidth(w);
10402             }
10403             if(dlg.isVisible()){
10404                 dlg.fixedcenter = false;
10405             }
10406             // to big, make it scroll. = But as usual stupid IE does not support
10407             // !important..
10408             
10409             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10410                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10411                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10412             } else {
10413                 bodyEl.dom.style.height = '';
10414                 bodyEl.dom.style.overflowY = '';
10415             }
10416             if (cw > w) {
10417                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10418             } else {
10419                 bodyEl.dom.style.overflowX = '';
10420             }
10421             
10422             dlg.setContentSize(w, bodyEl.getHeight());
10423             if(dlg.isVisible()){
10424                 dlg.fixedcenter = true;
10425             }
10426             return this;
10427         },
10428
10429         /**
10430          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10431          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10432          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10433          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10434          * @return {Roo.MessageBox} This message box
10435          */
10436         updateProgress : function(value, text){
10437             if(text){
10438                 this.updateText(text);
10439             }
10440             if (pp) { // weird bug on my firefox - for some reason this is not defined
10441                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10442             }
10443             return this;
10444         },        
10445
10446         /**
10447          * Returns true if the message box is currently displayed
10448          * @return {Boolean} True if the message box is visible, else false
10449          */
10450         isVisible : function(){
10451             return dlg && dlg.isVisible();  
10452         },
10453
10454         /**
10455          * Hides the message box if it is displayed
10456          */
10457         hide : function(){
10458             if(this.isVisible()){
10459                 dlg.hide();
10460             }  
10461         },
10462
10463         /**
10464          * Displays a new message box, or reinitializes an existing message box, based on the config options
10465          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10466          * The following config object properties are supported:
10467          * <pre>
10468 Property    Type             Description
10469 ----------  ---------------  ------------------------------------------------------------------------------------
10470 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10471                                    closes (defaults to undefined)
10472 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10473                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10474 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10475                                    progress and wait dialogs will ignore this property and always hide the
10476                                    close button as they can only be closed programmatically.
10477 cls               String           A custom CSS class to apply to the message box element
10478 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10479                                    displayed (defaults to 75)
10480 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10481                                    function will be btn (the name of the button that was clicked, if applicable,
10482                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10483                                    Progress and wait dialogs will ignore this option since they do not respond to
10484                                    user actions and can only be closed programmatically, so any required function
10485                                    should be called by the same code after it closes the dialog.
10486 icon              String           A CSS class that provides a background image to be used as an icon for
10487                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10488 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10489 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10490 modal             Boolean          False to allow user interaction with the page while the message box is
10491                                    displayed (defaults to true)
10492 msg               String           A string that will replace the existing message box body text (defaults
10493                                    to the XHTML-compliant non-breaking space character '&#160;')
10494 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10495 progress          Boolean          True to display a progress bar (defaults to false)
10496 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10497 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10498 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10499 title             String           The title text
10500 value             String           The string value to set into the active textbox element if displayed
10501 wait              Boolean          True to display a progress bar (defaults to false)
10502 width             Number           The width of the dialog in pixels
10503 </pre>
10504          *
10505          * Example usage:
10506          * <pre><code>
10507 Roo.Msg.show({
10508    title: 'Address',
10509    msg: 'Please enter your address:',
10510    width: 300,
10511    buttons: Roo.MessageBox.OKCANCEL,
10512    multiline: true,
10513    fn: saveAddress,
10514    animEl: 'addAddressBtn'
10515 });
10516 </code></pre>
10517          * @param {Object} config Configuration options
10518          * @return {Roo.MessageBox} This message box
10519          */
10520         show : function(options)
10521         {
10522             
10523             // this causes nightmares if you show one dialog after another
10524             // especially on callbacks..
10525              
10526             if(this.isVisible()){
10527                 
10528                 this.hide();
10529                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10530                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10531                 Roo.log("New Dialog Message:" +  options.msg )
10532                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10533                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10534                 
10535             }
10536             var d = this.getDialog();
10537             opt = options;
10538             d.setTitle(opt.title || "&#160;");
10539             d.close.setDisplayed(opt.closable !== false);
10540             activeTextEl = textboxEl;
10541             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10542             if(opt.prompt){
10543                 if(opt.multiline){
10544                     textboxEl.hide();
10545                     textareaEl.show();
10546                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10547                         opt.multiline : this.defaultTextHeight);
10548                     activeTextEl = textareaEl;
10549                 }else{
10550                     textboxEl.show();
10551                     textareaEl.hide();
10552                 }
10553             }else{
10554                 textboxEl.hide();
10555                 textareaEl.hide();
10556             }
10557             progressEl.setDisplayed(opt.progress === true);
10558             this.updateProgress(0);
10559             activeTextEl.dom.value = opt.value || "";
10560             if(opt.prompt){
10561                 dlg.setDefaultButton(activeTextEl);
10562             }else{
10563                 var bs = opt.buttons;
10564                 var db = null;
10565                 if(bs && bs.ok){
10566                     db = buttons["ok"];
10567                 }else if(bs && bs.yes){
10568                     db = buttons["yes"];
10569                 }
10570                 dlg.setDefaultButton(db);
10571             }
10572             bwidth = updateButtons(opt.buttons);
10573             this.updateText(opt.msg);
10574             if(opt.cls){
10575                 d.el.addClass(opt.cls);
10576             }
10577             d.proxyDrag = opt.proxyDrag === true;
10578             d.modal = opt.modal !== false;
10579             d.mask = opt.modal !== false ? mask : false;
10580             if(!d.isVisible()){
10581                 // force it to the end of the z-index stack so it gets a cursor in FF
10582                 document.body.appendChild(dlg.el.dom);
10583                 d.animateTarget = null;
10584                 d.show(options.animEl);
10585             }
10586             return this;
10587         },
10588
10589         /**
10590          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10591          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10592          * and closing the message box when the process is complete.
10593          * @param {String} title The title bar text
10594          * @param {String} msg The message box body text
10595          * @return {Roo.MessageBox} This message box
10596          */
10597         progress : function(title, msg){
10598             this.show({
10599                 title : title,
10600                 msg : msg,
10601                 buttons: false,
10602                 progress:true,
10603                 closable:false,
10604                 minWidth: this.minProgressWidth,
10605                 modal : true
10606             });
10607             return this;
10608         },
10609
10610         /**
10611          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10612          * If a callback function is passed it will be called after the user clicks the button, and the
10613          * id of the button that was clicked will be passed as the only parameter to the callback
10614          * (could also be the top-right close button).
10615          * @param {String} title The title bar text
10616          * @param {String} msg The message box body text
10617          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10618          * @param {Object} scope (optional) The scope of the callback function
10619          * @return {Roo.MessageBox} This message box
10620          */
10621         alert : function(title, msg, fn, scope){
10622             this.show({
10623                 title : title,
10624                 msg : msg,
10625                 buttons: this.OK,
10626                 fn: fn,
10627                 scope : scope,
10628                 modal : true
10629             });
10630             return this;
10631         },
10632
10633         /**
10634          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10635          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10636          * You are responsible for closing the message box when the process is complete.
10637          * @param {String} msg The message box body text
10638          * @param {String} title (optional) The title bar text
10639          * @return {Roo.MessageBox} This message box
10640          */
10641         wait : function(msg, title){
10642             this.show({
10643                 title : title,
10644                 msg : msg,
10645                 buttons: false,
10646                 closable:false,
10647                 progress:true,
10648                 modal:true,
10649                 width:300,
10650                 wait:true
10651             });
10652             waitTimer = Roo.TaskMgr.start({
10653                 run: function(i){
10654                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10655                 },
10656                 interval: 1000
10657             });
10658             return this;
10659         },
10660
10661         /**
10662          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10663          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10664          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10665          * @param {String} title The title bar text
10666          * @param {String} msg The message box body text
10667          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10668          * @param {Object} scope (optional) The scope of the callback function
10669          * @return {Roo.MessageBox} This message box
10670          */
10671         confirm : function(title, msg, fn, scope){
10672             this.show({
10673                 title : title,
10674                 msg : msg,
10675                 buttons: this.YESNO,
10676                 fn: fn,
10677                 scope : scope,
10678                 modal : true
10679             });
10680             return this;
10681         },
10682
10683         /**
10684          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10685          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10686          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10687          * (could also be the top-right close button) and the text that was entered will be passed as the two
10688          * parameters to the callback.
10689          * @param {String} title The title bar text
10690          * @param {String} msg The message box body text
10691          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10692          * @param {Object} scope (optional) The scope of the callback function
10693          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10694          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10695          * @return {Roo.MessageBox} This message box
10696          */
10697         prompt : function(title, msg, fn, scope, multiline){
10698             this.show({
10699                 title : title,
10700                 msg : msg,
10701                 buttons: this.OKCANCEL,
10702                 fn: fn,
10703                 minWidth:250,
10704                 scope : scope,
10705                 prompt:true,
10706                 multiline: multiline,
10707                 modal : true
10708             });
10709             return this;
10710         },
10711
10712         /**
10713          * Button config that displays a single OK button
10714          * @type Object
10715          */
10716         OK : {ok:true},
10717         /**
10718          * Button config that displays Yes and No buttons
10719          * @type Object
10720          */
10721         YESNO : {yes:true, no:true},
10722         /**
10723          * Button config that displays OK and Cancel buttons
10724          * @type Object
10725          */
10726         OKCANCEL : {ok:true, cancel:true},
10727         /**
10728          * Button config that displays Yes, No and Cancel buttons
10729          * @type Object
10730          */
10731         YESNOCANCEL : {yes:true, no:true, cancel:true},
10732
10733         /**
10734          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10735          * @type Number
10736          */
10737         defaultTextHeight : 75,
10738         /**
10739          * The maximum width in pixels of the message box (defaults to 600)
10740          * @type Number
10741          */
10742         maxWidth : 600,
10743         /**
10744          * The minimum width in pixels of the message box (defaults to 100)
10745          * @type Number
10746          */
10747         minWidth : 100,
10748         /**
10749          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10750          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10751          * @type Number
10752          */
10753         minProgressWidth : 250,
10754         /**
10755          * An object containing the default button text strings that can be overriden for localized language support.
10756          * Supported properties are: ok, cancel, yes and no.
10757          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10758          * @type Object
10759          */
10760         buttonText : {
10761             ok : "OK",
10762             cancel : "Cancel",
10763             yes : "Yes",
10764             no : "No"
10765         }
10766     };
10767 }();
10768
10769 /**
10770  * Shorthand for {@link Roo.MessageBox}
10771  */
10772 Roo.Msg = Roo.MessageBox;/*
10773  * Based on:
10774  * Ext JS Library 1.1.1
10775  * Copyright(c) 2006-2007, Ext JS, LLC.
10776  *
10777  * Originally Released Under LGPL - original licence link has changed is not relivant.
10778  *
10779  * Fork - LGPL
10780  * <script type="text/javascript">
10781  */
10782 /**
10783  * @class Roo.QuickTips
10784  * Provides attractive and customizable tooltips for any element.
10785  * @singleton
10786  */
10787 Roo.QuickTips = function(){
10788     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10789     var ce, bd, xy, dd;
10790     var visible = false, disabled = true, inited = false;
10791     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10792     
10793     var onOver = function(e){
10794         if(disabled){
10795             return;
10796         }
10797         var t = e.getTarget();
10798         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10799             return;
10800         }
10801         if(ce && t == ce.el){
10802             clearTimeout(hideProc);
10803             return;
10804         }
10805         if(t && tagEls[t.id]){
10806             tagEls[t.id].el = t;
10807             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10808             return;
10809         }
10810         var ttp, et = Roo.fly(t);
10811         var ns = cfg.namespace;
10812         if(tm.interceptTitles && t.title){
10813             ttp = t.title;
10814             t.qtip = ttp;
10815             t.removeAttribute("title");
10816             e.preventDefault();
10817         }else{
10818             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10819         }
10820         if(ttp){
10821             showProc = show.defer(tm.showDelay, tm, [{
10822                 el: t, 
10823                 text: ttp.replace(/\\n/g,'<br/>'),
10824                 width: et.getAttributeNS(ns, cfg.width),
10825                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10826                 title: et.getAttributeNS(ns, cfg.title),
10827                     cls: et.getAttributeNS(ns, cfg.cls)
10828             }]);
10829         }
10830     };
10831     
10832     var onOut = function(e){
10833         clearTimeout(showProc);
10834         var t = e.getTarget();
10835         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10836             hideProc = setTimeout(hide, tm.hideDelay);
10837         }
10838     };
10839     
10840     var onMove = function(e){
10841         if(disabled){
10842             return;
10843         }
10844         xy = e.getXY();
10845         xy[1] += 18;
10846         if(tm.trackMouse && ce){
10847             el.setXY(xy);
10848         }
10849     };
10850     
10851     var onDown = function(e){
10852         clearTimeout(showProc);
10853         clearTimeout(hideProc);
10854         if(!e.within(el)){
10855             if(tm.hideOnClick){
10856                 hide();
10857                 tm.disable();
10858                 tm.enable.defer(100, tm);
10859             }
10860         }
10861     };
10862     
10863     var getPad = function(){
10864         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10865     };
10866
10867     var show = function(o){
10868         if(disabled){
10869             return;
10870         }
10871         clearTimeout(dismissProc);
10872         ce = o;
10873         if(removeCls){ // in case manually hidden
10874             el.removeClass(removeCls);
10875             removeCls = null;
10876         }
10877         if(ce.cls){
10878             el.addClass(ce.cls);
10879             removeCls = ce.cls;
10880         }
10881         if(ce.title){
10882             tipTitle.update(ce.title);
10883             tipTitle.show();
10884         }else{
10885             tipTitle.update('');
10886             tipTitle.hide();
10887         }
10888         el.dom.style.width  = tm.maxWidth+'px';
10889         //tipBody.dom.style.width = '';
10890         tipBodyText.update(o.text);
10891         var p = getPad(), w = ce.width;
10892         if(!w){
10893             var td = tipBodyText.dom;
10894             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10895             if(aw > tm.maxWidth){
10896                 w = tm.maxWidth;
10897             }else if(aw < tm.minWidth){
10898                 w = tm.minWidth;
10899             }else{
10900                 w = aw;
10901             }
10902         }
10903         //tipBody.setWidth(w);
10904         el.setWidth(parseInt(w, 10) + p);
10905         if(ce.autoHide === false){
10906             close.setDisplayed(true);
10907             if(dd){
10908                 dd.unlock();
10909             }
10910         }else{
10911             close.setDisplayed(false);
10912             if(dd){
10913                 dd.lock();
10914             }
10915         }
10916         if(xy){
10917             el.avoidY = xy[1]-18;
10918             el.setXY(xy);
10919         }
10920         if(tm.animate){
10921             el.setOpacity(.1);
10922             el.setStyle("visibility", "visible");
10923             el.fadeIn({callback: afterShow});
10924         }else{
10925             afterShow();
10926         }
10927     };
10928     
10929     var afterShow = function(){
10930         if(ce){
10931             el.show();
10932             esc.enable();
10933             if(tm.autoDismiss && ce.autoHide !== false){
10934                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10935             }
10936         }
10937     };
10938     
10939     var hide = function(noanim){
10940         clearTimeout(dismissProc);
10941         clearTimeout(hideProc);
10942         ce = null;
10943         if(el.isVisible()){
10944             esc.disable();
10945             if(noanim !== true && tm.animate){
10946                 el.fadeOut({callback: afterHide});
10947             }else{
10948                 afterHide();
10949             } 
10950         }
10951     };
10952     
10953     var afterHide = function(){
10954         el.hide();
10955         if(removeCls){
10956             el.removeClass(removeCls);
10957             removeCls = null;
10958         }
10959     };
10960     
10961     return {
10962         /**
10963         * @cfg {Number} minWidth
10964         * The minimum width of the quick tip (defaults to 40)
10965         */
10966        minWidth : 40,
10967         /**
10968         * @cfg {Number} maxWidth
10969         * The maximum width of the quick tip (defaults to 300)
10970         */
10971        maxWidth : 300,
10972         /**
10973         * @cfg {Boolean} interceptTitles
10974         * True to automatically use the element's DOM title value if available (defaults to false)
10975         */
10976        interceptTitles : false,
10977         /**
10978         * @cfg {Boolean} trackMouse
10979         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
10980         */
10981        trackMouse : false,
10982         /**
10983         * @cfg {Boolean} hideOnClick
10984         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
10985         */
10986        hideOnClick : true,
10987         /**
10988         * @cfg {Number} showDelay
10989         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
10990         */
10991        showDelay : 500,
10992         /**
10993         * @cfg {Number} hideDelay
10994         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
10995         */
10996        hideDelay : 200,
10997         /**
10998         * @cfg {Boolean} autoHide
10999         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11000         * Used in conjunction with hideDelay.
11001         */
11002        autoHide : true,
11003         /**
11004         * @cfg {Boolean}
11005         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11006         * (defaults to true).  Used in conjunction with autoDismissDelay.
11007         */
11008        autoDismiss : true,
11009         /**
11010         * @cfg {Number}
11011         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11012         */
11013        autoDismissDelay : 5000,
11014        /**
11015         * @cfg {Boolean} animate
11016         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11017         */
11018        animate : false,
11019
11020        /**
11021         * @cfg {String} title
11022         * Title text to display (defaults to '').  This can be any valid HTML markup.
11023         */
11024         title: '',
11025        /**
11026         * @cfg {String} text
11027         * Body text to display (defaults to '').  This can be any valid HTML markup.
11028         */
11029         text : '',
11030        /**
11031         * @cfg {String} cls
11032         * A CSS class to apply to the base quick tip element (defaults to '').
11033         */
11034         cls : '',
11035        /**
11036         * @cfg {Number} width
11037         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11038         * minWidth or maxWidth.
11039         */
11040         width : null,
11041
11042     /**
11043      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11044      * or display QuickTips in a page.
11045      */
11046        init : function(){
11047           tm = Roo.QuickTips;
11048           cfg = tm.tagConfig;
11049           if(!inited){
11050               if(!Roo.isReady){ // allow calling of init() before onReady
11051                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11052                   return;
11053               }
11054               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11055               el.fxDefaults = {stopFx: true};
11056               // maximum custom styling
11057               //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>');
11058               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>');              
11059               tipTitle = el.child('h3');
11060               tipTitle.enableDisplayMode("block");
11061               tipBody = el.child('div.x-tip-bd');
11062               tipBodyText = el.child('div.x-tip-bd-inner');
11063               //bdLeft = el.child('div.x-tip-bd-left');
11064               //bdRight = el.child('div.x-tip-bd-right');
11065               close = el.child('div.x-tip-close');
11066               close.enableDisplayMode("block");
11067               close.on("click", hide);
11068               var d = Roo.get(document);
11069               d.on("mousedown", onDown);
11070               d.on("mouseover", onOver);
11071               d.on("mouseout", onOut);
11072               d.on("mousemove", onMove);
11073               esc = d.addKeyListener(27, hide);
11074               esc.disable();
11075               if(Roo.dd.DD){
11076                   dd = el.initDD("default", null, {
11077                       onDrag : function(){
11078                           el.sync();  
11079                       }
11080                   });
11081                   dd.setHandleElId(tipTitle.id);
11082                   dd.lock();
11083               }
11084               inited = true;
11085           }
11086           this.enable(); 
11087        },
11088
11089     /**
11090      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11091      * are supported:
11092      * <pre>
11093 Property    Type                   Description
11094 ----------  ---------------------  ------------------------------------------------------------------------
11095 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11096      * </ul>
11097      * @param {Object} config The config object
11098      */
11099        register : function(config){
11100            var cs = config instanceof Array ? config : arguments;
11101            for(var i = 0, len = cs.length; i < len; i++) {
11102                var c = cs[i];
11103                var target = c.target;
11104                if(target){
11105                    if(target instanceof Array){
11106                        for(var j = 0, jlen = target.length; j < jlen; j++){
11107                            tagEls[target[j]] = c;
11108                        }
11109                    }else{
11110                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11111                    }
11112                }
11113            }
11114        },
11115
11116     /**
11117      * Removes this quick tip from its element and destroys it.
11118      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11119      */
11120        unregister : function(el){
11121            delete tagEls[Roo.id(el)];
11122        },
11123
11124     /**
11125      * Enable this quick tip.
11126      */
11127        enable : function(){
11128            if(inited && disabled){
11129                locks.pop();
11130                if(locks.length < 1){
11131                    disabled = false;
11132                }
11133            }
11134        },
11135
11136     /**
11137      * Disable this quick tip.
11138      */
11139        disable : function(){
11140           disabled = true;
11141           clearTimeout(showProc);
11142           clearTimeout(hideProc);
11143           clearTimeout(dismissProc);
11144           if(ce){
11145               hide(true);
11146           }
11147           locks.push(1);
11148        },
11149
11150     /**
11151      * Returns true if the quick tip is enabled, else false.
11152      */
11153        isEnabled : function(){
11154             return !disabled;
11155        },
11156
11157         // private
11158        tagConfig : {
11159            namespace : "roo", // was ext?? this may break..
11160            alt_namespace : "ext",
11161            attribute : "qtip",
11162            width : "width",
11163            target : "target",
11164            title : "qtitle",
11165            hide : "hide",
11166            cls : "qclass"
11167        }
11168    };
11169 }();
11170
11171 // backwards compat
11172 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11173  * Based on:
11174  * Ext JS Library 1.1.1
11175  * Copyright(c) 2006-2007, Ext JS, LLC.
11176  *
11177  * Originally Released Under LGPL - original licence link has changed is not relivant.
11178  *
11179  * Fork - LGPL
11180  * <script type="text/javascript">
11181  */
11182  
11183
11184 /**
11185  * @class Roo.tree.TreePanel
11186  * @extends Roo.data.Tree
11187
11188  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11189  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11190  * @cfg {Boolean} enableDD true to enable drag and drop
11191  * @cfg {Boolean} enableDrag true to enable just drag
11192  * @cfg {Boolean} enableDrop true to enable just drop
11193  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11194  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11195  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11196  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11197  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11198  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11199  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11200  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11201  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11202  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11203  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11204  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11205  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11206  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11207  * @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>
11208  * @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>
11209  * 
11210  * @constructor
11211  * @param {String/HTMLElement/Element} el The container element
11212  * @param {Object} config
11213  */
11214 Roo.tree.TreePanel = function(el, config){
11215     var root = false;
11216     var loader = false;
11217     if (config.root) {
11218         root = config.root;
11219         delete config.root;
11220     }
11221     if (config.loader) {
11222         loader = config.loader;
11223         delete config.loader;
11224     }
11225     
11226     Roo.apply(this, config);
11227     Roo.tree.TreePanel.superclass.constructor.call(this);
11228     this.el = Roo.get(el);
11229     this.el.addClass('x-tree');
11230     //console.log(root);
11231     if (root) {
11232         this.setRootNode( Roo.factory(root, Roo.tree));
11233     }
11234     if (loader) {
11235         this.loader = Roo.factory(loader, Roo.tree);
11236     }
11237    /**
11238     * Read-only. The id of the container element becomes this TreePanel's id.
11239     */
11240     this.id = this.el.id;
11241     this.addEvents({
11242         /**
11243         * @event beforeload
11244         * Fires before a node is loaded, return false to cancel
11245         * @param {Node} node The node being loaded
11246         */
11247         "beforeload" : true,
11248         /**
11249         * @event load
11250         * Fires when a node is loaded
11251         * @param {Node} node The node that was loaded
11252         */
11253         "load" : true,
11254         /**
11255         * @event textchange
11256         * Fires when the text for a node is changed
11257         * @param {Node} node The node
11258         * @param {String} text The new text
11259         * @param {String} oldText The old text
11260         */
11261         "textchange" : true,
11262         /**
11263         * @event beforeexpand
11264         * Fires before a node is expanded, return false to cancel.
11265         * @param {Node} node The node
11266         * @param {Boolean} deep
11267         * @param {Boolean} anim
11268         */
11269         "beforeexpand" : true,
11270         /**
11271         * @event beforecollapse
11272         * Fires before a node is collapsed, return false to cancel.
11273         * @param {Node} node The node
11274         * @param {Boolean} deep
11275         * @param {Boolean} anim
11276         */
11277         "beforecollapse" : true,
11278         /**
11279         * @event expand
11280         * Fires when a node is expanded
11281         * @param {Node} node The node
11282         */
11283         "expand" : true,
11284         /**
11285         * @event disabledchange
11286         * Fires when the disabled status of a node changes
11287         * @param {Node} node The node
11288         * @param {Boolean} disabled
11289         */
11290         "disabledchange" : true,
11291         /**
11292         * @event collapse
11293         * Fires when a node is collapsed
11294         * @param {Node} node The node
11295         */
11296         "collapse" : true,
11297         /**
11298         * @event beforeclick
11299         * Fires before click processing on a node. Return false to cancel the default action.
11300         * @param {Node} node The node
11301         * @param {Roo.EventObject} e The event object
11302         */
11303         "beforeclick":true,
11304         /**
11305         * @event checkchange
11306         * Fires when a node with a checkbox's checked property changes
11307         * @param {Node} this This node
11308         * @param {Boolean} checked
11309         */
11310         "checkchange":true,
11311         /**
11312         * @event click
11313         * Fires when a node is clicked
11314         * @param {Node} node The node
11315         * @param {Roo.EventObject} e The event object
11316         */
11317         "click":true,
11318         /**
11319         * @event dblclick
11320         * Fires when a node is double clicked
11321         * @param {Node} node The node
11322         * @param {Roo.EventObject} e The event object
11323         */
11324         "dblclick":true,
11325         /**
11326         * @event contextmenu
11327         * Fires when a node is right clicked
11328         * @param {Node} node The node
11329         * @param {Roo.EventObject} e The event object
11330         */
11331         "contextmenu":true,
11332         /**
11333         * @event beforechildrenrendered
11334         * Fires right before the child nodes for a node are rendered
11335         * @param {Node} node The node
11336         */
11337         "beforechildrenrendered":true,
11338         /**
11339         * @event startdrag
11340         * Fires when a node starts being dragged
11341         * @param {Roo.tree.TreePanel} this
11342         * @param {Roo.tree.TreeNode} node
11343         * @param {event} e The raw browser event
11344         */ 
11345        "startdrag" : true,
11346        /**
11347         * @event enddrag
11348         * Fires when a drag operation is complete
11349         * @param {Roo.tree.TreePanel} this
11350         * @param {Roo.tree.TreeNode} node
11351         * @param {event} e The raw browser event
11352         */
11353        "enddrag" : true,
11354        /**
11355         * @event dragdrop
11356         * Fires when a dragged node is dropped on a valid DD target
11357         * @param {Roo.tree.TreePanel} this
11358         * @param {Roo.tree.TreeNode} node
11359         * @param {DD} dd The dd it was dropped on
11360         * @param {event} e The raw browser event
11361         */
11362        "dragdrop" : true,
11363        /**
11364         * @event beforenodedrop
11365         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11366         * passed to handlers has the following properties:<br />
11367         * <ul style="padding:5px;padding-left:16px;">
11368         * <li>tree - The TreePanel</li>
11369         * <li>target - The node being targeted for the drop</li>
11370         * <li>data - The drag data from the drag source</li>
11371         * <li>point - The point of the drop - append, above or below</li>
11372         * <li>source - The drag source</li>
11373         * <li>rawEvent - Raw mouse event</li>
11374         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11375         * to be inserted by setting them on this object.</li>
11376         * <li>cancel - Set this to true to cancel the drop.</li>
11377         * </ul>
11378         * @param {Object} dropEvent
11379         */
11380        "beforenodedrop" : true,
11381        /**
11382         * @event nodedrop
11383         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11384         * passed to handlers has the following properties:<br />
11385         * <ul style="padding:5px;padding-left:16px;">
11386         * <li>tree - The TreePanel</li>
11387         * <li>target - The node being targeted for the drop</li>
11388         * <li>data - The drag data from the drag source</li>
11389         * <li>point - The point of the drop - append, above or below</li>
11390         * <li>source - The drag source</li>
11391         * <li>rawEvent - Raw mouse event</li>
11392         * <li>dropNode - Dropped node(s).</li>
11393         * </ul>
11394         * @param {Object} dropEvent
11395         */
11396        "nodedrop" : true,
11397         /**
11398         * @event nodedragover
11399         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11400         * passed to handlers has the following properties:<br />
11401         * <ul style="padding:5px;padding-left:16px;">
11402         * <li>tree - The TreePanel</li>
11403         * <li>target - The node being targeted for the drop</li>
11404         * <li>data - The drag data from the drag source</li>
11405         * <li>point - The point of the drop - append, above or below</li>
11406         * <li>source - The drag source</li>
11407         * <li>rawEvent - Raw mouse event</li>
11408         * <li>dropNode - Drop node(s) provided by the source.</li>
11409         * <li>cancel - Set this to true to signal drop not allowed.</li>
11410         * </ul>
11411         * @param {Object} dragOverEvent
11412         */
11413        "nodedragover" : true,
11414        /**
11415         * @event appendnode
11416         * Fires when append node to the tree
11417         * @param {Roo.tree.TreePanel} this
11418         * @param {Roo.tree.TreeNode} node
11419         * @param {Number} index The index of the newly appended node
11420         */
11421        "appendnode" : true
11422         
11423     });
11424     if(this.singleExpand){
11425        this.on("beforeexpand", this.restrictExpand, this);
11426     }
11427     if (this.editor) {
11428         this.editor.tree = this;
11429         this.editor = Roo.factory(this.editor, Roo.tree);
11430     }
11431     
11432     if (this.selModel) {
11433         this.selModel = Roo.factory(this.selModel, Roo.tree);
11434     }
11435    
11436 };
11437 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11438     rootVisible : true,
11439     animate: Roo.enableFx,
11440     lines : true,
11441     enableDD : false,
11442     hlDrop : Roo.enableFx,
11443   
11444     renderer: false,
11445     
11446     rendererTip: false,
11447     // private
11448     restrictExpand : function(node){
11449         var p = node.parentNode;
11450         if(p){
11451             if(p.expandedChild && p.expandedChild.parentNode == p){
11452                 p.expandedChild.collapse();
11453             }
11454             p.expandedChild = node;
11455         }
11456     },
11457
11458     // private override
11459     setRootNode : function(node){
11460         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11461         if(!this.rootVisible){
11462             node.ui = new Roo.tree.RootTreeNodeUI(node);
11463         }
11464         return node;
11465     },
11466
11467     /**
11468      * Returns the container element for this TreePanel
11469      */
11470     getEl : function(){
11471         return this.el;
11472     },
11473
11474     /**
11475      * Returns the default TreeLoader for this TreePanel
11476      */
11477     getLoader : function(){
11478         return this.loader;
11479     },
11480
11481     /**
11482      * Expand all nodes
11483      */
11484     expandAll : function(){
11485         this.root.expand(true);
11486     },
11487
11488     /**
11489      * Collapse all nodes
11490      */
11491     collapseAll : function(){
11492         this.root.collapse(true);
11493     },
11494
11495     /**
11496      * Returns the selection model used by this TreePanel
11497      */
11498     getSelectionModel : function(){
11499         if(!this.selModel){
11500             this.selModel = new Roo.tree.DefaultSelectionModel();
11501         }
11502         return this.selModel;
11503     },
11504
11505     /**
11506      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11507      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11508      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11509      * @return {Array}
11510      */
11511     getChecked : function(a, startNode){
11512         startNode = startNode || this.root;
11513         var r = [];
11514         var f = function(){
11515             if(this.attributes.checked){
11516                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11517             }
11518         }
11519         startNode.cascade(f);
11520         return r;
11521     },
11522
11523     /**
11524      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11525      * @param {String} path
11526      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11527      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11528      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11529      */
11530     expandPath : function(path, attr, callback){
11531         attr = attr || "id";
11532         var keys = path.split(this.pathSeparator);
11533         var curNode = this.root;
11534         if(curNode.attributes[attr] != keys[1]){ // invalid root
11535             if(callback){
11536                 callback(false, null);
11537             }
11538             return;
11539         }
11540         var index = 1;
11541         var f = function(){
11542             if(++index == keys.length){
11543                 if(callback){
11544                     callback(true, curNode);
11545                 }
11546                 return;
11547             }
11548             var c = curNode.findChild(attr, keys[index]);
11549             if(!c){
11550                 if(callback){
11551                     callback(false, curNode);
11552                 }
11553                 return;
11554             }
11555             curNode = c;
11556             c.expand(false, false, f);
11557         };
11558         curNode.expand(false, false, f);
11559     },
11560
11561     /**
11562      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11563      * @param {String} path
11564      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11565      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11566      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11567      */
11568     selectPath : function(path, attr, callback){
11569         attr = attr || "id";
11570         var keys = path.split(this.pathSeparator);
11571         var v = keys.pop();
11572         if(keys.length > 0){
11573             var f = function(success, node){
11574                 if(success && node){
11575                     var n = node.findChild(attr, v);
11576                     if(n){
11577                         n.select();
11578                         if(callback){
11579                             callback(true, n);
11580                         }
11581                     }else if(callback){
11582                         callback(false, n);
11583                     }
11584                 }else{
11585                     if(callback){
11586                         callback(false, n);
11587                     }
11588                 }
11589             };
11590             this.expandPath(keys.join(this.pathSeparator), attr, f);
11591         }else{
11592             this.root.select();
11593             if(callback){
11594                 callback(true, this.root);
11595             }
11596         }
11597     },
11598
11599     getTreeEl : function(){
11600         return this.el;
11601     },
11602
11603     /**
11604      * Trigger rendering of this TreePanel
11605      */
11606     render : function(){
11607         if (this.innerCt) {
11608             return this; // stop it rendering more than once!!
11609         }
11610         
11611         this.innerCt = this.el.createChild({tag:"ul",
11612                cls:"x-tree-root-ct " +
11613                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11614
11615         if(this.containerScroll){
11616             Roo.dd.ScrollManager.register(this.el);
11617         }
11618         if((this.enableDD || this.enableDrop) && !this.dropZone){
11619            /**
11620             * The dropZone used by this tree if drop is enabled
11621             * @type Roo.tree.TreeDropZone
11622             */
11623              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11624                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11625            });
11626         }
11627         if((this.enableDD || this.enableDrag) && !this.dragZone){
11628            /**
11629             * The dragZone used by this tree if drag is enabled
11630             * @type Roo.tree.TreeDragZone
11631             */
11632             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11633                ddGroup: this.ddGroup || "TreeDD",
11634                scroll: this.ddScroll
11635            });
11636         }
11637         this.getSelectionModel().init(this);
11638         if (!this.root) {
11639             Roo.log("ROOT not set in tree");
11640             return this;
11641         }
11642         this.root.render();
11643         if(!this.rootVisible){
11644             this.root.renderChildren();
11645         }
11646         return this;
11647     }
11648 });/*
11649  * Based on:
11650  * Ext JS Library 1.1.1
11651  * Copyright(c) 2006-2007, Ext JS, LLC.
11652  *
11653  * Originally Released Under LGPL - original licence link has changed is not relivant.
11654  *
11655  * Fork - LGPL
11656  * <script type="text/javascript">
11657  */
11658  
11659
11660 /**
11661  * @class Roo.tree.DefaultSelectionModel
11662  * @extends Roo.util.Observable
11663  * The default single selection for a TreePanel.
11664  * @param {Object} cfg Configuration
11665  */
11666 Roo.tree.DefaultSelectionModel = function(cfg){
11667    this.selNode = null;
11668    
11669    
11670    
11671    this.addEvents({
11672        /**
11673         * @event selectionchange
11674         * Fires when the selected node changes
11675         * @param {DefaultSelectionModel} this
11676         * @param {TreeNode} node the new selection
11677         */
11678        "selectionchange" : true,
11679
11680        /**
11681         * @event beforeselect
11682         * Fires before the selected node changes, return false to cancel the change
11683         * @param {DefaultSelectionModel} this
11684         * @param {TreeNode} node the new selection
11685         * @param {TreeNode} node the old selection
11686         */
11687        "beforeselect" : true
11688    });
11689    
11690     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11691 };
11692
11693 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11694     init : function(tree){
11695         this.tree = tree;
11696         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11697         tree.on("click", this.onNodeClick, this);
11698     },
11699     
11700     onNodeClick : function(node, e){
11701         if (e.ctrlKey && this.selNode == node)  {
11702             this.unselect(node);
11703             return;
11704         }
11705         this.select(node);
11706     },
11707     
11708     /**
11709      * Select a node.
11710      * @param {TreeNode} node The node to select
11711      * @return {TreeNode} The selected node
11712      */
11713     select : function(node){
11714         var last = this.selNode;
11715         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11716             if(last){
11717                 last.ui.onSelectedChange(false);
11718             }
11719             this.selNode = node;
11720             node.ui.onSelectedChange(true);
11721             this.fireEvent("selectionchange", this, node, last);
11722         }
11723         return node;
11724     },
11725     
11726     /**
11727      * Deselect a node.
11728      * @param {TreeNode} node The node to unselect
11729      */
11730     unselect : function(node){
11731         if(this.selNode == node){
11732             this.clearSelections();
11733         }    
11734     },
11735     
11736     /**
11737      * Clear all selections
11738      */
11739     clearSelections : function(){
11740         var n = this.selNode;
11741         if(n){
11742             n.ui.onSelectedChange(false);
11743             this.selNode = null;
11744             this.fireEvent("selectionchange", this, null);
11745         }
11746         return n;
11747     },
11748     
11749     /**
11750      * Get the selected node
11751      * @return {TreeNode} The selected node
11752      */
11753     getSelectedNode : function(){
11754         return this.selNode;    
11755     },
11756     
11757     /**
11758      * Returns true if the node is selected
11759      * @param {TreeNode} node The node to check
11760      * @return {Boolean}
11761      */
11762     isSelected : function(node){
11763         return this.selNode == node;  
11764     },
11765
11766     /**
11767      * Selects the node above the selected node in the tree, intelligently walking the nodes
11768      * @return TreeNode The new selection
11769      */
11770     selectPrevious : function(){
11771         var s = this.selNode || this.lastSelNode;
11772         if(!s){
11773             return null;
11774         }
11775         var ps = s.previousSibling;
11776         if(ps){
11777             if(!ps.isExpanded() || ps.childNodes.length < 1){
11778                 return this.select(ps);
11779             } else{
11780                 var lc = ps.lastChild;
11781                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11782                     lc = lc.lastChild;
11783                 }
11784                 return this.select(lc);
11785             }
11786         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11787             return this.select(s.parentNode);
11788         }
11789         return null;
11790     },
11791
11792     /**
11793      * Selects the node above the selected node in the tree, intelligently walking the nodes
11794      * @return TreeNode The new selection
11795      */
11796     selectNext : function(){
11797         var s = this.selNode || this.lastSelNode;
11798         if(!s){
11799             return null;
11800         }
11801         if(s.firstChild && s.isExpanded()){
11802              return this.select(s.firstChild);
11803          }else if(s.nextSibling){
11804              return this.select(s.nextSibling);
11805          }else if(s.parentNode){
11806             var newS = null;
11807             s.parentNode.bubble(function(){
11808                 if(this.nextSibling){
11809                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11810                     return false;
11811                 }
11812             });
11813             return newS;
11814          }
11815         return null;
11816     },
11817
11818     onKeyDown : function(e){
11819         var s = this.selNode || this.lastSelNode;
11820         // undesirable, but required
11821         var sm = this;
11822         if(!s){
11823             return;
11824         }
11825         var k = e.getKey();
11826         switch(k){
11827              case e.DOWN:
11828                  e.stopEvent();
11829                  this.selectNext();
11830              break;
11831              case e.UP:
11832                  e.stopEvent();
11833                  this.selectPrevious();
11834              break;
11835              case e.RIGHT:
11836                  e.preventDefault();
11837                  if(s.hasChildNodes()){
11838                      if(!s.isExpanded()){
11839                          s.expand();
11840                      }else if(s.firstChild){
11841                          this.select(s.firstChild, e);
11842                      }
11843                  }
11844              break;
11845              case e.LEFT:
11846                  e.preventDefault();
11847                  if(s.hasChildNodes() && s.isExpanded()){
11848                      s.collapse();
11849                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11850                      this.select(s.parentNode, e);
11851                  }
11852              break;
11853         };
11854     }
11855 });
11856
11857 /**
11858  * @class Roo.tree.MultiSelectionModel
11859  * @extends Roo.util.Observable
11860  * Multi selection for a TreePanel.
11861  * @param {Object} cfg Configuration
11862  */
11863 Roo.tree.MultiSelectionModel = function(){
11864    this.selNodes = [];
11865    this.selMap = {};
11866    this.addEvents({
11867        /**
11868         * @event selectionchange
11869         * Fires when the selected nodes change
11870         * @param {MultiSelectionModel} this
11871         * @param {Array} nodes Array of the selected nodes
11872         */
11873        "selectionchange" : true
11874    });
11875    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11876    
11877 };
11878
11879 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11880     init : function(tree){
11881         this.tree = tree;
11882         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11883         tree.on("click", this.onNodeClick, this);
11884     },
11885     
11886     onNodeClick : function(node, e){
11887         this.select(node, e, e.ctrlKey);
11888     },
11889     
11890     /**
11891      * Select a node.
11892      * @param {TreeNode} node The node to select
11893      * @param {EventObject} e (optional) An event associated with the selection
11894      * @param {Boolean} keepExisting True to retain existing selections
11895      * @return {TreeNode} The selected node
11896      */
11897     select : function(node, e, keepExisting){
11898         if(keepExisting !== true){
11899             this.clearSelections(true);
11900         }
11901         if(this.isSelected(node)){
11902             this.lastSelNode = node;
11903             return node;
11904         }
11905         this.selNodes.push(node);
11906         this.selMap[node.id] = node;
11907         this.lastSelNode = node;
11908         node.ui.onSelectedChange(true);
11909         this.fireEvent("selectionchange", this, this.selNodes);
11910         return node;
11911     },
11912     
11913     /**
11914      * Deselect a node.
11915      * @param {TreeNode} node The node to unselect
11916      */
11917     unselect : function(node){
11918         if(this.selMap[node.id]){
11919             node.ui.onSelectedChange(false);
11920             var sn = this.selNodes;
11921             var index = -1;
11922             if(sn.indexOf){
11923                 index = sn.indexOf(node);
11924             }else{
11925                 for(var i = 0, len = sn.length; i < len; i++){
11926                     if(sn[i] == node){
11927                         index = i;
11928                         break;
11929                     }
11930                 }
11931             }
11932             if(index != -1){
11933                 this.selNodes.splice(index, 1);
11934             }
11935             delete this.selMap[node.id];
11936             this.fireEvent("selectionchange", this, this.selNodes);
11937         }
11938     },
11939     
11940     /**
11941      * Clear all selections
11942      */
11943     clearSelections : function(suppressEvent){
11944         var sn = this.selNodes;
11945         if(sn.length > 0){
11946             for(var i = 0, len = sn.length; i < len; i++){
11947                 sn[i].ui.onSelectedChange(false);
11948             }
11949             this.selNodes = [];
11950             this.selMap = {};
11951             if(suppressEvent !== true){
11952                 this.fireEvent("selectionchange", this, this.selNodes);
11953             }
11954         }
11955     },
11956     
11957     /**
11958      * Returns true if the node is selected
11959      * @param {TreeNode} node The node to check
11960      * @return {Boolean}
11961      */
11962     isSelected : function(node){
11963         return this.selMap[node.id] ? true : false;  
11964     },
11965     
11966     /**
11967      * Returns an array of the selected nodes
11968      * @return {Array}
11969      */
11970     getSelectedNodes : function(){
11971         return this.selNodes;    
11972     },
11973
11974     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
11975
11976     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
11977
11978     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
11979 });/*
11980  * Based on:
11981  * Ext JS Library 1.1.1
11982  * Copyright(c) 2006-2007, Ext JS, LLC.
11983  *
11984  * Originally Released Under LGPL - original licence link has changed is not relivant.
11985  *
11986  * Fork - LGPL
11987  * <script type="text/javascript">
11988  */
11989  
11990 /**
11991  * @class Roo.tree.TreeNode
11992  * @extends Roo.data.Node
11993  * @cfg {String} text The text for this node
11994  * @cfg {Boolean} expanded true to start the node expanded
11995  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
11996  * @cfg {Boolean} allowDrop false if this node cannot be drop on
11997  * @cfg {Boolean} disabled true to start the node disabled
11998  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
11999  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12000  * @cfg {String} cls A css class to be added to the node
12001  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12002  * @cfg {String} href URL of the link used for the node (defaults to #)
12003  * @cfg {String} hrefTarget target frame for the link
12004  * @cfg {String} qtip An Ext QuickTip for the node
12005  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12006  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12007  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12008  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12009  * (defaults to undefined with no checkbox rendered)
12010  * @constructor
12011  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12012  */
12013 Roo.tree.TreeNode = function(attributes){
12014     attributes = attributes || {};
12015     if(typeof attributes == "string"){
12016         attributes = {text: attributes};
12017     }
12018     this.childrenRendered = false;
12019     this.rendered = false;
12020     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12021     this.expanded = attributes.expanded === true;
12022     this.isTarget = attributes.isTarget !== false;
12023     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12024     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12025
12026     /**
12027      * Read-only. The text for this node. To change it use setText().
12028      * @type String
12029      */
12030     this.text = attributes.text;
12031     /**
12032      * True if this node is disabled.
12033      * @type Boolean
12034      */
12035     this.disabled = attributes.disabled === true;
12036
12037     this.addEvents({
12038         /**
12039         * @event textchange
12040         * Fires when the text for this node is changed
12041         * @param {Node} this This node
12042         * @param {String} text The new text
12043         * @param {String} oldText The old text
12044         */
12045         "textchange" : true,
12046         /**
12047         * @event beforeexpand
12048         * Fires before this node is expanded, return false to cancel.
12049         * @param {Node} this This node
12050         * @param {Boolean} deep
12051         * @param {Boolean} anim
12052         */
12053         "beforeexpand" : true,
12054         /**
12055         * @event beforecollapse
12056         * Fires before this node is collapsed, return false to cancel.
12057         * @param {Node} this This node
12058         * @param {Boolean} deep
12059         * @param {Boolean} anim
12060         */
12061         "beforecollapse" : true,
12062         /**
12063         * @event expand
12064         * Fires when this node is expanded
12065         * @param {Node} this This node
12066         */
12067         "expand" : true,
12068         /**
12069         * @event disabledchange
12070         * Fires when the disabled status of this node changes
12071         * @param {Node} this This node
12072         * @param {Boolean} disabled
12073         */
12074         "disabledchange" : true,
12075         /**
12076         * @event collapse
12077         * Fires when this node is collapsed
12078         * @param {Node} this This node
12079         */
12080         "collapse" : true,
12081         /**
12082         * @event beforeclick
12083         * Fires before click processing. Return false to cancel the default action.
12084         * @param {Node} this This node
12085         * @param {Roo.EventObject} e The event object
12086         */
12087         "beforeclick":true,
12088         /**
12089         * @event checkchange
12090         * Fires when a node with a checkbox's checked property changes
12091         * @param {Node} this This node
12092         * @param {Boolean} checked
12093         */
12094         "checkchange":true,
12095         /**
12096         * @event click
12097         * Fires when this node is clicked
12098         * @param {Node} this This node
12099         * @param {Roo.EventObject} e The event object
12100         */
12101         "click":true,
12102         /**
12103         * @event dblclick
12104         * Fires when this node is double clicked
12105         * @param {Node} this This node
12106         * @param {Roo.EventObject} e The event object
12107         */
12108         "dblclick":true,
12109         /**
12110         * @event contextmenu
12111         * Fires when this node is right clicked
12112         * @param {Node} this This node
12113         * @param {Roo.EventObject} e The event object
12114         */
12115         "contextmenu":true,
12116         /**
12117         * @event beforechildrenrendered
12118         * Fires right before the child nodes for this node are rendered
12119         * @param {Node} this This node
12120         */
12121         "beforechildrenrendered":true
12122     });
12123
12124     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12125
12126     /**
12127      * Read-only. The UI for this node
12128      * @type TreeNodeUI
12129      */
12130     this.ui = new uiClass(this);
12131     
12132     // finally support items[]
12133     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12134         return;
12135     }
12136     
12137     
12138     Roo.each(this.attributes.items, function(c) {
12139         this.appendChild(Roo.factory(c,Roo.Tree));
12140     }, this);
12141     delete this.attributes.items;
12142     
12143     
12144     
12145 };
12146 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12147     preventHScroll: true,
12148     /**
12149      * Returns true if this node is expanded
12150      * @return {Boolean}
12151      */
12152     isExpanded : function(){
12153         return this.expanded;
12154     },
12155
12156     /**
12157      * Returns the UI object for this node
12158      * @return {TreeNodeUI}
12159      */
12160     getUI : function(){
12161         return this.ui;
12162     },
12163
12164     // private override
12165     setFirstChild : function(node){
12166         var of = this.firstChild;
12167         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12168         if(this.childrenRendered && of && node != of){
12169             of.renderIndent(true, true);
12170         }
12171         if(this.rendered){
12172             this.renderIndent(true, true);
12173         }
12174     },
12175
12176     // private override
12177     setLastChild : function(node){
12178         var ol = this.lastChild;
12179         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12180         if(this.childrenRendered && ol && node != ol){
12181             ol.renderIndent(true, true);
12182         }
12183         if(this.rendered){
12184             this.renderIndent(true, true);
12185         }
12186     },
12187
12188     // these methods are overridden to provide lazy rendering support
12189     // private override
12190     appendChild : function()
12191     {
12192         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12193         if(node && this.childrenRendered){
12194             node.render();
12195         }
12196         this.ui.updateExpandIcon();
12197         return node;
12198     },
12199
12200     // private override
12201     removeChild : function(node){
12202         this.ownerTree.getSelectionModel().unselect(node);
12203         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12204         // if it's been rendered remove dom node
12205         if(this.childrenRendered){
12206             node.ui.remove();
12207         }
12208         if(this.childNodes.length < 1){
12209             this.collapse(false, false);
12210         }else{
12211             this.ui.updateExpandIcon();
12212         }
12213         if(!this.firstChild) {
12214             this.childrenRendered = false;
12215         }
12216         return node;
12217     },
12218
12219     // private override
12220     insertBefore : function(node, refNode){
12221         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12222         if(newNode && refNode && this.childrenRendered){
12223             node.render();
12224         }
12225         this.ui.updateExpandIcon();
12226         return newNode;
12227     },
12228
12229     /**
12230      * Sets the text for this node
12231      * @param {String} text
12232      */
12233     setText : function(text){
12234         var oldText = this.text;
12235         this.text = text;
12236         this.attributes.text = text;
12237         if(this.rendered){ // event without subscribing
12238             this.ui.onTextChange(this, text, oldText);
12239         }
12240         this.fireEvent("textchange", this, text, oldText);
12241     },
12242
12243     /**
12244      * Triggers selection of this node
12245      */
12246     select : function(){
12247         this.getOwnerTree().getSelectionModel().select(this);
12248     },
12249
12250     /**
12251      * Triggers deselection of this node
12252      */
12253     unselect : function(){
12254         this.getOwnerTree().getSelectionModel().unselect(this);
12255     },
12256
12257     /**
12258      * Returns true if this node is selected
12259      * @return {Boolean}
12260      */
12261     isSelected : function(){
12262         return this.getOwnerTree().getSelectionModel().isSelected(this);
12263     },
12264
12265     /**
12266      * Expand this node.
12267      * @param {Boolean} deep (optional) True to expand all children as well
12268      * @param {Boolean} anim (optional) false to cancel the default animation
12269      * @param {Function} callback (optional) A callback to be called when
12270      * expanding this node completes (does not wait for deep expand to complete).
12271      * Called with 1 parameter, this node.
12272      */
12273     expand : function(deep, anim, callback){
12274         if(!this.expanded){
12275             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12276                 return;
12277             }
12278             if(!this.childrenRendered){
12279                 this.renderChildren();
12280             }
12281             this.expanded = true;
12282             
12283             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12284                 this.ui.animExpand(function(){
12285                     this.fireEvent("expand", this);
12286                     if(typeof callback == "function"){
12287                         callback(this);
12288                     }
12289                     if(deep === true){
12290                         this.expandChildNodes(true);
12291                     }
12292                 }.createDelegate(this));
12293                 return;
12294             }else{
12295                 this.ui.expand();
12296                 this.fireEvent("expand", this);
12297                 if(typeof callback == "function"){
12298                     callback(this);
12299                 }
12300             }
12301         }else{
12302            if(typeof callback == "function"){
12303                callback(this);
12304            }
12305         }
12306         if(deep === true){
12307             this.expandChildNodes(true);
12308         }
12309     },
12310
12311     isHiddenRoot : function(){
12312         return this.isRoot && !this.getOwnerTree().rootVisible;
12313     },
12314
12315     /**
12316      * Collapse this node.
12317      * @param {Boolean} deep (optional) True to collapse all children as well
12318      * @param {Boolean} anim (optional) false to cancel the default animation
12319      */
12320     collapse : function(deep, anim){
12321         if(this.expanded && !this.isHiddenRoot()){
12322             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12323                 return;
12324             }
12325             this.expanded = false;
12326             if((this.getOwnerTree().animate && anim !== false) || anim){
12327                 this.ui.animCollapse(function(){
12328                     this.fireEvent("collapse", this);
12329                     if(deep === true){
12330                         this.collapseChildNodes(true);
12331                     }
12332                 }.createDelegate(this));
12333                 return;
12334             }else{
12335                 this.ui.collapse();
12336                 this.fireEvent("collapse", this);
12337             }
12338         }
12339         if(deep === true){
12340             var cs = this.childNodes;
12341             for(var i = 0, len = cs.length; i < len; i++) {
12342                 cs[i].collapse(true, false);
12343             }
12344         }
12345     },
12346
12347     // private
12348     delayedExpand : function(delay){
12349         if(!this.expandProcId){
12350             this.expandProcId = this.expand.defer(delay, this);
12351         }
12352     },
12353
12354     // private
12355     cancelExpand : function(){
12356         if(this.expandProcId){
12357             clearTimeout(this.expandProcId);
12358         }
12359         this.expandProcId = false;
12360     },
12361
12362     /**
12363      * Toggles expanded/collapsed state of the node
12364      */
12365     toggle : function(){
12366         if(this.expanded){
12367             this.collapse();
12368         }else{
12369             this.expand();
12370         }
12371     },
12372
12373     /**
12374      * Ensures all parent nodes are expanded
12375      */
12376     ensureVisible : function(callback){
12377         var tree = this.getOwnerTree();
12378         tree.expandPath(this.parentNode.getPath(), false, function(){
12379             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12380             Roo.callback(callback);
12381         }.createDelegate(this));
12382     },
12383
12384     /**
12385      * Expand all child nodes
12386      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12387      */
12388     expandChildNodes : function(deep){
12389         var cs = this.childNodes;
12390         for(var i = 0, len = cs.length; i < len; i++) {
12391                 cs[i].expand(deep);
12392         }
12393     },
12394
12395     /**
12396      * Collapse all child nodes
12397      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12398      */
12399     collapseChildNodes : function(deep){
12400         var cs = this.childNodes;
12401         for(var i = 0, len = cs.length; i < len; i++) {
12402                 cs[i].collapse(deep);
12403         }
12404     },
12405
12406     /**
12407      * Disables this node
12408      */
12409     disable : function(){
12410         this.disabled = true;
12411         this.unselect();
12412         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12413             this.ui.onDisableChange(this, true);
12414         }
12415         this.fireEvent("disabledchange", this, true);
12416     },
12417
12418     /**
12419      * Enables this node
12420      */
12421     enable : function(){
12422         this.disabled = false;
12423         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12424             this.ui.onDisableChange(this, false);
12425         }
12426         this.fireEvent("disabledchange", this, false);
12427     },
12428
12429     // private
12430     renderChildren : function(suppressEvent){
12431         if(suppressEvent !== false){
12432             this.fireEvent("beforechildrenrendered", this);
12433         }
12434         var cs = this.childNodes;
12435         for(var i = 0, len = cs.length; i < len; i++){
12436             cs[i].render(true);
12437         }
12438         this.childrenRendered = true;
12439     },
12440
12441     // private
12442     sort : function(fn, scope){
12443         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12444         if(this.childrenRendered){
12445             var cs = this.childNodes;
12446             for(var i = 0, len = cs.length; i < len; i++){
12447                 cs[i].render(true);
12448             }
12449         }
12450     },
12451
12452     // private
12453     render : function(bulkRender){
12454         this.ui.render(bulkRender);
12455         if(!this.rendered){
12456             this.rendered = true;
12457             if(this.expanded){
12458                 this.expanded = false;
12459                 this.expand(false, false);
12460             }
12461         }
12462     },
12463
12464     // private
12465     renderIndent : function(deep, refresh){
12466         if(refresh){
12467             this.ui.childIndent = null;
12468         }
12469         this.ui.renderIndent();
12470         if(deep === true && this.childrenRendered){
12471             var cs = this.childNodes;
12472             for(var i = 0, len = cs.length; i < len; i++){
12473                 cs[i].renderIndent(true, refresh);
12474             }
12475         }
12476     }
12477 });/*
12478  * Based on:
12479  * Ext JS Library 1.1.1
12480  * Copyright(c) 2006-2007, Ext JS, LLC.
12481  *
12482  * Originally Released Under LGPL - original licence link has changed is not relivant.
12483  *
12484  * Fork - LGPL
12485  * <script type="text/javascript">
12486  */
12487  
12488 /**
12489  * @class Roo.tree.AsyncTreeNode
12490  * @extends Roo.tree.TreeNode
12491  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12492  * @constructor
12493  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12494  */
12495  Roo.tree.AsyncTreeNode = function(config){
12496     this.loaded = false;
12497     this.loading = false;
12498     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12499     /**
12500     * @event beforeload
12501     * Fires before this node is loaded, return false to cancel
12502     * @param {Node} this This node
12503     */
12504     this.addEvents({'beforeload':true, 'load': true});
12505     /**
12506     * @event load
12507     * Fires when this node is loaded
12508     * @param {Node} this This node
12509     */
12510     /**
12511      * The loader used by this node (defaults to using the tree's defined loader)
12512      * @type TreeLoader
12513      * @property loader
12514      */
12515 };
12516 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12517     expand : function(deep, anim, callback){
12518         if(this.loading){ // if an async load is already running, waiting til it's done
12519             var timer;
12520             var f = function(){
12521                 if(!this.loading){ // done loading
12522                     clearInterval(timer);
12523                     this.expand(deep, anim, callback);
12524                 }
12525             }.createDelegate(this);
12526             timer = setInterval(f, 200);
12527             return;
12528         }
12529         if(!this.loaded){
12530             if(this.fireEvent("beforeload", this) === false){
12531                 return;
12532             }
12533             this.loading = true;
12534             this.ui.beforeLoad(this);
12535             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12536             if(loader){
12537                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12538                 return;
12539             }
12540         }
12541         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12542     },
12543     
12544     /**
12545      * Returns true if this node is currently loading
12546      * @return {Boolean}
12547      */
12548     isLoading : function(){
12549         return this.loading;  
12550     },
12551     
12552     loadComplete : function(deep, anim, callback){
12553         this.loading = false;
12554         this.loaded = true;
12555         this.ui.afterLoad(this);
12556         this.fireEvent("load", this);
12557         this.expand(deep, anim, callback);
12558     },
12559     
12560     /**
12561      * Returns true if this node has been loaded
12562      * @return {Boolean}
12563      */
12564     isLoaded : function(){
12565         return this.loaded;
12566     },
12567     
12568     hasChildNodes : function(){
12569         if(!this.isLeaf() && !this.loaded){
12570             return true;
12571         }else{
12572             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12573         }
12574     },
12575
12576     /**
12577      * Trigger a reload for this node
12578      * @param {Function} callback
12579      */
12580     reload : function(callback){
12581         this.collapse(false, false);
12582         while(this.firstChild){
12583             this.removeChild(this.firstChild);
12584         }
12585         this.childrenRendered = false;
12586         this.loaded = false;
12587         if(this.isHiddenRoot()){
12588             this.expanded = false;
12589         }
12590         this.expand(false, false, callback);
12591     }
12592 });/*
12593  * Based on:
12594  * Ext JS Library 1.1.1
12595  * Copyright(c) 2006-2007, Ext JS, LLC.
12596  *
12597  * Originally Released Under LGPL - original licence link has changed is not relivant.
12598  *
12599  * Fork - LGPL
12600  * <script type="text/javascript">
12601  */
12602  
12603 /**
12604  * @class Roo.tree.TreeNodeUI
12605  * @constructor
12606  * @param {Object} node The node to render
12607  * The TreeNode UI implementation is separate from the
12608  * tree implementation. Unless you are customizing the tree UI,
12609  * you should never have to use this directly.
12610  */
12611 Roo.tree.TreeNodeUI = function(node){
12612     this.node = node;
12613     this.rendered = false;
12614     this.animating = false;
12615     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12616 };
12617
12618 Roo.tree.TreeNodeUI.prototype = {
12619     removeChild : function(node){
12620         if(this.rendered){
12621             this.ctNode.removeChild(node.ui.getEl());
12622         }
12623     },
12624
12625     beforeLoad : function(){
12626          this.addClass("x-tree-node-loading");
12627     },
12628
12629     afterLoad : function(){
12630          this.removeClass("x-tree-node-loading");
12631     },
12632
12633     onTextChange : function(node, text, oldText){
12634         if(this.rendered){
12635             this.textNode.innerHTML = text;
12636         }
12637     },
12638
12639     onDisableChange : function(node, state){
12640         this.disabled = state;
12641         if(state){
12642             this.addClass("x-tree-node-disabled");
12643         }else{
12644             this.removeClass("x-tree-node-disabled");
12645         }
12646     },
12647
12648     onSelectedChange : function(state){
12649         if(state){
12650             this.focus();
12651             this.addClass("x-tree-selected");
12652         }else{
12653             //this.blur();
12654             this.removeClass("x-tree-selected");
12655         }
12656     },
12657
12658     onMove : function(tree, node, oldParent, newParent, index, refNode){
12659         this.childIndent = null;
12660         if(this.rendered){
12661             var targetNode = newParent.ui.getContainer();
12662             if(!targetNode){//target not rendered
12663                 this.holder = document.createElement("div");
12664                 this.holder.appendChild(this.wrap);
12665                 return;
12666             }
12667             var insertBefore = refNode ? refNode.ui.getEl() : null;
12668             if(insertBefore){
12669                 targetNode.insertBefore(this.wrap, insertBefore);
12670             }else{
12671                 targetNode.appendChild(this.wrap);
12672             }
12673             this.node.renderIndent(true);
12674         }
12675     },
12676
12677     addClass : function(cls){
12678         if(this.elNode){
12679             Roo.fly(this.elNode).addClass(cls);
12680         }
12681     },
12682
12683     removeClass : function(cls){
12684         if(this.elNode){
12685             Roo.fly(this.elNode).removeClass(cls);
12686         }
12687     },
12688
12689     remove : function(){
12690         if(this.rendered){
12691             this.holder = document.createElement("div");
12692             this.holder.appendChild(this.wrap);
12693         }
12694     },
12695
12696     fireEvent : function(){
12697         return this.node.fireEvent.apply(this.node, arguments);
12698     },
12699
12700     initEvents : function(){
12701         this.node.on("move", this.onMove, this);
12702         var E = Roo.EventManager;
12703         var a = this.anchor;
12704
12705         var el = Roo.fly(a, '_treeui');
12706
12707         if(Roo.isOpera){ // opera render bug ignores the CSS
12708             el.setStyle("text-decoration", "none");
12709         }
12710
12711         el.on("click", this.onClick, this);
12712         el.on("dblclick", this.onDblClick, this);
12713
12714         if(this.checkbox){
12715             Roo.EventManager.on(this.checkbox,
12716                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12717         }
12718
12719         el.on("contextmenu", this.onContextMenu, this);
12720
12721         var icon = Roo.fly(this.iconNode);
12722         icon.on("click", this.onClick, this);
12723         icon.on("dblclick", this.onDblClick, this);
12724         icon.on("contextmenu", this.onContextMenu, this);
12725         E.on(this.ecNode, "click", this.ecClick, this, true);
12726
12727         if(this.node.disabled){
12728             this.addClass("x-tree-node-disabled");
12729         }
12730         if(this.node.hidden){
12731             this.addClass("x-tree-node-disabled");
12732         }
12733         var ot = this.node.getOwnerTree();
12734         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12735         if(dd && (!this.node.isRoot || ot.rootVisible)){
12736             Roo.dd.Registry.register(this.elNode, {
12737                 node: this.node,
12738                 handles: this.getDDHandles(),
12739                 isHandle: false
12740             });
12741         }
12742     },
12743
12744     getDDHandles : function(){
12745         return [this.iconNode, this.textNode];
12746     },
12747
12748     hide : function(){
12749         if(this.rendered){
12750             this.wrap.style.display = "none";
12751         }
12752     },
12753
12754     show : function(){
12755         if(this.rendered){
12756             this.wrap.style.display = "";
12757         }
12758     },
12759
12760     onContextMenu : function(e){
12761         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12762             e.preventDefault();
12763             this.focus();
12764             this.fireEvent("contextmenu", this.node, e);
12765         }
12766     },
12767
12768     onClick : function(e){
12769         if(this.dropping){
12770             e.stopEvent();
12771             return;
12772         }
12773         if(this.fireEvent("beforeclick", this.node, e) !== false){
12774             if(!this.disabled && this.node.attributes.href){
12775                 this.fireEvent("click", this.node, e);
12776                 return;
12777             }
12778             e.preventDefault();
12779             if(this.disabled){
12780                 return;
12781             }
12782
12783             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12784                 this.node.toggle();
12785             }
12786
12787             this.fireEvent("click", this.node, e);
12788         }else{
12789             e.stopEvent();
12790         }
12791     },
12792
12793     onDblClick : function(e){
12794         e.preventDefault();
12795         if(this.disabled){
12796             return;
12797         }
12798         if(this.checkbox){
12799             this.toggleCheck();
12800         }
12801         if(!this.animating && this.node.hasChildNodes()){
12802             this.node.toggle();
12803         }
12804         this.fireEvent("dblclick", this.node, e);
12805     },
12806
12807     onCheckChange : function(){
12808         var checked = this.checkbox.checked;
12809         this.node.attributes.checked = checked;
12810         this.fireEvent('checkchange', this.node, checked);
12811     },
12812
12813     ecClick : function(e){
12814         if(!this.animating && this.node.hasChildNodes()){
12815             this.node.toggle();
12816         }
12817     },
12818
12819     startDrop : function(){
12820         this.dropping = true;
12821     },
12822
12823     // delayed drop so the click event doesn't get fired on a drop
12824     endDrop : function(){
12825        setTimeout(function(){
12826            this.dropping = false;
12827        }.createDelegate(this), 50);
12828     },
12829
12830     expand : function(){
12831         this.updateExpandIcon();
12832         this.ctNode.style.display = "";
12833     },
12834
12835     focus : function(){
12836         if(!this.node.preventHScroll){
12837             try{this.anchor.focus();
12838             }catch(e){}
12839         }else if(!Roo.isIE){
12840             try{
12841                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12842                 var l = noscroll.scrollLeft;
12843                 this.anchor.focus();
12844                 noscroll.scrollLeft = l;
12845             }catch(e){}
12846         }
12847     },
12848
12849     toggleCheck : function(value){
12850         var cb = this.checkbox;
12851         if(cb){
12852             cb.checked = (value === undefined ? !cb.checked : value);
12853         }
12854     },
12855
12856     blur : function(){
12857         try{
12858             this.anchor.blur();
12859         }catch(e){}
12860     },
12861
12862     animExpand : function(callback){
12863         var ct = Roo.get(this.ctNode);
12864         ct.stopFx();
12865         if(!this.node.hasChildNodes()){
12866             this.updateExpandIcon();
12867             this.ctNode.style.display = "";
12868             Roo.callback(callback);
12869             return;
12870         }
12871         this.animating = true;
12872         this.updateExpandIcon();
12873
12874         ct.slideIn('t', {
12875            callback : function(){
12876                this.animating = false;
12877                Roo.callback(callback);
12878             },
12879             scope: this,
12880             duration: this.node.ownerTree.duration || .25
12881         });
12882     },
12883
12884     highlight : function(){
12885         var tree = this.node.getOwnerTree();
12886         Roo.fly(this.wrap).highlight(
12887             tree.hlColor || "C3DAF9",
12888             {endColor: tree.hlBaseColor}
12889         );
12890     },
12891
12892     collapse : function(){
12893         this.updateExpandIcon();
12894         this.ctNode.style.display = "none";
12895     },
12896
12897     animCollapse : function(callback){
12898         var ct = Roo.get(this.ctNode);
12899         ct.enableDisplayMode('block');
12900         ct.stopFx();
12901
12902         this.animating = true;
12903         this.updateExpandIcon();
12904
12905         ct.slideOut('t', {
12906             callback : function(){
12907                this.animating = false;
12908                Roo.callback(callback);
12909             },
12910             scope: this,
12911             duration: this.node.ownerTree.duration || .25
12912         });
12913     },
12914
12915     getContainer : function(){
12916         return this.ctNode;
12917     },
12918
12919     getEl : function(){
12920         return this.wrap;
12921     },
12922
12923     appendDDGhost : function(ghostNode){
12924         ghostNode.appendChild(this.elNode.cloneNode(true));
12925     },
12926
12927     getDDRepairXY : function(){
12928         return Roo.lib.Dom.getXY(this.iconNode);
12929     },
12930
12931     onRender : function(){
12932         this.render();
12933     },
12934
12935     render : function(bulkRender){
12936         var n = this.node, a = n.attributes;
12937         var targetNode = n.parentNode ?
12938               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12939
12940         if(!this.rendered){
12941             this.rendered = true;
12942
12943             this.renderElements(n, a, targetNode, bulkRender);
12944
12945             if(a.qtip){
12946                if(this.textNode.setAttributeNS){
12947                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
12948                    if(a.qtipTitle){
12949                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
12950                    }
12951                }else{
12952                    this.textNode.setAttribute("ext:qtip", a.qtip);
12953                    if(a.qtipTitle){
12954                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
12955                    }
12956                }
12957             }else if(a.qtipCfg){
12958                 a.qtipCfg.target = Roo.id(this.textNode);
12959                 Roo.QuickTips.register(a.qtipCfg);
12960             }
12961             this.initEvents();
12962             if(!this.node.expanded){
12963                 this.updateExpandIcon();
12964             }
12965         }else{
12966             if(bulkRender === true) {
12967                 targetNode.appendChild(this.wrap);
12968             }
12969         }
12970     },
12971
12972     renderElements : function(n, a, targetNode, bulkRender)
12973     {
12974         // add some indent caching, this helps performance when rendering a large tree
12975         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
12976         var t = n.getOwnerTree();
12977         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
12978         if (typeof(n.attributes.html) != 'undefined') {
12979             txt = n.attributes.html;
12980         }
12981         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
12982         var cb = typeof a.checked == 'boolean';
12983         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
12984         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
12985             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
12986             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
12987             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
12988             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
12989             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
12990              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
12991                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
12992             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
12993             "</li>"];
12994
12995         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
12996             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
12997                                 n.nextSibling.ui.getEl(), buf.join(""));
12998         }else{
12999             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13000         }
13001
13002         this.elNode = this.wrap.childNodes[0];
13003         this.ctNode = this.wrap.childNodes[1];
13004         var cs = this.elNode.childNodes;
13005         this.indentNode = cs[0];
13006         this.ecNode = cs[1];
13007         this.iconNode = cs[2];
13008         var index = 3;
13009         if(cb){
13010             this.checkbox = cs[3];
13011             index++;
13012         }
13013         this.anchor = cs[index];
13014         this.textNode = cs[index].firstChild;
13015     },
13016
13017     getAnchor : function(){
13018         return this.anchor;
13019     },
13020
13021     getTextEl : function(){
13022         return this.textNode;
13023     },
13024
13025     getIconEl : function(){
13026         return this.iconNode;
13027     },
13028
13029     isChecked : function(){
13030         return this.checkbox ? this.checkbox.checked : false;
13031     },
13032
13033     updateExpandIcon : function(){
13034         if(this.rendered){
13035             var n = this.node, c1, c2;
13036             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13037             var hasChild = n.hasChildNodes();
13038             if(hasChild){
13039                 if(n.expanded){
13040                     cls += "-minus";
13041                     c1 = "x-tree-node-collapsed";
13042                     c2 = "x-tree-node-expanded";
13043                 }else{
13044                     cls += "-plus";
13045                     c1 = "x-tree-node-expanded";
13046                     c2 = "x-tree-node-collapsed";
13047                 }
13048                 if(this.wasLeaf){
13049                     this.removeClass("x-tree-node-leaf");
13050                     this.wasLeaf = false;
13051                 }
13052                 if(this.c1 != c1 || this.c2 != c2){
13053                     Roo.fly(this.elNode).replaceClass(c1, c2);
13054                     this.c1 = c1; this.c2 = c2;
13055                 }
13056             }else{
13057                 // this changes non-leafs into leafs if they have no children.
13058                 // it's not very rational behaviour..
13059                 
13060                 if(!this.wasLeaf && this.node.leaf){
13061                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13062                     delete this.c1;
13063                     delete this.c2;
13064                     this.wasLeaf = true;
13065                 }
13066             }
13067             var ecc = "x-tree-ec-icon "+cls;
13068             if(this.ecc != ecc){
13069                 this.ecNode.className = ecc;
13070                 this.ecc = ecc;
13071             }
13072         }
13073     },
13074
13075     getChildIndent : function(){
13076         if(!this.childIndent){
13077             var buf = [];
13078             var p = this.node;
13079             while(p){
13080                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13081                     if(!p.isLast()) {
13082                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13083                     } else {
13084                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13085                     }
13086                 }
13087                 p = p.parentNode;
13088             }
13089             this.childIndent = buf.join("");
13090         }
13091         return this.childIndent;
13092     },
13093
13094     renderIndent : function(){
13095         if(this.rendered){
13096             var indent = "";
13097             var p = this.node.parentNode;
13098             if(p){
13099                 indent = p.ui.getChildIndent();
13100             }
13101             if(this.indentMarkup != indent){ // don't rerender if not required
13102                 this.indentNode.innerHTML = indent;
13103                 this.indentMarkup = indent;
13104             }
13105             this.updateExpandIcon();
13106         }
13107     }
13108 };
13109
13110 Roo.tree.RootTreeNodeUI = function(){
13111     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13112 };
13113 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13114     render : function(){
13115         if(!this.rendered){
13116             var targetNode = this.node.ownerTree.innerCt.dom;
13117             this.node.expanded = true;
13118             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13119             this.wrap = this.ctNode = targetNode.firstChild;
13120         }
13121     },
13122     collapse : function(){
13123     },
13124     expand : function(){
13125     }
13126 });/*
13127  * Based on:
13128  * Ext JS Library 1.1.1
13129  * Copyright(c) 2006-2007, Ext JS, LLC.
13130  *
13131  * Originally Released Under LGPL - original licence link has changed is not relivant.
13132  *
13133  * Fork - LGPL
13134  * <script type="text/javascript">
13135  */
13136 /**
13137  * @class Roo.tree.TreeLoader
13138  * @extends Roo.util.Observable
13139  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13140  * nodes from a specified URL. The response must be a javascript Array definition
13141  * who's elements are node definition objects. eg:
13142  * <pre><code>
13143 {  success : true,
13144    data :      [
13145    
13146     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13147     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13148     ]
13149 }
13150
13151
13152 </code></pre>
13153  * <br><br>
13154  * The old style respose with just an array is still supported, but not recommended.
13155  * <br><br>
13156  *
13157  * A server request is sent, and child nodes are loaded only when a node is expanded.
13158  * The loading node's id is passed to the server under the parameter name "node" to
13159  * enable the server to produce the correct child nodes.
13160  * <br><br>
13161  * To pass extra parameters, an event handler may be attached to the "beforeload"
13162  * event, and the parameters specified in the TreeLoader's baseParams property:
13163  * <pre><code>
13164     myTreeLoader.on("beforeload", function(treeLoader, node) {
13165         this.baseParams.category = node.attributes.category;
13166     }, this);
13167     
13168 </code></pre>
13169  *
13170  * This would pass an HTTP parameter called "category" to the server containing
13171  * the value of the Node's "category" attribute.
13172  * @constructor
13173  * Creates a new Treeloader.
13174  * @param {Object} config A config object containing config properties.
13175  */
13176 Roo.tree.TreeLoader = function(config){
13177     this.baseParams = {};
13178     this.requestMethod = "POST";
13179     Roo.apply(this, config);
13180
13181     this.addEvents({
13182     
13183         /**
13184          * @event beforeload
13185          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13186          * @param {Object} This TreeLoader object.
13187          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13188          * @param {Object} callback The callback function specified in the {@link #load} call.
13189          */
13190         beforeload : true,
13191         /**
13192          * @event load
13193          * Fires when the node has been successfuly loaded.
13194          * @param {Object} This TreeLoader object.
13195          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13196          * @param {Object} response The response object containing the data from the server.
13197          */
13198         load : true,
13199         /**
13200          * @event loadexception
13201          * Fires if the network request failed.
13202          * @param {Object} This TreeLoader object.
13203          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13204          * @param {Object} response The response object containing the data from the server.
13205          */
13206         loadexception : true,
13207         /**
13208          * @event create
13209          * Fires before a node is created, enabling you to return custom Node types 
13210          * @param {Object} This TreeLoader object.
13211          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13212          */
13213         create : true
13214     });
13215
13216     Roo.tree.TreeLoader.superclass.constructor.call(this);
13217 };
13218
13219 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13220     /**
13221     * @cfg {String} dataUrl The URL from which to request a Json string which
13222     * specifies an array of node definition object representing the child nodes
13223     * to be loaded.
13224     */
13225     /**
13226     * @cfg {String} requestMethod either GET or POST
13227     * defaults to POST (due to BC)
13228     * to be loaded.
13229     */
13230     /**
13231     * @cfg {Object} baseParams (optional) An object containing properties which
13232     * specify HTTP parameters to be passed to each request for child nodes.
13233     */
13234     /**
13235     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13236     * created by this loader. If the attributes sent by the server have an attribute in this object,
13237     * they take priority.
13238     */
13239     /**
13240     * @cfg {Object} uiProviders (optional) An object containing properties which
13241     * 
13242     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13243     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13244     * <i>uiProvider</i> attribute of a returned child node is a string rather
13245     * than a reference to a TreeNodeUI implementation, this that string value
13246     * is used as a property name in the uiProviders object. You can define the provider named
13247     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13248     */
13249     uiProviders : {},
13250
13251     /**
13252     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13253     * child nodes before loading.
13254     */
13255     clearOnLoad : true,
13256
13257     /**
13258     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13259     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13260     * Grid query { data : [ .....] }
13261     */
13262     
13263     root : false,
13264      /**
13265     * @cfg {String} queryParam (optional) 
13266     * Name of the query as it will be passed on the querystring (defaults to 'node')
13267     * eg. the request will be ?node=[id]
13268     */
13269     
13270     
13271     queryParam: false,
13272     
13273     /**
13274      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13275      * This is called automatically when a node is expanded, but may be used to reload
13276      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13277      * @param {Roo.tree.TreeNode} node
13278      * @param {Function} callback
13279      */
13280     load : function(node, callback){
13281         if(this.clearOnLoad){
13282             while(node.firstChild){
13283                 node.removeChild(node.firstChild);
13284             }
13285         }
13286         if(node.attributes.children){ // preloaded json children
13287             var cs = node.attributes.children;
13288             for(var i = 0, len = cs.length; i < len; i++){
13289                 node.appendChild(this.createNode(cs[i]));
13290             }
13291             if(typeof callback == "function"){
13292                 callback();
13293             }
13294         }else if(this.dataUrl){
13295             this.requestData(node, callback);
13296         }
13297     },
13298
13299     getParams: function(node){
13300         var buf = [], bp = this.baseParams;
13301         for(var key in bp){
13302             if(typeof bp[key] != "function"){
13303                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13304             }
13305         }
13306         var n = this.queryParam === false ? 'node' : this.queryParam;
13307         buf.push(n + "=", encodeURIComponent(node.id));
13308         return buf.join("");
13309     },
13310
13311     requestData : function(node, callback){
13312         if(this.fireEvent("beforeload", this, node, callback) !== false){
13313             this.transId = Roo.Ajax.request({
13314                 method:this.requestMethod,
13315                 url: this.dataUrl||this.url,
13316                 success: this.handleResponse,
13317                 failure: this.handleFailure,
13318                 scope: this,
13319                 argument: {callback: callback, node: node},
13320                 params: this.getParams(node)
13321             });
13322         }else{
13323             // if the load is cancelled, make sure we notify
13324             // the node that we are done
13325             if(typeof callback == "function"){
13326                 callback();
13327             }
13328         }
13329     },
13330
13331     isLoading : function(){
13332         return this.transId ? true : false;
13333     },
13334
13335     abort : function(){
13336         if(this.isLoading()){
13337             Roo.Ajax.abort(this.transId);
13338         }
13339     },
13340
13341     // private
13342     createNode : function(attr)
13343     {
13344         // apply baseAttrs, nice idea Corey!
13345         if(this.baseAttrs){
13346             Roo.applyIf(attr, this.baseAttrs);
13347         }
13348         if(this.applyLoader !== false){
13349             attr.loader = this;
13350         }
13351         // uiProvider = depreciated..
13352         
13353         if(typeof(attr.uiProvider) == 'string'){
13354            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13355                 /**  eval:var:attr */ eval(attr.uiProvider);
13356         }
13357         if(typeof(this.uiProviders['default']) != 'undefined') {
13358             attr.uiProvider = this.uiProviders['default'];
13359         }
13360         
13361         this.fireEvent('create', this, attr);
13362         
13363         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13364         return(attr.leaf ?
13365                         new Roo.tree.TreeNode(attr) :
13366                         new Roo.tree.AsyncTreeNode(attr));
13367     },
13368
13369     processResponse : function(response, node, callback)
13370     {
13371         var json = response.responseText;
13372         try {
13373             
13374             var o = Roo.decode(json);
13375             
13376             if (this.root === false && typeof(o.success) != undefined) {
13377                 this.root = 'data'; // the default behaviour for list like data..
13378                 }
13379                 
13380             if (this.root !== false &&  !o.success) {
13381                 // it's a failure condition.
13382                 var a = response.argument;
13383                 this.fireEvent("loadexception", this, a.node, response);
13384                 Roo.log("Load failed - should have a handler really");
13385                 return;
13386             }
13387             
13388             
13389             
13390             if (this.root !== false) {
13391                  o = o[this.root];
13392             }
13393             
13394             for(var i = 0, len = o.length; i < len; i++){
13395                 var n = this.createNode(o[i]);
13396                 if(n){
13397                     node.appendChild(n);
13398                 }
13399             }
13400             if(typeof callback == "function"){
13401                 callback(this, node);
13402             }
13403         }catch(e){
13404             this.handleFailure(response);
13405         }
13406     },
13407
13408     handleResponse : function(response){
13409         this.transId = false;
13410         var a = response.argument;
13411         this.processResponse(response, a.node, a.callback);
13412         this.fireEvent("load", this, a.node, response);
13413     },
13414
13415     handleFailure : function(response)
13416     {
13417         // should handle failure better..
13418         this.transId = false;
13419         var a = response.argument;
13420         this.fireEvent("loadexception", this, a.node, response);
13421         if(typeof a.callback == "function"){
13422             a.callback(this, a.node);
13423         }
13424     }
13425 });/*
13426  * Based on:
13427  * Ext JS Library 1.1.1
13428  * Copyright(c) 2006-2007, Ext JS, LLC.
13429  *
13430  * Originally Released Under LGPL - original licence link has changed is not relivant.
13431  *
13432  * Fork - LGPL
13433  * <script type="text/javascript">
13434  */
13435
13436 /**
13437 * @class Roo.tree.TreeFilter
13438 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13439 * @param {TreePanel} tree
13440 * @param {Object} config (optional)
13441  */
13442 Roo.tree.TreeFilter = function(tree, config){
13443     this.tree = tree;
13444     this.filtered = {};
13445     Roo.apply(this, config);
13446 };
13447
13448 Roo.tree.TreeFilter.prototype = {
13449     clearBlank:false,
13450     reverse:false,
13451     autoClear:false,
13452     remove:false,
13453
13454      /**
13455      * Filter the data by a specific attribute.
13456      * @param {String/RegExp} value Either string that the attribute value
13457      * should start with or a RegExp to test against the attribute
13458      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13459      * @param {TreeNode} startNode (optional) The node to start the filter at.
13460      */
13461     filter : function(value, attr, startNode){
13462         attr = attr || "text";
13463         var f;
13464         if(typeof value == "string"){
13465             var vlen = value.length;
13466             // auto clear empty filter
13467             if(vlen == 0 && this.clearBlank){
13468                 this.clear();
13469                 return;
13470             }
13471             value = value.toLowerCase();
13472             f = function(n){
13473                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13474             };
13475         }else if(value.exec){ // regex?
13476             f = function(n){
13477                 return value.test(n.attributes[attr]);
13478             };
13479         }else{
13480             throw 'Illegal filter type, must be string or regex';
13481         }
13482         this.filterBy(f, null, startNode);
13483         },
13484
13485     /**
13486      * Filter by a function. The passed function will be called with each
13487      * node in the tree (or from the startNode). If the function returns true, the node is kept
13488      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13489      * @param {Function} fn The filter function
13490      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13491      */
13492     filterBy : function(fn, scope, startNode){
13493         startNode = startNode || this.tree.root;
13494         if(this.autoClear){
13495             this.clear();
13496         }
13497         var af = this.filtered, rv = this.reverse;
13498         var f = function(n){
13499             if(n == startNode){
13500                 return true;
13501             }
13502             if(af[n.id]){
13503                 return false;
13504             }
13505             var m = fn.call(scope || n, n);
13506             if(!m || rv){
13507                 af[n.id] = n;
13508                 n.ui.hide();
13509                 return false;
13510             }
13511             return true;
13512         };
13513         startNode.cascade(f);
13514         if(this.remove){
13515            for(var id in af){
13516                if(typeof id != "function"){
13517                    var n = af[id];
13518                    if(n && n.parentNode){
13519                        n.parentNode.removeChild(n);
13520                    }
13521                }
13522            }
13523         }
13524     },
13525
13526     /**
13527      * Clears the current filter. Note: with the "remove" option
13528      * set a filter cannot be cleared.
13529      */
13530     clear : function(){
13531         var t = this.tree;
13532         var af = this.filtered;
13533         for(var id in af){
13534             if(typeof id != "function"){
13535                 var n = af[id];
13536                 if(n){
13537                     n.ui.show();
13538                 }
13539             }
13540         }
13541         this.filtered = {};
13542     }
13543 };
13544 /*
13545  * Based on:
13546  * Ext JS Library 1.1.1
13547  * Copyright(c) 2006-2007, Ext JS, LLC.
13548  *
13549  * Originally Released Under LGPL - original licence link has changed is not relivant.
13550  *
13551  * Fork - LGPL
13552  * <script type="text/javascript">
13553  */
13554  
13555
13556 /**
13557  * @class Roo.tree.TreeSorter
13558  * Provides sorting of nodes in a TreePanel
13559  * 
13560  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13561  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13562  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13563  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13564  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13565  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13566  * @constructor
13567  * @param {TreePanel} tree
13568  * @param {Object} config
13569  */
13570 Roo.tree.TreeSorter = function(tree, config){
13571     Roo.apply(this, config);
13572     tree.on("beforechildrenrendered", this.doSort, this);
13573     tree.on("append", this.updateSort, this);
13574     tree.on("insert", this.updateSort, this);
13575     
13576     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13577     var p = this.property || "text";
13578     var sortType = this.sortType;
13579     var fs = this.folderSort;
13580     var cs = this.caseSensitive === true;
13581     var leafAttr = this.leafAttr || 'leaf';
13582
13583     this.sortFn = function(n1, n2){
13584         if(fs){
13585             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13586                 return 1;
13587             }
13588             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13589                 return -1;
13590             }
13591         }
13592         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13593         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13594         if(v1 < v2){
13595                         return dsc ? +1 : -1;
13596                 }else if(v1 > v2){
13597                         return dsc ? -1 : +1;
13598         }else{
13599                 return 0;
13600         }
13601     };
13602 };
13603
13604 Roo.tree.TreeSorter.prototype = {
13605     doSort : function(node){
13606         node.sort(this.sortFn);
13607     },
13608     
13609     compareNodes : function(n1, n2){
13610         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13611     },
13612     
13613     updateSort : function(tree, node){
13614         if(node.childrenRendered){
13615             this.doSort.defer(1, this, [node]);
13616         }
13617     }
13618 };/*
13619  * Based on:
13620  * Ext JS Library 1.1.1
13621  * Copyright(c) 2006-2007, Ext JS, LLC.
13622  *
13623  * Originally Released Under LGPL - original licence link has changed is not relivant.
13624  *
13625  * Fork - LGPL
13626  * <script type="text/javascript">
13627  */
13628
13629 if(Roo.dd.DropZone){
13630     
13631 Roo.tree.TreeDropZone = function(tree, config){
13632     this.allowParentInsert = false;
13633     this.allowContainerDrop = false;
13634     this.appendOnly = false;
13635     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13636     this.tree = tree;
13637     this.lastInsertClass = "x-tree-no-status";
13638     this.dragOverData = {};
13639 };
13640
13641 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13642     ddGroup : "TreeDD",
13643     scroll:  true,
13644     
13645     expandDelay : 1000,
13646     
13647     expandNode : function(node){
13648         if(node.hasChildNodes() && !node.isExpanded()){
13649             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13650         }
13651     },
13652     
13653     queueExpand : function(node){
13654         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13655     },
13656     
13657     cancelExpand : function(){
13658         if(this.expandProcId){
13659             clearTimeout(this.expandProcId);
13660             this.expandProcId = false;
13661         }
13662     },
13663     
13664     isValidDropPoint : function(n, pt, dd, e, data){
13665         if(!n || !data){ return false; }
13666         var targetNode = n.node;
13667         var dropNode = data.node;
13668         // default drop rules
13669         if(!(targetNode && targetNode.isTarget && pt)){
13670             return false;
13671         }
13672         if(pt == "append" && targetNode.allowChildren === false){
13673             return false;
13674         }
13675         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13676             return false;
13677         }
13678         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13679             return false;
13680         }
13681         // reuse the object
13682         var overEvent = this.dragOverData;
13683         overEvent.tree = this.tree;
13684         overEvent.target = targetNode;
13685         overEvent.data = data;
13686         overEvent.point = pt;
13687         overEvent.source = dd;
13688         overEvent.rawEvent = e;
13689         overEvent.dropNode = dropNode;
13690         overEvent.cancel = false;  
13691         var result = this.tree.fireEvent("nodedragover", overEvent);
13692         return overEvent.cancel === false && result !== false;
13693     },
13694     
13695     getDropPoint : function(e, n, dd)
13696     {
13697         var tn = n.node;
13698         if(tn.isRoot){
13699             return tn.allowChildren !== false ? "append" : false; // always append for root
13700         }
13701         var dragEl = n.ddel;
13702         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13703         var y = Roo.lib.Event.getPageY(e);
13704         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13705         
13706         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13707         var noAppend = tn.allowChildren === false;
13708         if(this.appendOnly || tn.parentNode.allowChildren === false){
13709             return noAppend ? false : "append";
13710         }
13711         var noBelow = false;
13712         if(!this.allowParentInsert){
13713             noBelow = tn.hasChildNodes() && tn.isExpanded();
13714         }
13715         var q = (b - t) / (noAppend ? 2 : 3);
13716         if(y >= t && y < (t + q)){
13717             return "above";
13718         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13719             return "below";
13720         }else{
13721             return "append";
13722         }
13723     },
13724     
13725     onNodeEnter : function(n, dd, e, data)
13726     {
13727         this.cancelExpand();
13728     },
13729     
13730     onNodeOver : function(n, dd, e, data)
13731     {
13732        
13733         var pt = this.getDropPoint(e, n, dd);
13734         var node = n.node;
13735         
13736         // auto node expand check
13737         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13738             this.queueExpand(node);
13739         }else if(pt != "append"){
13740             this.cancelExpand();
13741         }
13742         
13743         // set the insert point style on the target node
13744         var returnCls = this.dropNotAllowed;
13745         if(this.isValidDropPoint(n, pt, dd, e, data)){
13746            if(pt){
13747                var el = n.ddel;
13748                var cls;
13749                if(pt == "above"){
13750                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13751                    cls = "x-tree-drag-insert-above";
13752                }else if(pt == "below"){
13753                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13754                    cls = "x-tree-drag-insert-below";
13755                }else{
13756                    returnCls = "x-tree-drop-ok-append";
13757                    cls = "x-tree-drag-append";
13758                }
13759                if(this.lastInsertClass != cls){
13760                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13761                    this.lastInsertClass = cls;
13762                }
13763            }
13764        }
13765        return returnCls;
13766     },
13767     
13768     onNodeOut : function(n, dd, e, data){
13769         
13770         this.cancelExpand();
13771         this.removeDropIndicators(n);
13772     },
13773     
13774     onNodeDrop : function(n, dd, e, data){
13775         var point = this.getDropPoint(e, n, dd);
13776         var targetNode = n.node;
13777         targetNode.ui.startDrop();
13778         if(!this.isValidDropPoint(n, point, dd, e, data)){
13779             targetNode.ui.endDrop();
13780             return false;
13781         }
13782         // first try to find the drop node
13783         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13784         var dropEvent = {
13785             tree : this.tree,
13786             target: targetNode,
13787             data: data,
13788             point: point,
13789             source: dd,
13790             rawEvent: e,
13791             dropNode: dropNode,
13792             cancel: !dropNode   
13793         };
13794         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13795         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13796             targetNode.ui.endDrop();
13797             return false;
13798         }
13799         // allow target changing
13800         targetNode = dropEvent.target;
13801         if(point == "append" && !targetNode.isExpanded()){
13802             targetNode.expand(false, null, function(){
13803                 this.completeDrop(dropEvent);
13804             }.createDelegate(this));
13805         }else{
13806             this.completeDrop(dropEvent);
13807         }
13808         return true;
13809     },
13810     
13811     completeDrop : function(de){
13812         var ns = de.dropNode, p = de.point, t = de.target;
13813         if(!(ns instanceof Array)){
13814             ns = [ns];
13815         }
13816         var n;
13817         for(var i = 0, len = ns.length; i < len; i++){
13818             n = ns[i];
13819             if(p == "above"){
13820                 t.parentNode.insertBefore(n, t);
13821             }else if(p == "below"){
13822                 t.parentNode.insertBefore(n, t.nextSibling);
13823             }else{
13824                 t.appendChild(n);
13825             }
13826         }
13827         n.ui.focus();
13828         if(this.tree.hlDrop){
13829             n.ui.highlight();
13830         }
13831         t.ui.endDrop();
13832         this.tree.fireEvent("nodedrop", de);
13833     },
13834     
13835     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13836         if(this.tree.hlDrop){
13837             dropNode.ui.focus();
13838             dropNode.ui.highlight();
13839         }
13840         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13841     },
13842     
13843     getTree : function(){
13844         return this.tree;
13845     },
13846     
13847     removeDropIndicators : function(n){
13848         if(n && n.ddel){
13849             var el = n.ddel;
13850             Roo.fly(el).removeClass([
13851                     "x-tree-drag-insert-above",
13852                     "x-tree-drag-insert-below",
13853                     "x-tree-drag-append"]);
13854             this.lastInsertClass = "_noclass";
13855         }
13856     },
13857     
13858     beforeDragDrop : function(target, e, id){
13859         this.cancelExpand();
13860         return true;
13861     },
13862     
13863     afterRepair : function(data){
13864         if(data && Roo.enableFx){
13865             data.node.ui.highlight();
13866         }
13867         this.hideProxy();
13868     } 
13869     
13870 });
13871
13872 }
13873 /*
13874  * Based on:
13875  * Ext JS Library 1.1.1
13876  * Copyright(c) 2006-2007, Ext JS, LLC.
13877  *
13878  * Originally Released Under LGPL - original licence link has changed is not relivant.
13879  *
13880  * Fork - LGPL
13881  * <script type="text/javascript">
13882  */
13883  
13884
13885 if(Roo.dd.DragZone){
13886 Roo.tree.TreeDragZone = function(tree, config){
13887     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13888     this.tree = tree;
13889 };
13890
13891 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13892     ddGroup : "TreeDD",
13893    
13894     onBeforeDrag : function(data, e){
13895         var n = data.node;
13896         return n && n.draggable && !n.disabled;
13897     },
13898      
13899     
13900     onInitDrag : function(e){
13901         var data = this.dragData;
13902         this.tree.getSelectionModel().select(data.node);
13903         this.proxy.update("");
13904         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13905         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13906     },
13907     
13908     getRepairXY : function(e, data){
13909         return data.node.ui.getDDRepairXY();
13910     },
13911     
13912     onEndDrag : function(data, e){
13913         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13914         
13915         
13916     },
13917     
13918     onValidDrop : function(dd, e, id){
13919         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13920         this.hideProxy();
13921     },
13922     
13923     beforeInvalidDrop : function(e, id){
13924         // this scrolls the original position back into view
13925         var sm = this.tree.getSelectionModel();
13926         sm.clearSelections();
13927         sm.select(this.dragData.node);
13928     }
13929 });
13930 }/*
13931  * Based on:
13932  * Ext JS Library 1.1.1
13933  * Copyright(c) 2006-2007, Ext JS, LLC.
13934  *
13935  * Originally Released Under LGPL - original licence link has changed is not relivant.
13936  *
13937  * Fork - LGPL
13938  * <script type="text/javascript">
13939  */
13940 /**
13941  * @class Roo.tree.TreeEditor
13942  * @extends Roo.Editor
13943  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
13944  * as the editor field.
13945  * @constructor
13946  * @param {Object} config (used to be the tree panel.)
13947  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
13948  * 
13949  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
13950  * @cfg {Roo.form.TextField|Object} field The field configuration
13951  *
13952  * 
13953  */
13954 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
13955     var tree = config;
13956     var field;
13957     if (oldconfig) { // old style..
13958         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
13959     } else {
13960         // new style..
13961         tree = config.tree;
13962         config.field = config.field  || {};
13963         config.field.xtype = 'TextField';
13964         field = Roo.factory(config.field, Roo.form);
13965     }
13966     config = config || {};
13967     
13968     
13969     this.addEvents({
13970         /**
13971          * @event beforenodeedit
13972          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13973          * false from the handler of this event.
13974          * @param {Editor} this
13975          * @param {Roo.tree.Node} node 
13976          */
13977         "beforenodeedit" : true
13978     });
13979     
13980     //Roo.log(config);
13981     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
13982
13983     this.tree = tree;
13984
13985     tree.on('beforeclick', this.beforeNodeClick, this);
13986     tree.getTreeEl().on('mousedown', this.hide, this);
13987     this.on('complete', this.updateNode, this);
13988     this.on('beforestartedit', this.fitToTree, this);
13989     this.on('startedit', this.bindScroll, this, {delay:10});
13990     this.on('specialkey', this.onSpecialKey, this);
13991 };
13992
13993 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
13994     /**
13995      * @cfg {String} alignment
13996      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
13997      */
13998     alignment: "l-l",
13999     // inherit
14000     autoSize: false,
14001     /**
14002      * @cfg {Boolean} hideEl
14003      * True to hide the bound element while the editor is displayed (defaults to false)
14004      */
14005     hideEl : false,
14006     /**
14007      * @cfg {String} cls
14008      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14009      */
14010     cls: "x-small-editor x-tree-editor",
14011     /**
14012      * @cfg {Boolean} shim
14013      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14014      */
14015     shim:false,
14016     // inherit
14017     shadow:"frame",
14018     /**
14019      * @cfg {Number} maxWidth
14020      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14021      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14022      * scroll and client offsets into account prior to each edit.
14023      */
14024     maxWidth: 250,
14025
14026     editDelay : 350,
14027
14028     // private
14029     fitToTree : function(ed, el){
14030         var td = this.tree.getTreeEl().dom, nd = el.dom;
14031         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14032             td.scrollLeft = nd.offsetLeft;
14033         }
14034         var w = Math.min(
14035                 this.maxWidth,
14036                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14037         this.setSize(w, '');
14038         
14039         return this.fireEvent('beforenodeedit', this, this.editNode);
14040         
14041     },
14042
14043     // private
14044     triggerEdit : function(node){
14045         this.completeEdit();
14046         this.editNode = node;
14047         this.startEdit(node.ui.textNode, node.text);
14048     },
14049
14050     // private
14051     bindScroll : function(){
14052         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14053     },
14054
14055     // private
14056     beforeNodeClick : function(node, e){
14057         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14058         this.lastClick = new Date();
14059         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14060             e.stopEvent();
14061             this.triggerEdit(node);
14062             return false;
14063         }
14064         return true;
14065     },
14066
14067     // private
14068     updateNode : function(ed, value){
14069         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14070         this.editNode.setText(value);
14071     },
14072
14073     // private
14074     onHide : function(){
14075         Roo.tree.TreeEditor.superclass.onHide.call(this);
14076         if(this.editNode){
14077             this.editNode.ui.focus();
14078         }
14079     },
14080
14081     // private
14082     onSpecialKey : function(field, e){
14083         var k = e.getKey();
14084         if(k == e.ESC){
14085             e.stopEvent();
14086             this.cancelEdit();
14087         }else if(k == e.ENTER && !e.hasModifier()){
14088             e.stopEvent();
14089             this.completeEdit();
14090         }
14091     }
14092 });//<Script type="text/javascript">
14093 /*
14094  * Based on:
14095  * Ext JS Library 1.1.1
14096  * Copyright(c) 2006-2007, Ext JS, LLC.
14097  *
14098  * Originally Released Under LGPL - original licence link has changed is not relivant.
14099  *
14100  * Fork - LGPL
14101  * <script type="text/javascript">
14102  */
14103  
14104 /**
14105  * Not documented??? - probably should be...
14106  */
14107
14108 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14109     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14110     
14111     renderElements : function(n, a, targetNode, bulkRender){
14112         //consel.log("renderElements?");
14113         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14114
14115         var t = n.getOwnerTree();
14116         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14117         
14118         var cols = t.columns;
14119         var bw = t.borderWidth;
14120         var c = cols[0];
14121         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14122          var cb = typeof a.checked == "boolean";
14123         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14124         var colcls = 'x-t-' + tid + '-c0';
14125         var buf = [
14126             '<li class="x-tree-node">',
14127             
14128                 
14129                 '<div class="x-tree-node-el ', a.cls,'">',
14130                     // extran...
14131                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14132                 
14133                 
14134                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14135                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14136                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14137                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14138                            (a.iconCls ? ' '+a.iconCls : ''),
14139                            '" unselectable="on" />',
14140                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14141                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14142                              
14143                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14144                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14145                             '<span unselectable="on" qtip="' + tx + '">',
14146                              tx,
14147                              '</span></a>' ,
14148                     '</div>',
14149                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14150                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14151                  ];
14152         for(var i = 1, len = cols.length; i < len; i++){
14153             c = cols[i];
14154             colcls = 'x-t-' + tid + '-c' +i;
14155             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14156             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14157                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14158                       "</div>");
14159          }
14160          
14161          buf.push(
14162             '</a>',
14163             '<div class="x-clear"></div></div>',
14164             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14165             "</li>");
14166         
14167         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14168             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14169                                 n.nextSibling.ui.getEl(), buf.join(""));
14170         }else{
14171             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14172         }
14173         var el = this.wrap.firstChild;
14174         this.elRow = el;
14175         this.elNode = el.firstChild;
14176         this.ranchor = el.childNodes[1];
14177         this.ctNode = this.wrap.childNodes[1];
14178         var cs = el.firstChild.childNodes;
14179         this.indentNode = cs[0];
14180         this.ecNode = cs[1];
14181         this.iconNode = cs[2];
14182         var index = 3;
14183         if(cb){
14184             this.checkbox = cs[3];
14185             index++;
14186         }
14187         this.anchor = cs[index];
14188         
14189         this.textNode = cs[index].firstChild;
14190         
14191         //el.on("click", this.onClick, this);
14192         //el.on("dblclick", this.onDblClick, this);
14193         
14194         
14195        // console.log(this);
14196     },
14197     initEvents : function(){
14198         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14199         
14200             
14201         var a = this.ranchor;
14202
14203         var el = Roo.get(a);
14204
14205         if(Roo.isOpera){ // opera render bug ignores the CSS
14206             el.setStyle("text-decoration", "none");
14207         }
14208
14209         el.on("click", this.onClick, this);
14210         el.on("dblclick", this.onDblClick, this);
14211         el.on("contextmenu", this.onContextMenu, this);
14212         
14213     },
14214     
14215     /*onSelectedChange : function(state){
14216         if(state){
14217             this.focus();
14218             this.addClass("x-tree-selected");
14219         }else{
14220             //this.blur();
14221             this.removeClass("x-tree-selected");
14222         }
14223     },*/
14224     addClass : function(cls){
14225         if(this.elRow){
14226             Roo.fly(this.elRow).addClass(cls);
14227         }
14228         
14229     },
14230     
14231     
14232     removeClass : function(cls){
14233         if(this.elRow){
14234             Roo.fly(this.elRow).removeClass(cls);
14235         }
14236     }
14237
14238     
14239     
14240 });//<Script type="text/javascript">
14241
14242 /*
14243  * Based on:
14244  * Ext JS Library 1.1.1
14245  * Copyright(c) 2006-2007, Ext JS, LLC.
14246  *
14247  * Originally Released Under LGPL - original licence link has changed is not relivant.
14248  *
14249  * Fork - LGPL
14250  * <script type="text/javascript">
14251  */
14252  
14253
14254 /**
14255  * @class Roo.tree.ColumnTree
14256  * @extends Roo.data.TreePanel
14257  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14258  * @cfg {int} borderWidth  compined right/left border allowance
14259  * @constructor
14260  * @param {String/HTMLElement/Element} el The container element
14261  * @param {Object} config
14262  */
14263 Roo.tree.ColumnTree =  function(el, config)
14264 {
14265    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14266    this.addEvents({
14267         /**
14268         * @event resize
14269         * Fire this event on a container when it resizes
14270         * @param {int} w Width
14271         * @param {int} h Height
14272         */
14273        "resize" : true
14274     });
14275     this.on('resize', this.onResize, this);
14276 };
14277
14278 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14279     //lines:false,
14280     
14281     
14282     borderWidth: Roo.isBorderBox ? 0 : 2, 
14283     headEls : false,
14284     
14285     render : function(){
14286         // add the header.....
14287        
14288         Roo.tree.ColumnTree.superclass.render.apply(this);
14289         
14290         this.el.addClass('x-column-tree');
14291         
14292         this.headers = this.el.createChild(
14293             {cls:'x-tree-headers'},this.innerCt.dom);
14294    
14295         var cols = this.columns, c;
14296         var totalWidth = 0;
14297         this.headEls = [];
14298         var  len = cols.length;
14299         for(var i = 0; i < len; i++){
14300              c = cols[i];
14301              totalWidth += c.width;
14302             this.headEls.push(this.headers.createChild({
14303                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14304                  cn: {
14305                      cls:'x-tree-hd-text',
14306                      html: c.header
14307                  },
14308                  style:'width:'+(c.width-this.borderWidth)+'px;'
14309              }));
14310         }
14311         this.headers.createChild({cls:'x-clear'});
14312         // prevent floats from wrapping when clipped
14313         this.headers.setWidth(totalWidth);
14314         //this.innerCt.setWidth(totalWidth);
14315         this.innerCt.setStyle({ overflow: 'auto' });
14316         this.onResize(this.width, this.height);
14317              
14318         
14319     },
14320     onResize : function(w,h)
14321     {
14322         this.height = h;
14323         this.width = w;
14324         // resize cols..
14325         this.innerCt.setWidth(this.width);
14326         this.innerCt.setHeight(this.height-20);
14327         
14328         // headers...
14329         var cols = this.columns, c;
14330         var totalWidth = 0;
14331         var expEl = false;
14332         var len = cols.length;
14333         for(var i = 0; i < len; i++){
14334             c = cols[i];
14335             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14336                 // it's the expander..
14337                 expEl  = this.headEls[i];
14338                 continue;
14339             }
14340             totalWidth += c.width;
14341             
14342         }
14343         if (expEl) {
14344             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14345         }
14346         this.headers.setWidth(w-20);
14347
14348         
14349         
14350         
14351     }
14352 });
14353 /*
14354  * Based on:
14355  * Ext JS Library 1.1.1
14356  * Copyright(c) 2006-2007, Ext JS, LLC.
14357  *
14358  * Originally Released Under LGPL - original licence link has changed is not relivant.
14359  *
14360  * Fork - LGPL
14361  * <script type="text/javascript">
14362  */
14363  
14364 /**
14365  * @class Roo.menu.Menu
14366  * @extends Roo.util.Observable
14367  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14368  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14369  * @constructor
14370  * Creates a new Menu
14371  * @param {Object} config Configuration options
14372  */
14373 Roo.menu.Menu = function(config){
14374     
14375     Roo.menu.Menu.superclass.constructor.call(this, config);
14376     
14377     this.id = this.id || Roo.id();
14378     this.addEvents({
14379         /**
14380          * @event beforeshow
14381          * Fires before this menu is displayed
14382          * @param {Roo.menu.Menu} this
14383          */
14384         beforeshow : true,
14385         /**
14386          * @event beforehide
14387          * Fires before this menu is hidden
14388          * @param {Roo.menu.Menu} this
14389          */
14390         beforehide : true,
14391         /**
14392          * @event show
14393          * Fires after this menu is displayed
14394          * @param {Roo.menu.Menu} this
14395          */
14396         show : true,
14397         /**
14398          * @event hide
14399          * Fires after this menu is hidden
14400          * @param {Roo.menu.Menu} this
14401          */
14402         hide : true,
14403         /**
14404          * @event click
14405          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14406          * @param {Roo.menu.Menu} this
14407          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14408          * @param {Roo.EventObject} e
14409          */
14410         click : true,
14411         /**
14412          * @event mouseover
14413          * Fires when the mouse is hovering over this menu
14414          * @param {Roo.menu.Menu} this
14415          * @param {Roo.EventObject} e
14416          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14417          */
14418         mouseover : true,
14419         /**
14420          * @event mouseout
14421          * Fires when the mouse exits this menu
14422          * @param {Roo.menu.Menu} this
14423          * @param {Roo.EventObject} e
14424          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14425          */
14426         mouseout : true,
14427         /**
14428          * @event itemclick
14429          * Fires when a menu item contained in this menu is clicked
14430          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14431          * @param {Roo.EventObject} e
14432          */
14433         itemclick: true
14434     });
14435     if (this.registerMenu) {
14436         Roo.menu.MenuMgr.register(this);
14437     }
14438     
14439     var mis = this.items;
14440     this.items = new Roo.util.MixedCollection();
14441     if(mis){
14442         this.add.apply(this, mis);
14443     }
14444 };
14445
14446 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14447     /**
14448      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14449      */
14450     minWidth : 120,
14451     /**
14452      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14453      * for bottom-right shadow (defaults to "sides")
14454      */
14455     shadow : "sides",
14456     /**
14457      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14458      * this menu (defaults to "tl-tr?")
14459      */
14460     subMenuAlign : "tl-tr?",
14461     /**
14462      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14463      * relative to its element of origin (defaults to "tl-bl?")
14464      */
14465     defaultAlign : "tl-bl?",
14466     /**
14467      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14468      */
14469     allowOtherMenus : false,
14470     /**
14471      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14472      */
14473     registerMenu : true,
14474
14475     hidden:true,
14476
14477     // private
14478     render : function(){
14479         if(this.el){
14480             return;
14481         }
14482         var el = this.el = new Roo.Layer({
14483             cls: "x-menu",
14484             shadow:this.shadow,
14485             constrain: false,
14486             parentEl: this.parentEl || document.body,
14487             zindex:15000
14488         });
14489
14490         this.keyNav = new Roo.menu.MenuNav(this);
14491
14492         if(this.plain){
14493             el.addClass("x-menu-plain");
14494         }
14495         if(this.cls){
14496             el.addClass(this.cls);
14497         }
14498         // generic focus element
14499         this.focusEl = el.createChild({
14500             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14501         });
14502         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14503         //disabling touch- as it's causing issues ..
14504         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14505         ul.on('click'   , this.onClick, this);
14506         
14507         
14508         ul.on("mouseover", this.onMouseOver, this);
14509         ul.on("mouseout", this.onMouseOut, this);
14510         this.items.each(function(item){
14511             if (item.hidden) {
14512                 return;
14513             }
14514             
14515             var li = document.createElement("li");
14516             li.className = "x-menu-list-item";
14517             ul.dom.appendChild(li);
14518             item.render(li, this);
14519         }, this);
14520         this.ul = ul;
14521         this.autoWidth();
14522     },
14523
14524     // private
14525     autoWidth : function(){
14526         var el = this.el, ul = this.ul;
14527         if(!el){
14528             return;
14529         }
14530         var w = this.width;
14531         if(w){
14532             el.setWidth(w);
14533         }else if(Roo.isIE){
14534             el.setWidth(this.minWidth);
14535             var t = el.dom.offsetWidth; // force recalc
14536             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14537         }
14538     },
14539
14540     // private
14541     delayAutoWidth : function(){
14542         if(this.rendered){
14543             if(!this.awTask){
14544                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14545             }
14546             this.awTask.delay(20);
14547         }
14548     },
14549
14550     // private
14551     findTargetItem : function(e){
14552         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14553         if(t && t.menuItemId){
14554             return this.items.get(t.menuItemId);
14555         }
14556     },
14557
14558     // private
14559     onClick : function(e){
14560         Roo.log("menu.onClick");
14561         var t = this.findTargetItem(e);
14562         if(!t){
14563             return;
14564         }
14565         Roo.log(e);
14566         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14567             if(t == this.activeItem && t.shouldDeactivate(e)){
14568                 this.activeItem.deactivate();
14569                 delete this.activeItem;
14570                 return;
14571             }
14572             if(t.canActivate){
14573                 this.setActiveItem(t, true);
14574             }
14575             return;
14576             
14577             
14578         }
14579         
14580         t.onClick(e);
14581         this.fireEvent("click", this, t, e);
14582     },
14583
14584     // private
14585     setActiveItem : function(item, autoExpand){
14586         if(item != this.activeItem){
14587             if(this.activeItem){
14588                 this.activeItem.deactivate();
14589             }
14590             this.activeItem = item;
14591             item.activate(autoExpand);
14592         }else if(autoExpand){
14593             item.expandMenu();
14594         }
14595     },
14596
14597     // private
14598     tryActivate : function(start, step){
14599         var items = this.items;
14600         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14601             var item = items.get(i);
14602             if(!item.disabled && item.canActivate){
14603                 this.setActiveItem(item, false);
14604                 return item;
14605             }
14606         }
14607         return false;
14608     },
14609
14610     // private
14611     onMouseOver : function(e){
14612         var t;
14613         if(t = this.findTargetItem(e)){
14614             if(t.canActivate && !t.disabled){
14615                 this.setActiveItem(t, true);
14616             }
14617         }
14618         this.fireEvent("mouseover", this, e, t);
14619     },
14620
14621     // private
14622     onMouseOut : function(e){
14623         var t;
14624         if(t = this.findTargetItem(e)){
14625             if(t == this.activeItem && t.shouldDeactivate(e)){
14626                 this.activeItem.deactivate();
14627                 delete this.activeItem;
14628             }
14629         }
14630         this.fireEvent("mouseout", this, e, t);
14631     },
14632
14633     /**
14634      * Read-only.  Returns true if the menu is currently displayed, else false.
14635      * @type Boolean
14636      */
14637     isVisible : function(){
14638         return this.el && !this.hidden;
14639     },
14640
14641     /**
14642      * Displays this menu relative to another element
14643      * @param {String/HTMLElement/Roo.Element} element The element to align to
14644      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14645      * the element (defaults to this.defaultAlign)
14646      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14647      */
14648     show : function(el, pos, parentMenu){
14649         this.parentMenu = parentMenu;
14650         if(!this.el){
14651             this.render();
14652         }
14653         this.fireEvent("beforeshow", this);
14654         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14655     },
14656
14657     /**
14658      * Displays this menu at a specific xy position
14659      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14660      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14661      */
14662     showAt : function(xy, parentMenu, /* private: */_e){
14663         this.parentMenu = parentMenu;
14664         if(!this.el){
14665             this.render();
14666         }
14667         if(_e !== false){
14668             this.fireEvent("beforeshow", this);
14669             xy = this.el.adjustForConstraints(xy);
14670         }
14671         this.el.setXY(xy);
14672         this.el.show();
14673         this.hidden = false;
14674         this.focus();
14675         this.fireEvent("show", this);
14676     },
14677
14678     focus : function(){
14679         if(!this.hidden){
14680             this.doFocus.defer(50, this);
14681         }
14682     },
14683
14684     doFocus : function(){
14685         if(!this.hidden){
14686             this.focusEl.focus();
14687         }
14688     },
14689
14690     /**
14691      * Hides this menu and optionally all parent menus
14692      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14693      */
14694     hide : function(deep){
14695         if(this.el && this.isVisible()){
14696             this.fireEvent("beforehide", this);
14697             if(this.activeItem){
14698                 this.activeItem.deactivate();
14699                 this.activeItem = null;
14700             }
14701             this.el.hide();
14702             this.hidden = true;
14703             this.fireEvent("hide", this);
14704         }
14705         if(deep === true && this.parentMenu){
14706             this.parentMenu.hide(true);
14707         }
14708     },
14709
14710     /**
14711      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14712      * Any of the following are valid:
14713      * <ul>
14714      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14715      * <li>An HTMLElement object which will be converted to a menu item</li>
14716      * <li>A menu item config object that will be created as a new menu item</li>
14717      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14718      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14719      * </ul>
14720      * Usage:
14721      * <pre><code>
14722 // Create the menu
14723 var menu = new Roo.menu.Menu();
14724
14725 // Create a menu item to add by reference
14726 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14727
14728 // Add a bunch of items at once using different methods.
14729 // Only the last item added will be returned.
14730 var item = menu.add(
14731     menuItem,                // add existing item by ref
14732     'Dynamic Item',          // new TextItem
14733     '-',                     // new separator
14734     { text: 'Config Item' }  // new item by config
14735 );
14736 </code></pre>
14737      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14738      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14739      */
14740     add : function(){
14741         var a = arguments, l = a.length, item;
14742         for(var i = 0; i < l; i++){
14743             var el = a[i];
14744             if ((typeof(el) == "object") && el.xtype && el.xns) {
14745                 el = Roo.factory(el, Roo.menu);
14746             }
14747             
14748             if(el.render){ // some kind of Item
14749                 item = this.addItem(el);
14750             }else if(typeof el == "string"){ // string
14751                 if(el == "separator" || el == "-"){
14752                     item = this.addSeparator();
14753                 }else{
14754                     item = this.addText(el);
14755                 }
14756             }else if(el.tagName || el.el){ // element
14757                 item = this.addElement(el);
14758             }else if(typeof el == "object"){ // must be menu item config?
14759                 item = this.addMenuItem(el);
14760             }
14761         }
14762         return item;
14763     },
14764
14765     /**
14766      * Returns this menu's underlying {@link Roo.Element} object
14767      * @return {Roo.Element} The element
14768      */
14769     getEl : function(){
14770         if(!this.el){
14771             this.render();
14772         }
14773         return this.el;
14774     },
14775
14776     /**
14777      * Adds a separator bar to the menu
14778      * @return {Roo.menu.Item} The menu item that was added
14779      */
14780     addSeparator : function(){
14781         return this.addItem(new Roo.menu.Separator());
14782     },
14783
14784     /**
14785      * Adds an {@link Roo.Element} object to the menu
14786      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14787      * @return {Roo.menu.Item} The menu item that was added
14788      */
14789     addElement : function(el){
14790         return this.addItem(new Roo.menu.BaseItem(el));
14791     },
14792
14793     /**
14794      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14795      * @param {Roo.menu.Item} item The menu item to add
14796      * @return {Roo.menu.Item} The menu item that was added
14797      */
14798     addItem : function(item){
14799         this.items.add(item);
14800         if(this.ul){
14801             var li = document.createElement("li");
14802             li.className = "x-menu-list-item";
14803             this.ul.dom.appendChild(li);
14804             item.render(li, this);
14805             this.delayAutoWidth();
14806         }
14807         return item;
14808     },
14809
14810     /**
14811      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14812      * @param {Object} config A MenuItem config object
14813      * @return {Roo.menu.Item} The menu item that was added
14814      */
14815     addMenuItem : function(config){
14816         if(!(config instanceof Roo.menu.Item)){
14817             if(typeof config.checked == "boolean"){ // must be check menu item config?
14818                 config = new Roo.menu.CheckItem(config);
14819             }else{
14820                 config = new Roo.menu.Item(config);
14821             }
14822         }
14823         return this.addItem(config);
14824     },
14825
14826     /**
14827      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14828      * @param {String} text The text to display in the menu item
14829      * @return {Roo.menu.Item} The menu item that was added
14830      */
14831     addText : function(text){
14832         return this.addItem(new Roo.menu.TextItem({ text : text }));
14833     },
14834
14835     /**
14836      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14837      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14838      * @param {Roo.menu.Item} item The menu item to add
14839      * @return {Roo.menu.Item} The menu item that was added
14840      */
14841     insert : function(index, item){
14842         this.items.insert(index, item);
14843         if(this.ul){
14844             var li = document.createElement("li");
14845             li.className = "x-menu-list-item";
14846             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14847             item.render(li, this);
14848             this.delayAutoWidth();
14849         }
14850         return item;
14851     },
14852
14853     /**
14854      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14855      * @param {Roo.menu.Item} item The menu item to remove
14856      */
14857     remove : function(item){
14858         this.items.removeKey(item.id);
14859         item.destroy();
14860     },
14861
14862     /**
14863      * Removes and destroys all items in the menu
14864      */
14865     removeAll : function(){
14866         var f;
14867         while(f = this.items.first()){
14868             this.remove(f);
14869         }
14870     }
14871 });
14872
14873 // MenuNav is a private utility class used internally by the Menu
14874 Roo.menu.MenuNav = function(menu){
14875     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14876     this.scope = this.menu = menu;
14877 };
14878
14879 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14880     doRelay : function(e, h){
14881         var k = e.getKey();
14882         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14883             this.menu.tryActivate(0, 1);
14884             return false;
14885         }
14886         return h.call(this.scope || this, e, this.menu);
14887     },
14888
14889     up : function(e, m){
14890         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14891             m.tryActivate(m.items.length-1, -1);
14892         }
14893     },
14894
14895     down : function(e, m){
14896         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14897             m.tryActivate(0, 1);
14898         }
14899     },
14900
14901     right : function(e, m){
14902         if(m.activeItem){
14903             m.activeItem.expandMenu(true);
14904         }
14905     },
14906
14907     left : function(e, m){
14908         m.hide();
14909         if(m.parentMenu && m.parentMenu.activeItem){
14910             m.parentMenu.activeItem.activate();
14911         }
14912     },
14913
14914     enter : function(e, m){
14915         if(m.activeItem){
14916             e.stopPropagation();
14917             m.activeItem.onClick(e);
14918             m.fireEvent("click", this, m.activeItem);
14919             return true;
14920         }
14921     }
14922 });/*
14923  * Based on:
14924  * Ext JS Library 1.1.1
14925  * Copyright(c) 2006-2007, Ext JS, LLC.
14926  *
14927  * Originally Released Under LGPL - original licence link has changed is not relivant.
14928  *
14929  * Fork - LGPL
14930  * <script type="text/javascript">
14931  */
14932  
14933 /**
14934  * @class Roo.menu.MenuMgr
14935  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14936  * @singleton
14937  */
14938 Roo.menu.MenuMgr = function(){
14939    var menus, active, groups = {}, attached = false, lastShow = new Date();
14940
14941    // private - called when first menu is created
14942    function init(){
14943        menus = {};
14944        active = new Roo.util.MixedCollection();
14945        Roo.get(document).addKeyListener(27, function(){
14946            if(active.length > 0){
14947                hideAll();
14948            }
14949        });
14950    }
14951
14952    // private
14953    function hideAll(){
14954        if(active && active.length > 0){
14955            var c = active.clone();
14956            c.each(function(m){
14957                m.hide();
14958            });
14959        }
14960    }
14961
14962    // private
14963    function onHide(m){
14964        active.remove(m);
14965        if(active.length < 1){
14966            Roo.get(document).un("mousedown", onMouseDown);
14967            attached = false;
14968        }
14969    }
14970
14971    // private
14972    function onShow(m){
14973        var last = active.last();
14974        lastShow = new Date();
14975        active.add(m);
14976        if(!attached){
14977            Roo.get(document).on("mousedown", onMouseDown);
14978            attached = true;
14979        }
14980        if(m.parentMenu){
14981           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
14982           m.parentMenu.activeChild = m;
14983        }else if(last && last.isVisible()){
14984           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
14985        }
14986    }
14987
14988    // private
14989    function onBeforeHide(m){
14990        if(m.activeChild){
14991            m.activeChild.hide();
14992        }
14993        if(m.autoHideTimer){
14994            clearTimeout(m.autoHideTimer);
14995            delete m.autoHideTimer;
14996        }
14997    }
14998
14999    // private
15000    function onBeforeShow(m){
15001        var pm = m.parentMenu;
15002        if(!pm && !m.allowOtherMenus){
15003            hideAll();
15004        }else if(pm && pm.activeChild && active != m){
15005            pm.activeChild.hide();
15006        }
15007    }
15008
15009    // private
15010    function onMouseDown(e){
15011        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15012            hideAll();
15013        }
15014    }
15015
15016    // private
15017    function onBeforeCheck(mi, state){
15018        if(state){
15019            var g = groups[mi.group];
15020            for(var i = 0, l = g.length; i < l; i++){
15021                if(g[i] != mi){
15022                    g[i].setChecked(false);
15023                }
15024            }
15025        }
15026    }
15027
15028    return {
15029
15030        /**
15031         * Hides all menus that are currently visible
15032         */
15033        hideAll : function(){
15034             hideAll();  
15035        },
15036
15037        // private
15038        register : function(menu){
15039            if(!menus){
15040                init();
15041            }
15042            menus[menu.id] = menu;
15043            menu.on("beforehide", onBeforeHide);
15044            menu.on("hide", onHide);
15045            menu.on("beforeshow", onBeforeShow);
15046            menu.on("show", onShow);
15047            var g = menu.group;
15048            if(g && menu.events["checkchange"]){
15049                if(!groups[g]){
15050                    groups[g] = [];
15051                }
15052                groups[g].push(menu);
15053                menu.on("checkchange", onCheck);
15054            }
15055        },
15056
15057         /**
15058          * Returns a {@link Roo.menu.Menu} object
15059          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15060          * be used to generate and return a new Menu instance.
15061          */
15062        get : function(menu){
15063            if(typeof menu == "string"){ // menu id
15064                return menus[menu];
15065            }else if(menu.events){  // menu instance
15066                return menu;
15067            }else if(typeof menu.length == 'number'){ // array of menu items?
15068                return new Roo.menu.Menu({items:menu});
15069            }else{ // otherwise, must be a config
15070                return new Roo.menu.Menu(menu);
15071            }
15072        },
15073
15074        // private
15075        unregister : function(menu){
15076            delete menus[menu.id];
15077            menu.un("beforehide", onBeforeHide);
15078            menu.un("hide", onHide);
15079            menu.un("beforeshow", onBeforeShow);
15080            menu.un("show", onShow);
15081            var g = menu.group;
15082            if(g && menu.events["checkchange"]){
15083                groups[g].remove(menu);
15084                menu.un("checkchange", onCheck);
15085            }
15086        },
15087
15088        // private
15089        registerCheckable : function(menuItem){
15090            var g = menuItem.group;
15091            if(g){
15092                if(!groups[g]){
15093                    groups[g] = [];
15094                }
15095                groups[g].push(menuItem);
15096                menuItem.on("beforecheckchange", onBeforeCheck);
15097            }
15098        },
15099
15100        // private
15101        unregisterCheckable : function(menuItem){
15102            var g = menuItem.group;
15103            if(g){
15104                groups[g].remove(menuItem);
15105                menuItem.un("beforecheckchange", onBeforeCheck);
15106            }
15107        }
15108    };
15109 }();/*
15110  * Based on:
15111  * Ext JS Library 1.1.1
15112  * Copyright(c) 2006-2007, Ext JS, LLC.
15113  *
15114  * Originally Released Under LGPL - original licence link has changed is not relivant.
15115  *
15116  * Fork - LGPL
15117  * <script type="text/javascript">
15118  */
15119  
15120
15121 /**
15122  * @class Roo.menu.BaseItem
15123  * @extends Roo.Component
15124  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15125  * management and base configuration options shared by all menu components.
15126  * @constructor
15127  * Creates a new BaseItem
15128  * @param {Object} config Configuration options
15129  */
15130 Roo.menu.BaseItem = function(config){
15131     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15132
15133     this.addEvents({
15134         /**
15135          * @event click
15136          * Fires when this item is clicked
15137          * @param {Roo.menu.BaseItem} this
15138          * @param {Roo.EventObject} e
15139          */
15140         click: true,
15141         /**
15142          * @event activate
15143          * Fires when this item is activated
15144          * @param {Roo.menu.BaseItem} this
15145          */
15146         activate : true,
15147         /**
15148          * @event deactivate
15149          * Fires when this item is deactivated
15150          * @param {Roo.menu.BaseItem} this
15151          */
15152         deactivate : true
15153     });
15154
15155     if(this.handler){
15156         this.on("click", this.handler, this.scope, true);
15157     }
15158 };
15159
15160 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15161     /**
15162      * @cfg {Function} handler
15163      * A function that will handle the click event of this menu item (defaults to undefined)
15164      */
15165     /**
15166      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15167      */
15168     canActivate : false,
15169     
15170      /**
15171      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15172      */
15173     hidden: false,
15174     
15175     /**
15176      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15177      */
15178     activeClass : "x-menu-item-active",
15179     /**
15180      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15181      */
15182     hideOnClick : true,
15183     /**
15184      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15185      */
15186     hideDelay : 100,
15187
15188     // private
15189     ctype: "Roo.menu.BaseItem",
15190
15191     // private
15192     actionMode : "container",
15193
15194     // private
15195     render : function(container, parentMenu){
15196         this.parentMenu = parentMenu;
15197         Roo.menu.BaseItem.superclass.render.call(this, container);
15198         this.container.menuItemId = this.id;
15199     },
15200
15201     // private
15202     onRender : function(container, position){
15203         this.el = Roo.get(this.el);
15204         container.dom.appendChild(this.el.dom);
15205     },
15206
15207     // private
15208     onClick : function(e){
15209         if(!this.disabled && this.fireEvent("click", this, e) !== false
15210                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15211             this.handleClick(e);
15212         }else{
15213             e.stopEvent();
15214         }
15215     },
15216
15217     // private
15218     activate : function(){
15219         if(this.disabled){
15220             return false;
15221         }
15222         var li = this.container;
15223         li.addClass(this.activeClass);
15224         this.region = li.getRegion().adjust(2, 2, -2, -2);
15225         this.fireEvent("activate", this);
15226         return true;
15227     },
15228
15229     // private
15230     deactivate : function(){
15231         this.container.removeClass(this.activeClass);
15232         this.fireEvent("deactivate", this);
15233     },
15234
15235     // private
15236     shouldDeactivate : function(e){
15237         return !this.region || !this.region.contains(e.getPoint());
15238     },
15239
15240     // private
15241     handleClick : function(e){
15242         if(this.hideOnClick){
15243             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15244         }
15245     },
15246
15247     // private
15248     expandMenu : function(autoActivate){
15249         // do nothing
15250     },
15251
15252     // private
15253     hideMenu : function(){
15254         // do nothing
15255     }
15256 });/*
15257  * Based on:
15258  * Ext JS Library 1.1.1
15259  * Copyright(c) 2006-2007, Ext JS, LLC.
15260  *
15261  * Originally Released Under LGPL - original licence link has changed is not relivant.
15262  *
15263  * Fork - LGPL
15264  * <script type="text/javascript">
15265  */
15266  
15267 /**
15268  * @class Roo.menu.Adapter
15269  * @extends Roo.menu.BaseItem
15270  * 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.
15271  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15272  * @constructor
15273  * Creates a new Adapter
15274  * @param {Object} config Configuration options
15275  */
15276 Roo.menu.Adapter = function(component, config){
15277     Roo.menu.Adapter.superclass.constructor.call(this, config);
15278     this.component = component;
15279 };
15280 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15281     // private
15282     canActivate : true,
15283
15284     // private
15285     onRender : function(container, position){
15286         this.component.render(container);
15287         this.el = this.component.getEl();
15288     },
15289
15290     // private
15291     activate : function(){
15292         if(this.disabled){
15293             return false;
15294         }
15295         this.component.focus();
15296         this.fireEvent("activate", this);
15297         return true;
15298     },
15299
15300     // private
15301     deactivate : function(){
15302         this.fireEvent("deactivate", this);
15303     },
15304
15305     // private
15306     disable : function(){
15307         this.component.disable();
15308         Roo.menu.Adapter.superclass.disable.call(this);
15309     },
15310
15311     // private
15312     enable : function(){
15313         this.component.enable();
15314         Roo.menu.Adapter.superclass.enable.call(this);
15315     }
15316 });/*
15317  * Based on:
15318  * Ext JS Library 1.1.1
15319  * Copyright(c) 2006-2007, Ext JS, LLC.
15320  *
15321  * Originally Released Under LGPL - original licence link has changed is not relivant.
15322  *
15323  * Fork - LGPL
15324  * <script type="text/javascript">
15325  */
15326
15327 /**
15328  * @class Roo.menu.TextItem
15329  * @extends Roo.menu.BaseItem
15330  * Adds a static text string to a menu, usually used as either a heading or group separator.
15331  * Note: old style constructor with text is still supported.
15332  * 
15333  * @constructor
15334  * Creates a new TextItem
15335  * @param {Object} cfg Configuration
15336  */
15337 Roo.menu.TextItem = function(cfg){
15338     if (typeof(cfg) == 'string') {
15339         this.text = cfg;
15340     } else {
15341         Roo.apply(this,cfg);
15342     }
15343     
15344     Roo.menu.TextItem.superclass.constructor.call(this);
15345 };
15346
15347 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15348     /**
15349      * @cfg {String} text Text to show on item.
15350      */
15351     text : '',
15352     
15353     /**
15354      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15355      */
15356     hideOnClick : false,
15357     /**
15358      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15359      */
15360     itemCls : "x-menu-text",
15361
15362     // private
15363     onRender : function(){
15364         var s = document.createElement("span");
15365         s.className = this.itemCls;
15366         s.innerHTML = this.text;
15367         this.el = s;
15368         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15369     }
15370 });/*
15371  * Based on:
15372  * Ext JS Library 1.1.1
15373  * Copyright(c) 2006-2007, Ext JS, LLC.
15374  *
15375  * Originally Released Under LGPL - original licence link has changed is not relivant.
15376  *
15377  * Fork - LGPL
15378  * <script type="text/javascript">
15379  */
15380
15381 /**
15382  * @class Roo.menu.Separator
15383  * @extends Roo.menu.BaseItem
15384  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15385  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15386  * @constructor
15387  * @param {Object} config Configuration options
15388  */
15389 Roo.menu.Separator = function(config){
15390     Roo.menu.Separator.superclass.constructor.call(this, config);
15391 };
15392
15393 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15394     /**
15395      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15396      */
15397     itemCls : "x-menu-sep",
15398     /**
15399      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15400      */
15401     hideOnClick : false,
15402
15403     // private
15404     onRender : function(li){
15405         var s = document.createElement("span");
15406         s.className = this.itemCls;
15407         s.innerHTML = "&#160;";
15408         this.el = s;
15409         li.addClass("x-menu-sep-li");
15410         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15411     }
15412 });/*
15413  * Based on:
15414  * Ext JS Library 1.1.1
15415  * Copyright(c) 2006-2007, Ext JS, LLC.
15416  *
15417  * Originally Released Under LGPL - original licence link has changed is not relivant.
15418  *
15419  * Fork - LGPL
15420  * <script type="text/javascript">
15421  */
15422 /**
15423  * @class Roo.menu.Item
15424  * @extends Roo.menu.BaseItem
15425  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15426  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15427  * activation and click handling.
15428  * @constructor
15429  * Creates a new Item
15430  * @param {Object} config Configuration options
15431  */
15432 Roo.menu.Item = function(config){
15433     Roo.menu.Item.superclass.constructor.call(this, config);
15434     if(this.menu){
15435         this.menu = Roo.menu.MenuMgr.get(this.menu);
15436     }
15437 };
15438 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15439     
15440     /**
15441      * @cfg {String} text
15442      * The text to show on the menu item.
15443      */
15444     text: '',
15445      /**
15446      * @cfg {String} HTML to render in menu
15447      * The text to show on the menu item (HTML version).
15448      */
15449     html: '',
15450     /**
15451      * @cfg {String} icon
15452      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15453      */
15454     icon: undefined,
15455     /**
15456      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15457      */
15458     itemCls : "x-menu-item",
15459     /**
15460      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15461      */
15462     canActivate : true,
15463     /**
15464      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15465      */
15466     showDelay: 200,
15467     // doc'd in BaseItem
15468     hideDelay: 200,
15469
15470     // private
15471     ctype: "Roo.menu.Item",
15472     
15473     // private
15474     onRender : function(container, position){
15475         var el = document.createElement("a");
15476         el.hideFocus = true;
15477         el.unselectable = "on";
15478         el.href = this.href || "#";
15479         if(this.hrefTarget){
15480             el.target = this.hrefTarget;
15481         }
15482         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15483         
15484         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15485         
15486         el.innerHTML = String.format(
15487                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15488                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15489         this.el = el;
15490         Roo.menu.Item.superclass.onRender.call(this, container, position);
15491     },
15492
15493     /**
15494      * Sets the text to display in this menu item
15495      * @param {String} text The text to display
15496      * @param {Boolean} isHTML true to indicate text is pure html.
15497      */
15498     setText : function(text, isHTML){
15499         if (isHTML) {
15500             this.html = text;
15501         } else {
15502             this.text = text;
15503             this.html = '';
15504         }
15505         if(this.rendered){
15506             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15507      
15508             this.el.update(String.format(
15509                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15510                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15511             this.parentMenu.autoWidth();
15512         }
15513     },
15514
15515     // private
15516     handleClick : function(e){
15517         if(!this.href){ // if no link defined, stop the event automatically
15518             e.stopEvent();
15519         }
15520         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15521     },
15522
15523     // private
15524     activate : function(autoExpand){
15525         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15526             this.focus();
15527             if(autoExpand){
15528                 this.expandMenu();
15529             }
15530         }
15531         return true;
15532     },
15533
15534     // private
15535     shouldDeactivate : function(e){
15536         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15537             if(this.menu && this.menu.isVisible()){
15538                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15539             }
15540             return true;
15541         }
15542         return false;
15543     },
15544
15545     // private
15546     deactivate : function(){
15547         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15548         this.hideMenu();
15549     },
15550
15551     // private
15552     expandMenu : function(autoActivate){
15553         if(!this.disabled && this.menu){
15554             clearTimeout(this.hideTimer);
15555             delete this.hideTimer;
15556             if(!this.menu.isVisible() && !this.showTimer){
15557                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15558             }else if (this.menu.isVisible() && autoActivate){
15559                 this.menu.tryActivate(0, 1);
15560             }
15561         }
15562     },
15563
15564     // private
15565     deferExpand : function(autoActivate){
15566         delete this.showTimer;
15567         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15568         if(autoActivate){
15569             this.menu.tryActivate(0, 1);
15570         }
15571     },
15572
15573     // private
15574     hideMenu : function(){
15575         clearTimeout(this.showTimer);
15576         delete this.showTimer;
15577         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15578             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15579         }
15580     },
15581
15582     // private
15583     deferHide : function(){
15584         delete this.hideTimer;
15585         this.menu.hide();
15586     }
15587 });/*
15588  * Based on:
15589  * Ext JS Library 1.1.1
15590  * Copyright(c) 2006-2007, Ext JS, LLC.
15591  *
15592  * Originally Released Under LGPL - original licence link has changed is not relivant.
15593  *
15594  * Fork - LGPL
15595  * <script type="text/javascript">
15596  */
15597  
15598 /**
15599  * @class Roo.menu.CheckItem
15600  * @extends Roo.menu.Item
15601  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15602  * @constructor
15603  * Creates a new CheckItem
15604  * @param {Object} config Configuration options
15605  */
15606 Roo.menu.CheckItem = function(config){
15607     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15608     this.addEvents({
15609         /**
15610          * @event beforecheckchange
15611          * Fires before the checked value is set, providing an opportunity to cancel if needed
15612          * @param {Roo.menu.CheckItem} this
15613          * @param {Boolean} checked The new checked value that will be set
15614          */
15615         "beforecheckchange" : true,
15616         /**
15617          * @event checkchange
15618          * Fires after the checked value has been set
15619          * @param {Roo.menu.CheckItem} this
15620          * @param {Boolean} checked The checked value that was set
15621          */
15622         "checkchange" : true
15623     });
15624     if(this.checkHandler){
15625         this.on('checkchange', this.checkHandler, this.scope);
15626     }
15627 };
15628 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15629     /**
15630      * @cfg {String} group
15631      * All check items with the same group name will automatically be grouped into a single-select
15632      * radio button group (defaults to '')
15633      */
15634     /**
15635      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15636      */
15637     itemCls : "x-menu-item x-menu-check-item",
15638     /**
15639      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15640      */
15641     groupClass : "x-menu-group-item",
15642
15643     /**
15644      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15645      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15646      * initialized with checked = true will be rendered as checked.
15647      */
15648     checked: false,
15649
15650     // private
15651     ctype: "Roo.menu.CheckItem",
15652
15653     // private
15654     onRender : function(c){
15655         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15656         if(this.group){
15657             this.el.addClass(this.groupClass);
15658         }
15659         Roo.menu.MenuMgr.registerCheckable(this);
15660         if(this.checked){
15661             this.checked = false;
15662             this.setChecked(true, true);
15663         }
15664     },
15665
15666     // private
15667     destroy : function(){
15668         if(this.rendered){
15669             Roo.menu.MenuMgr.unregisterCheckable(this);
15670         }
15671         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15672     },
15673
15674     /**
15675      * Set the checked state of this item
15676      * @param {Boolean} checked The new checked value
15677      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15678      */
15679     setChecked : function(state, suppressEvent){
15680         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15681             if(this.container){
15682                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15683             }
15684             this.checked = state;
15685             if(suppressEvent !== true){
15686                 this.fireEvent("checkchange", this, state);
15687             }
15688         }
15689     },
15690
15691     // private
15692     handleClick : function(e){
15693        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15694            this.setChecked(!this.checked);
15695        }
15696        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15697     }
15698 });/*
15699  * Based on:
15700  * Ext JS Library 1.1.1
15701  * Copyright(c) 2006-2007, Ext JS, LLC.
15702  *
15703  * Originally Released Under LGPL - original licence link has changed is not relivant.
15704  *
15705  * Fork - LGPL
15706  * <script type="text/javascript">
15707  */
15708  
15709 /**
15710  * @class Roo.menu.DateItem
15711  * @extends Roo.menu.Adapter
15712  * A menu item that wraps the {@link Roo.DatPicker} component.
15713  * @constructor
15714  * Creates a new DateItem
15715  * @param {Object} config Configuration options
15716  */
15717 Roo.menu.DateItem = function(config){
15718     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15719     /** The Roo.DatePicker object @type Roo.DatePicker */
15720     this.picker = this.component;
15721     this.addEvents({select: true});
15722     
15723     this.picker.on("render", function(picker){
15724         picker.getEl().swallowEvent("click");
15725         picker.container.addClass("x-menu-date-item");
15726     });
15727
15728     this.picker.on("select", this.onSelect, this);
15729 };
15730
15731 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15732     // private
15733     onSelect : function(picker, date){
15734         this.fireEvent("select", this, date, picker);
15735         Roo.menu.DateItem.superclass.handleClick.call(this);
15736     }
15737 });/*
15738  * Based on:
15739  * Ext JS Library 1.1.1
15740  * Copyright(c) 2006-2007, Ext JS, LLC.
15741  *
15742  * Originally Released Under LGPL - original licence link has changed is not relivant.
15743  *
15744  * Fork - LGPL
15745  * <script type="text/javascript">
15746  */
15747  
15748 /**
15749  * @class Roo.menu.ColorItem
15750  * @extends Roo.menu.Adapter
15751  * A menu item that wraps the {@link Roo.ColorPalette} component.
15752  * @constructor
15753  * Creates a new ColorItem
15754  * @param {Object} config Configuration options
15755  */
15756 Roo.menu.ColorItem = function(config){
15757     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15758     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15759     this.palette = this.component;
15760     this.relayEvents(this.palette, ["select"]);
15761     if(this.selectHandler){
15762         this.on('select', this.selectHandler, this.scope);
15763     }
15764 };
15765 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15766  * Based on:
15767  * Ext JS Library 1.1.1
15768  * Copyright(c) 2006-2007, Ext JS, LLC.
15769  *
15770  * Originally Released Under LGPL - original licence link has changed is not relivant.
15771  *
15772  * Fork - LGPL
15773  * <script type="text/javascript">
15774  */
15775  
15776
15777 /**
15778  * @class Roo.menu.DateMenu
15779  * @extends Roo.menu.Menu
15780  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15781  * @constructor
15782  * Creates a new DateMenu
15783  * @param {Object} config Configuration options
15784  */
15785 Roo.menu.DateMenu = function(config){
15786     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15787     this.plain = true;
15788     var di = new Roo.menu.DateItem(config);
15789     this.add(di);
15790     /**
15791      * The {@link Roo.DatePicker} instance for this DateMenu
15792      * @type DatePicker
15793      */
15794     this.picker = di.picker;
15795     /**
15796      * @event select
15797      * @param {DatePicker} picker
15798      * @param {Date} date
15799      */
15800     this.relayEvents(di, ["select"]);
15801     this.on('beforeshow', function(){
15802         if(this.picker){
15803             this.picker.hideMonthPicker(false);
15804         }
15805     }, this);
15806 };
15807 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15808     cls:'x-date-menu'
15809 });/*
15810  * Based on:
15811  * Ext JS Library 1.1.1
15812  * Copyright(c) 2006-2007, Ext JS, LLC.
15813  *
15814  * Originally Released Under LGPL - original licence link has changed is not relivant.
15815  *
15816  * Fork - LGPL
15817  * <script type="text/javascript">
15818  */
15819  
15820
15821 /**
15822  * @class Roo.menu.ColorMenu
15823  * @extends Roo.menu.Menu
15824  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15825  * @constructor
15826  * Creates a new ColorMenu
15827  * @param {Object} config Configuration options
15828  */
15829 Roo.menu.ColorMenu = function(config){
15830     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15831     this.plain = true;
15832     var ci = new Roo.menu.ColorItem(config);
15833     this.add(ci);
15834     /**
15835      * The {@link Roo.ColorPalette} instance for this ColorMenu
15836      * @type ColorPalette
15837      */
15838     this.palette = ci.palette;
15839     /**
15840      * @event select
15841      * @param {ColorPalette} palette
15842      * @param {String} color
15843      */
15844     this.relayEvents(ci, ["select"]);
15845 };
15846 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15847  * Based on:
15848  * Ext JS Library 1.1.1
15849  * Copyright(c) 2006-2007, Ext JS, LLC.
15850  *
15851  * Originally Released Under LGPL - original licence link has changed is not relivant.
15852  *
15853  * Fork - LGPL
15854  * <script type="text/javascript">
15855  */
15856  
15857 /**
15858  * @class Roo.form.TextItem
15859  * @extends Roo.BoxComponent
15860  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15861  * @constructor
15862  * Creates a new TextItem
15863  * @param {Object} config Configuration options
15864  */
15865 Roo.form.TextItem = function(config){
15866     Roo.form.TextItem.superclass.constructor.call(this, config);
15867 };
15868
15869 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15870     
15871     /**
15872      * @cfg {String} tag the tag for this item (default div)
15873      */
15874     tag : 'div',
15875     /**
15876      * @cfg {String} html the content for this item
15877      */
15878     html : '',
15879     
15880     getAutoCreate : function()
15881     {
15882         var cfg = {
15883             id: this.id,
15884             tag: this.tag,
15885             html: this.html,
15886             cls: 'x-form-item'
15887         };
15888         
15889         return cfg;
15890         
15891     },
15892     
15893     onRender : function(ct, position)
15894     {
15895         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15896         
15897         if(!this.el){
15898             var cfg = this.getAutoCreate();
15899             if(!cfg.name){
15900                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15901             }
15902             if (!cfg.name.length) {
15903                 delete cfg.name;
15904             }
15905             this.el = ct.createChild(cfg, position);
15906         }
15907     },
15908     /*
15909      * setHTML
15910      * @param {String} html update the Contents of the element.
15911      */
15912     setHTML : function(html)
15913     {
15914         this.fieldEl.dom.innerHTML = html;
15915     }
15916     
15917 });/*
15918  * Based on:
15919  * Ext JS Library 1.1.1
15920  * Copyright(c) 2006-2007, Ext JS, LLC.
15921  *
15922  * Originally Released Under LGPL - original licence link has changed is not relivant.
15923  *
15924  * Fork - LGPL
15925  * <script type="text/javascript">
15926  */
15927  
15928 /**
15929  * @class Roo.form.Field
15930  * @extends Roo.BoxComponent
15931  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15932  * @constructor
15933  * Creates a new Field
15934  * @param {Object} config Configuration options
15935  */
15936 Roo.form.Field = function(config){
15937     Roo.form.Field.superclass.constructor.call(this, config);
15938 };
15939
15940 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
15941     /**
15942      * @cfg {String} fieldLabel Label to use when rendering a form.
15943      */
15944        /**
15945      * @cfg {String} qtip Mouse over tip
15946      */
15947      
15948     /**
15949      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
15950      */
15951     invalidClass : "x-form-invalid",
15952     /**
15953      * @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")
15954      */
15955     invalidText : "The value in this field is invalid",
15956     /**
15957      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
15958      */
15959     focusClass : "x-form-focus",
15960     /**
15961      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
15962       automatic validation (defaults to "keyup").
15963      */
15964     validationEvent : "keyup",
15965     /**
15966      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
15967      */
15968     validateOnBlur : true,
15969     /**
15970      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
15971      */
15972     validationDelay : 250,
15973     /**
15974      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
15975      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
15976      */
15977     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
15978     /**
15979      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
15980      */
15981     fieldClass : "x-form-field",
15982     /**
15983      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
15984      *<pre>
15985 Value         Description
15986 -----------   ----------------------------------------------------------------------
15987 qtip          Display a quick tip when the user hovers over the field
15988 title         Display a default browser title attribute popup
15989 under         Add a block div beneath the field containing the error text
15990 side          Add an error icon to the right of the field with a popup on hover
15991 [element id]  Add the error text directly to the innerHTML of the specified element
15992 </pre>
15993      */
15994     msgTarget : 'qtip',
15995     /**
15996      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
15997      */
15998     msgFx : 'normal',
15999
16000     /**
16001      * @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.
16002      */
16003     readOnly : false,
16004
16005     /**
16006      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16007      */
16008     disabled : false,
16009
16010     /**
16011      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16012      */
16013     inputType : undefined,
16014     
16015     /**
16016      * @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).
16017          */
16018         tabIndex : undefined,
16019         
16020     // private
16021     isFormField : true,
16022
16023     // private
16024     hasFocus : false,
16025     /**
16026      * @property {Roo.Element} fieldEl
16027      * Element Containing the rendered Field (with label etc.)
16028      */
16029     /**
16030      * @cfg {Mixed} value A value to initialize this field with.
16031      */
16032     value : undefined,
16033
16034     /**
16035      * @cfg {String} name The field's HTML name attribute.
16036      */
16037     /**
16038      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16039      */
16040     // private
16041     loadedValue : false,
16042      
16043      
16044         // private ??
16045         initComponent : function(){
16046         Roo.form.Field.superclass.initComponent.call(this);
16047         this.addEvents({
16048             /**
16049              * @event focus
16050              * Fires when this field receives input focus.
16051              * @param {Roo.form.Field} this
16052              */
16053             focus : true,
16054             /**
16055              * @event blur
16056              * Fires when this field loses input focus.
16057              * @param {Roo.form.Field} this
16058              */
16059             blur : true,
16060             /**
16061              * @event specialkey
16062              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16063              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16064              * @param {Roo.form.Field} this
16065              * @param {Roo.EventObject} e The event object
16066              */
16067             specialkey : true,
16068             /**
16069              * @event change
16070              * Fires just before the field blurs if the field value has changed.
16071              * @param {Roo.form.Field} this
16072              * @param {Mixed} newValue The new value
16073              * @param {Mixed} oldValue The original value
16074              */
16075             change : true,
16076             /**
16077              * @event invalid
16078              * Fires after the field has been marked as invalid.
16079              * @param {Roo.form.Field} this
16080              * @param {String} msg The validation message
16081              */
16082             invalid : true,
16083             /**
16084              * @event valid
16085              * Fires after the field has been validated with no errors.
16086              * @param {Roo.form.Field} this
16087              */
16088             valid : true,
16089              /**
16090              * @event keyup
16091              * Fires after the key up
16092              * @param {Roo.form.Field} this
16093              * @param {Roo.EventObject}  e The event Object
16094              */
16095             keyup : true
16096         });
16097     },
16098
16099     /**
16100      * Returns the name attribute of the field if available
16101      * @return {String} name The field name
16102      */
16103     getName: function(){
16104          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16105     },
16106
16107     // private
16108     onRender : function(ct, position){
16109         Roo.form.Field.superclass.onRender.call(this, ct, position);
16110         if(!this.el){
16111             var cfg = this.getAutoCreate();
16112             if(!cfg.name){
16113                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16114             }
16115             if (!cfg.name.length) {
16116                 delete cfg.name;
16117             }
16118             if(this.inputType){
16119                 cfg.type = this.inputType;
16120             }
16121             this.el = ct.createChild(cfg, position);
16122         }
16123         var type = this.el.dom.type;
16124         if(type){
16125             if(type == 'password'){
16126                 type = 'text';
16127             }
16128             this.el.addClass('x-form-'+type);
16129         }
16130         if(this.readOnly){
16131             this.el.dom.readOnly = true;
16132         }
16133         if(this.tabIndex !== undefined){
16134             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16135         }
16136
16137         this.el.addClass([this.fieldClass, this.cls]);
16138         this.initValue();
16139     },
16140
16141     /**
16142      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16143      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16144      * @return {Roo.form.Field} this
16145      */
16146     applyTo : function(target){
16147         this.allowDomMove = false;
16148         this.el = Roo.get(target);
16149         this.render(this.el.dom.parentNode);
16150         return this;
16151     },
16152
16153     // private
16154     initValue : function(){
16155         if(this.value !== undefined){
16156             this.setValue(this.value);
16157         }else if(this.el.dom.value.length > 0){
16158             this.setValue(this.el.dom.value);
16159         }
16160     },
16161
16162     /**
16163      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16164      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16165      */
16166     isDirty : function() {
16167         if(this.disabled) {
16168             return false;
16169         }
16170         return String(this.getValue()) !== String(this.originalValue);
16171     },
16172
16173     /**
16174      * stores the current value in loadedValue
16175      */
16176     resetHasChanged : function()
16177     {
16178         this.loadedValue = String(this.getValue());
16179     },
16180     /**
16181      * checks the current value against the 'loaded' value.
16182      * Note - will return false if 'resetHasChanged' has not been called first.
16183      */
16184     hasChanged : function()
16185     {
16186         if(this.disabled || this.readOnly) {
16187             return false;
16188         }
16189         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16190     },
16191     
16192     
16193     
16194     // private
16195     afterRender : function(){
16196         Roo.form.Field.superclass.afterRender.call(this);
16197         this.initEvents();
16198     },
16199
16200     // private
16201     fireKey : function(e){
16202         //Roo.log('field ' + e.getKey());
16203         if(e.isNavKeyPress()){
16204             this.fireEvent("specialkey", this, e);
16205         }
16206     },
16207
16208     /**
16209      * Resets the current field value to the originally loaded value and clears any validation messages
16210      */
16211     reset : function(){
16212         this.setValue(this.resetValue);
16213         this.originalValue = this.getValue();
16214         this.clearInvalid();
16215     },
16216
16217     // private
16218     initEvents : function(){
16219         // safari killled keypress - so keydown is now used..
16220         this.el.on("keydown" , this.fireKey,  this);
16221         this.el.on("focus", this.onFocus,  this);
16222         this.el.on("blur", this.onBlur,  this);
16223         this.el.relayEvent('keyup', this);
16224
16225         // reference to original value for reset
16226         this.originalValue = this.getValue();
16227         this.resetValue =  this.getValue();
16228     },
16229
16230     // private
16231     onFocus : function(){
16232         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16233             this.el.addClass(this.focusClass);
16234         }
16235         if(!this.hasFocus){
16236             this.hasFocus = true;
16237             this.startValue = this.getValue();
16238             this.fireEvent("focus", this);
16239         }
16240     },
16241
16242     beforeBlur : Roo.emptyFn,
16243
16244     // private
16245     onBlur : function(){
16246         this.beforeBlur();
16247         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16248             this.el.removeClass(this.focusClass);
16249         }
16250         this.hasFocus = false;
16251         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16252             this.validate();
16253         }
16254         var v = this.getValue();
16255         if(String(v) !== String(this.startValue)){
16256             this.fireEvent('change', this, v, this.startValue);
16257         }
16258         this.fireEvent("blur", this);
16259     },
16260
16261     /**
16262      * Returns whether or not the field value is currently valid
16263      * @param {Boolean} preventMark True to disable marking the field invalid
16264      * @return {Boolean} True if the value is valid, else false
16265      */
16266     isValid : function(preventMark){
16267         if(this.disabled){
16268             return true;
16269         }
16270         var restore = this.preventMark;
16271         this.preventMark = preventMark === true;
16272         var v = this.validateValue(this.processValue(this.getRawValue()));
16273         this.preventMark = restore;
16274         return v;
16275     },
16276
16277     /**
16278      * Validates the field value
16279      * @return {Boolean} True if the value is valid, else false
16280      */
16281     validate : function(){
16282         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16283             this.clearInvalid();
16284             return true;
16285         }
16286         return false;
16287     },
16288
16289     processValue : function(value){
16290         return value;
16291     },
16292
16293     // private
16294     // Subclasses should provide the validation implementation by overriding this
16295     validateValue : function(value){
16296         return true;
16297     },
16298
16299     /**
16300      * Mark this field as invalid
16301      * @param {String} msg The validation message
16302      */
16303     markInvalid : function(msg){
16304         if(!this.rendered || this.preventMark){ // not rendered
16305             return;
16306         }
16307         
16308         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16309         
16310         obj.el.addClass(this.invalidClass);
16311         msg = msg || this.invalidText;
16312         switch(this.msgTarget){
16313             case 'qtip':
16314                 obj.el.dom.qtip = msg;
16315                 obj.el.dom.qclass = 'x-form-invalid-tip';
16316                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16317                     Roo.QuickTips.enable();
16318                 }
16319                 break;
16320             case 'title':
16321                 this.el.dom.title = msg;
16322                 break;
16323             case 'under':
16324                 if(!this.errorEl){
16325                     var elp = this.el.findParent('.x-form-element', 5, true);
16326                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16327                     this.errorEl.setWidth(elp.getWidth(true)-20);
16328                 }
16329                 this.errorEl.update(msg);
16330                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16331                 break;
16332             case 'side':
16333                 if(!this.errorIcon){
16334                     var elp = this.el.findParent('.x-form-element', 5, true);
16335                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16336                 }
16337                 this.alignErrorIcon();
16338                 this.errorIcon.dom.qtip = msg;
16339                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16340                 this.errorIcon.show();
16341                 this.on('resize', this.alignErrorIcon, this);
16342                 break;
16343             default:
16344                 var t = Roo.getDom(this.msgTarget);
16345                 t.innerHTML = msg;
16346                 t.style.display = this.msgDisplay;
16347                 break;
16348         }
16349         this.fireEvent('invalid', this, msg);
16350     },
16351
16352     // private
16353     alignErrorIcon : function(){
16354         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16355     },
16356
16357     /**
16358      * Clear any invalid styles/messages for this field
16359      */
16360     clearInvalid : function(){
16361         if(!this.rendered || this.preventMark){ // not rendered
16362             return;
16363         }
16364         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16365         
16366         obj.el.removeClass(this.invalidClass);
16367         switch(this.msgTarget){
16368             case 'qtip':
16369                 obj.el.dom.qtip = '';
16370                 break;
16371             case 'title':
16372                 this.el.dom.title = '';
16373                 break;
16374             case 'under':
16375                 if(this.errorEl){
16376                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16377                 }
16378                 break;
16379             case 'side':
16380                 if(this.errorIcon){
16381                     this.errorIcon.dom.qtip = '';
16382                     this.errorIcon.hide();
16383                     this.un('resize', this.alignErrorIcon, this);
16384                 }
16385                 break;
16386             default:
16387                 var t = Roo.getDom(this.msgTarget);
16388                 t.innerHTML = '';
16389                 t.style.display = 'none';
16390                 break;
16391         }
16392         this.fireEvent('valid', this);
16393     },
16394
16395     /**
16396      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16397      * @return {Mixed} value The field value
16398      */
16399     getRawValue : function(){
16400         var v = this.el.getValue();
16401         
16402         return v;
16403     },
16404
16405     /**
16406      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16407      * @return {Mixed} value The field value
16408      */
16409     getValue : function(){
16410         var v = this.el.getValue();
16411          
16412         return v;
16413     },
16414
16415     /**
16416      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16417      * @param {Mixed} value The value to set
16418      */
16419     setRawValue : function(v){
16420         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16421     },
16422
16423     /**
16424      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16425      * @param {Mixed} value The value to set
16426      */
16427     setValue : function(v){
16428         this.value = v;
16429         if(this.rendered){
16430             this.el.dom.value = (v === null || v === undefined ? '' : v);
16431              this.validate();
16432         }
16433     },
16434
16435     adjustSize : function(w, h){
16436         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16437         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16438         return s;
16439     },
16440
16441     adjustWidth : function(tag, w){
16442         tag = tag.toLowerCase();
16443         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16444             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16445                 if(tag == 'input'){
16446                     return w + 2;
16447                 }
16448                 if(tag == 'textarea'){
16449                     return w-2;
16450                 }
16451             }else if(Roo.isOpera){
16452                 if(tag == 'input'){
16453                     return w + 2;
16454                 }
16455                 if(tag == 'textarea'){
16456                     return w-2;
16457                 }
16458             }
16459         }
16460         return w;
16461     }
16462 });
16463
16464
16465 // anything other than normal should be considered experimental
16466 Roo.form.Field.msgFx = {
16467     normal : {
16468         show: function(msgEl, f){
16469             msgEl.setDisplayed('block');
16470         },
16471
16472         hide : function(msgEl, f){
16473             msgEl.setDisplayed(false).update('');
16474         }
16475     },
16476
16477     slide : {
16478         show: function(msgEl, f){
16479             msgEl.slideIn('t', {stopFx:true});
16480         },
16481
16482         hide : function(msgEl, f){
16483             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16484         }
16485     },
16486
16487     slideRight : {
16488         show: function(msgEl, f){
16489             msgEl.fixDisplay();
16490             msgEl.alignTo(f.el, 'tl-tr');
16491             msgEl.slideIn('l', {stopFx:true});
16492         },
16493
16494         hide : function(msgEl, f){
16495             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16496         }
16497     }
16498 };/*
16499  * Based on:
16500  * Ext JS Library 1.1.1
16501  * Copyright(c) 2006-2007, Ext JS, LLC.
16502  *
16503  * Originally Released Under LGPL - original licence link has changed is not relivant.
16504  *
16505  * Fork - LGPL
16506  * <script type="text/javascript">
16507  */
16508  
16509
16510 /**
16511  * @class Roo.form.TextField
16512  * @extends Roo.form.Field
16513  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16514  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16515  * @constructor
16516  * Creates a new TextField
16517  * @param {Object} config Configuration options
16518  */
16519 Roo.form.TextField = function(config){
16520     Roo.form.TextField.superclass.constructor.call(this, config);
16521     this.addEvents({
16522         /**
16523          * @event autosize
16524          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16525          * according to the default logic, but this event provides a hook for the developer to apply additional
16526          * logic at runtime to resize the field if needed.
16527              * @param {Roo.form.Field} this This text field
16528              * @param {Number} width The new field width
16529              */
16530         autosize : true
16531     });
16532 };
16533
16534 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16535     /**
16536      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16537      */
16538     grow : false,
16539     /**
16540      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16541      */
16542     growMin : 30,
16543     /**
16544      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16545      */
16546     growMax : 800,
16547     /**
16548      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16549      */
16550     vtype : null,
16551     /**
16552      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16553      */
16554     maskRe : null,
16555     /**
16556      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16557      */
16558     disableKeyFilter : false,
16559     /**
16560      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16561      */
16562     allowBlank : true,
16563     /**
16564      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16565      */
16566     minLength : 0,
16567     /**
16568      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16569      */
16570     maxLength : Number.MAX_VALUE,
16571     /**
16572      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16573      */
16574     minLengthText : "The minimum length for this field is {0}",
16575     /**
16576      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16577      */
16578     maxLengthText : "The maximum length for this field is {0}",
16579     /**
16580      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16581      */
16582     selectOnFocus : false,
16583     /**
16584      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16585      */    
16586     allowLeadingSpace : false,
16587     /**
16588      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16589      */
16590     blankText : "This field is required",
16591     /**
16592      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16593      * If available, this function will be called only after the basic validators all return true, and will be passed the
16594      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16595      */
16596     validator : null,
16597     /**
16598      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16599      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16600      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16601      */
16602     regex : null,
16603     /**
16604      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16605      */
16606     regexText : "",
16607     /**
16608      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16609      */
16610     emptyText : null,
16611    
16612
16613     // private
16614     initEvents : function()
16615     {
16616         if (this.emptyText) {
16617             this.el.attr('placeholder', this.emptyText);
16618         }
16619         
16620         Roo.form.TextField.superclass.initEvents.call(this);
16621         if(this.validationEvent == 'keyup'){
16622             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16623             this.el.on('keyup', this.filterValidation, this);
16624         }
16625         else if(this.validationEvent !== false){
16626             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16627         }
16628         
16629         if(this.selectOnFocus){
16630             this.on("focus", this.preFocus, this);
16631         }
16632         if (!this.allowLeadingSpace) {
16633             this.on('blur', this.cleanLeadingSpace, this);
16634         }
16635         
16636         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16637             this.el.on("keypress", this.filterKeys, this);
16638         }
16639         if(this.grow){
16640             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16641             this.el.on("click", this.autoSize,  this);
16642         }
16643         if(this.el.is('input[type=password]') && Roo.isSafari){
16644             this.el.on('keydown', this.SafariOnKeyDown, this);
16645         }
16646     },
16647
16648     processValue : function(value){
16649         if(this.stripCharsRe){
16650             var newValue = value.replace(this.stripCharsRe, '');
16651             if(newValue !== value){
16652                 this.setRawValue(newValue);
16653                 return newValue;
16654             }
16655         }
16656         return value;
16657     },
16658
16659     filterValidation : function(e){
16660         if(!e.isNavKeyPress()){
16661             this.validationTask.delay(this.validationDelay);
16662         }
16663     },
16664
16665     // private
16666     onKeyUp : function(e){
16667         if(!e.isNavKeyPress()){
16668             this.autoSize();
16669         }
16670     },
16671     // private - clean the leading white space
16672     cleanLeadingSpace : function(e)
16673     {
16674         if ( this.inputType == 'file') {
16675             return;
16676         }
16677         
16678         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16679     },
16680     /**
16681      * Resets the current field value to the originally-loaded value and clears any validation messages.
16682      *  
16683      */
16684     reset : function(){
16685         Roo.form.TextField.superclass.reset.call(this);
16686        
16687     }, 
16688     // private
16689     preFocus : function(){
16690         
16691         if(this.selectOnFocus){
16692             this.el.dom.select();
16693         }
16694     },
16695
16696     
16697     // private
16698     filterKeys : function(e){
16699         var k = e.getKey();
16700         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16701             return;
16702         }
16703         var c = e.getCharCode(), cc = String.fromCharCode(c);
16704         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16705             return;
16706         }
16707         if(!this.maskRe.test(cc)){
16708             e.stopEvent();
16709         }
16710     },
16711
16712     setValue : function(v){
16713         
16714         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16715         
16716         this.autoSize();
16717     },
16718
16719     /**
16720      * Validates a value according to the field's validation rules and marks the field as invalid
16721      * if the validation fails
16722      * @param {Mixed} value The value to validate
16723      * @return {Boolean} True if the value is valid, else false
16724      */
16725     validateValue : function(value){
16726         if(value.length < 1)  { // if it's blank
16727              if(this.allowBlank){
16728                 this.clearInvalid();
16729                 return true;
16730              }else{
16731                 this.markInvalid(this.blankText);
16732                 return false;
16733              }
16734         }
16735         if(value.length < this.minLength){
16736             this.markInvalid(String.format(this.minLengthText, this.minLength));
16737             return false;
16738         }
16739         if(value.length > this.maxLength){
16740             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16741             return false;
16742         }
16743         if(this.vtype){
16744             var vt = Roo.form.VTypes;
16745             if(!vt[this.vtype](value, this)){
16746                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16747                 return false;
16748             }
16749         }
16750         if(typeof this.validator == "function"){
16751             var msg = this.validator(value);
16752             if(msg !== true){
16753                 this.markInvalid(msg);
16754                 return false;
16755             }
16756         }
16757         if(this.regex && !this.regex.test(value)){
16758             this.markInvalid(this.regexText);
16759             return false;
16760         }
16761         return true;
16762     },
16763
16764     /**
16765      * Selects text in this field
16766      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16767      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16768      */
16769     selectText : function(start, end){
16770         var v = this.getRawValue();
16771         if(v.length > 0){
16772             start = start === undefined ? 0 : start;
16773             end = end === undefined ? v.length : end;
16774             var d = this.el.dom;
16775             if(d.setSelectionRange){
16776                 d.setSelectionRange(start, end);
16777             }else if(d.createTextRange){
16778                 var range = d.createTextRange();
16779                 range.moveStart("character", start);
16780                 range.moveEnd("character", v.length-end);
16781                 range.select();
16782             }
16783         }
16784     },
16785
16786     /**
16787      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16788      * This only takes effect if grow = true, and fires the autosize event.
16789      */
16790     autoSize : function(){
16791         if(!this.grow || !this.rendered){
16792             return;
16793         }
16794         if(!this.metrics){
16795             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16796         }
16797         var el = this.el;
16798         var v = el.dom.value;
16799         var d = document.createElement('div');
16800         d.appendChild(document.createTextNode(v));
16801         v = d.innerHTML;
16802         d = null;
16803         v += "&#160;";
16804         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16805         this.el.setWidth(w);
16806         this.fireEvent("autosize", this, w);
16807     },
16808     
16809     // private
16810     SafariOnKeyDown : function(event)
16811     {
16812         // this is a workaround for a password hang bug on chrome/ webkit.
16813         
16814         var isSelectAll = false;
16815         
16816         if(this.el.dom.selectionEnd > 0){
16817             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16818         }
16819         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16820             event.preventDefault();
16821             this.setValue('');
16822             return;
16823         }
16824         
16825         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16826             
16827             event.preventDefault();
16828             // this is very hacky as keydown always get's upper case.
16829             
16830             var cc = String.fromCharCode(event.getCharCode());
16831             
16832             
16833             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16834             
16835         }
16836         
16837         
16838     }
16839 });/*
16840  * Based on:
16841  * Ext JS Library 1.1.1
16842  * Copyright(c) 2006-2007, Ext JS, LLC.
16843  *
16844  * Originally Released Under LGPL - original licence link has changed is not relivant.
16845  *
16846  * Fork - LGPL
16847  * <script type="text/javascript">
16848  */
16849  
16850 /**
16851  * @class Roo.form.Hidden
16852  * @extends Roo.form.TextField
16853  * Simple Hidden element used on forms 
16854  * 
16855  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16856  * 
16857  * @constructor
16858  * Creates a new Hidden form element.
16859  * @param {Object} config Configuration options
16860  */
16861
16862
16863
16864 // easy hidden field...
16865 Roo.form.Hidden = function(config){
16866     Roo.form.Hidden.superclass.constructor.call(this, config);
16867 };
16868   
16869 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16870     fieldLabel:      '',
16871     inputType:      'hidden',
16872     width:          50,
16873     allowBlank:     true,
16874     labelSeparator: '',
16875     hidden:         true,
16876     itemCls :       'x-form-item-display-none'
16877
16878
16879 });
16880
16881
16882 /*
16883  * Based on:
16884  * Ext JS Library 1.1.1
16885  * Copyright(c) 2006-2007, Ext JS, LLC.
16886  *
16887  * Originally Released Under LGPL - original licence link has changed is not relivant.
16888  *
16889  * Fork - LGPL
16890  * <script type="text/javascript">
16891  */
16892  
16893 /**
16894  * @class Roo.form.TriggerField
16895  * @extends Roo.form.TextField
16896  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16897  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16898  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16899  * for which you can provide a custom implementation.  For example:
16900  * <pre><code>
16901 var trigger = new Roo.form.TriggerField();
16902 trigger.onTriggerClick = myTriggerFn;
16903 trigger.applyTo('my-field');
16904 </code></pre>
16905  *
16906  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16907  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16908  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16909  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16910  * @constructor
16911  * Create a new TriggerField.
16912  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16913  * to the base TextField)
16914  */
16915 Roo.form.TriggerField = function(config){
16916     this.mimicing = false;
16917     Roo.form.TriggerField.superclass.constructor.call(this, config);
16918 };
16919
16920 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16921     /**
16922      * @cfg {String} triggerClass A CSS class to apply to the trigger
16923      */
16924     /**
16925      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16926      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16927      */
16928     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16929     /**
16930      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16931      */
16932     hideTrigger:false,
16933
16934     /** @cfg {Boolean} grow @hide */
16935     /** @cfg {Number} growMin @hide */
16936     /** @cfg {Number} growMax @hide */
16937
16938     /**
16939      * @hide 
16940      * @method
16941      */
16942     autoSize: Roo.emptyFn,
16943     // private
16944     monitorTab : true,
16945     // private
16946     deferHeight : true,
16947
16948     
16949     actionMode : 'wrap',
16950     // private
16951     onResize : function(w, h){
16952         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
16953         if(typeof w == 'number'){
16954             var x = w - this.trigger.getWidth();
16955             this.el.setWidth(this.adjustWidth('input', x));
16956             this.trigger.setStyle('left', x+'px');
16957         }
16958     },
16959
16960     // private
16961     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16962
16963     // private
16964     getResizeEl : function(){
16965         return this.wrap;
16966     },
16967
16968     // private
16969     getPositionEl : function(){
16970         return this.wrap;
16971     },
16972
16973     // private
16974     alignErrorIcon : function(){
16975         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
16976     },
16977
16978     // private
16979     onRender : function(ct, position){
16980         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
16981         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
16982         this.trigger = this.wrap.createChild(this.triggerConfig ||
16983                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
16984         if(this.hideTrigger){
16985             this.trigger.setDisplayed(false);
16986         }
16987         this.initTrigger();
16988         if(!this.width){
16989             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
16990         }
16991     },
16992
16993     // private
16994     initTrigger : function(){
16995         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
16996         this.trigger.addClassOnOver('x-form-trigger-over');
16997         this.trigger.addClassOnClick('x-form-trigger-click');
16998     },
16999
17000     // private
17001     onDestroy : function(){
17002         if(this.trigger){
17003             this.trigger.removeAllListeners();
17004             this.trigger.remove();
17005         }
17006         if(this.wrap){
17007             this.wrap.remove();
17008         }
17009         Roo.form.TriggerField.superclass.onDestroy.call(this);
17010     },
17011
17012     // private
17013     onFocus : function(){
17014         Roo.form.TriggerField.superclass.onFocus.call(this);
17015         if(!this.mimicing){
17016             this.wrap.addClass('x-trigger-wrap-focus');
17017             this.mimicing = true;
17018             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17019             if(this.monitorTab){
17020                 this.el.on("keydown", this.checkTab, this);
17021             }
17022         }
17023     },
17024
17025     // private
17026     checkTab : function(e){
17027         if(e.getKey() == e.TAB){
17028             this.triggerBlur();
17029         }
17030     },
17031
17032     // private
17033     onBlur : function(){
17034         // do nothing
17035     },
17036
17037     // private
17038     mimicBlur : function(e, t){
17039         if(!this.wrap.contains(t) && this.validateBlur()){
17040             this.triggerBlur();
17041         }
17042     },
17043
17044     // private
17045     triggerBlur : function(){
17046         this.mimicing = false;
17047         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17048         if(this.monitorTab){
17049             this.el.un("keydown", this.checkTab, this);
17050         }
17051         this.wrap.removeClass('x-trigger-wrap-focus');
17052         Roo.form.TriggerField.superclass.onBlur.call(this);
17053     },
17054
17055     // private
17056     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17057     validateBlur : function(e, t){
17058         return true;
17059     },
17060
17061     // private
17062     onDisable : function(){
17063         Roo.form.TriggerField.superclass.onDisable.call(this);
17064         if(this.wrap){
17065             this.wrap.addClass('x-item-disabled');
17066         }
17067     },
17068
17069     // private
17070     onEnable : function(){
17071         Roo.form.TriggerField.superclass.onEnable.call(this);
17072         if(this.wrap){
17073             this.wrap.removeClass('x-item-disabled');
17074         }
17075     },
17076
17077     // private
17078     onShow : function(){
17079         var ae = this.getActionEl();
17080         
17081         if(ae){
17082             ae.dom.style.display = '';
17083             ae.dom.style.visibility = 'visible';
17084         }
17085     },
17086
17087     // private
17088     
17089     onHide : function(){
17090         var ae = this.getActionEl();
17091         ae.dom.style.display = 'none';
17092     },
17093
17094     /**
17095      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17096      * by an implementing function.
17097      * @method
17098      * @param {EventObject} e
17099      */
17100     onTriggerClick : Roo.emptyFn
17101 });
17102
17103 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17104 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17105 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17106 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17107     initComponent : function(){
17108         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17109
17110         this.triggerConfig = {
17111             tag:'span', cls:'x-form-twin-triggers', cn:[
17112             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17113             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17114         ]};
17115     },
17116
17117     getTrigger : function(index){
17118         return this.triggers[index];
17119     },
17120
17121     initTrigger : function(){
17122         var ts = this.trigger.select('.x-form-trigger', true);
17123         this.wrap.setStyle('overflow', 'hidden');
17124         var triggerField = this;
17125         ts.each(function(t, all, index){
17126             t.hide = function(){
17127                 var w = triggerField.wrap.getWidth();
17128                 this.dom.style.display = 'none';
17129                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17130             };
17131             t.show = function(){
17132                 var w = triggerField.wrap.getWidth();
17133                 this.dom.style.display = '';
17134                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17135             };
17136             var triggerIndex = 'Trigger'+(index+1);
17137
17138             if(this['hide'+triggerIndex]){
17139                 t.dom.style.display = 'none';
17140             }
17141             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17142             t.addClassOnOver('x-form-trigger-over');
17143             t.addClassOnClick('x-form-trigger-click');
17144         }, this);
17145         this.triggers = ts.elements;
17146     },
17147
17148     onTrigger1Click : Roo.emptyFn,
17149     onTrigger2Click : Roo.emptyFn
17150 });/*
17151  * Based on:
17152  * Ext JS Library 1.1.1
17153  * Copyright(c) 2006-2007, Ext JS, LLC.
17154  *
17155  * Originally Released Under LGPL - original licence link has changed is not relivant.
17156  *
17157  * Fork - LGPL
17158  * <script type="text/javascript">
17159  */
17160  
17161 /**
17162  * @class Roo.form.TextArea
17163  * @extends Roo.form.TextField
17164  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17165  * support for auto-sizing.
17166  * @constructor
17167  * Creates a new TextArea
17168  * @param {Object} config Configuration options
17169  */
17170 Roo.form.TextArea = function(config){
17171     Roo.form.TextArea.superclass.constructor.call(this, config);
17172     // these are provided exchanges for backwards compat
17173     // minHeight/maxHeight were replaced by growMin/growMax to be
17174     // compatible with TextField growing config values
17175     if(this.minHeight !== undefined){
17176         this.growMin = this.minHeight;
17177     }
17178     if(this.maxHeight !== undefined){
17179         this.growMax = this.maxHeight;
17180     }
17181 };
17182
17183 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17184     /**
17185      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17186      */
17187     growMin : 60,
17188     /**
17189      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17190      */
17191     growMax: 1000,
17192     /**
17193      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17194      * in the field (equivalent to setting overflow: hidden, defaults to false)
17195      */
17196     preventScrollbars: false,
17197     /**
17198      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17199      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17200      */
17201
17202     // private
17203     onRender : function(ct, position){
17204         if(!this.el){
17205             this.defaultAutoCreate = {
17206                 tag: "textarea",
17207                 style:"width:300px;height:60px;",
17208                 autocomplete: "new-password"
17209             };
17210         }
17211         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17212         if(this.grow){
17213             this.textSizeEl = Roo.DomHelper.append(document.body, {
17214                 tag: "pre", cls: "x-form-grow-sizer"
17215             });
17216             if(this.preventScrollbars){
17217                 this.el.setStyle("overflow", "hidden");
17218             }
17219             this.el.setHeight(this.growMin);
17220         }
17221     },
17222
17223     onDestroy : function(){
17224         if(this.textSizeEl){
17225             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17226         }
17227         Roo.form.TextArea.superclass.onDestroy.call(this);
17228     },
17229
17230     // private
17231     onKeyUp : function(e){
17232         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17233             this.autoSize();
17234         }
17235     },
17236
17237     /**
17238      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17239      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17240      */
17241     autoSize : function(){
17242         if(!this.grow || !this.textSizeEl){
17243             return;
17244         }
17245         var el = this.el;
17246         var v = el.dom.value;
17247         var ts = this.textSizeEl;
17248
17249         ts.innerHTML = '';
17250         ts.appendChild(document.createTextNode(v));
17251         v = ts.innerHTML;
17252
17253         Roo.fly(ts).setWidth(this.el.getWidth());
17254         if(v.length < 1){
17255             v = "&#160;&#160;";
17256         }else{
17257             if(Roo.isIE){
17258                 v = v.replace(/\n/g, '<p>&#160;</p>');
17259             }
17260             v += "&#160;\n&#160;";
17261         }
17262         ts.innerHTML = v;
17263         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17264         if(h != this.lastHeight){
17265             this.lastHeight = h;
17266             this.el.setHeight(h);
17267             this.fireEvent("autosize", this, h);
17268         }
17269     }
17270 });/*
17271  * Based on:
17272  * Ext JS Library 1.1.1
17273  * Copyright(c) 2006-2007, Ext JS, LLC.
17274  *
17275  * Originally Released Under LGPL - original licence link has changed is not relivant.
17276  *
17277  * Fork - LGPL
17278  * <script type="text/javascript">
17279  */
17280  
17281
17282 /**
17283  * @class Roo.form.NumberField
17284  * @extends Roo.form.TextField
17285  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17286  * @constructor
17287  * Creates a new NumberField
17288  * @param {Object} config Configuration options
17289  */
17290 Roo.form.NumberField = function(config){
17291     Roo.form.NumberField.superclass.constructor.call(this, config);
17292 };
17293
17294 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17295     /**
17296      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17297      */
17298     fieldClass: "x-form-field x-form-num-field",
17299     /**
17300      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17301      */
17302     allowDecimals : true,
17303     /**
17304      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17305      */
17306     decimalSeparator : ".",
17307     /**
17308      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17309      */
17310     decimalPrecision : 2,
17311     /**
17312      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17313      */
17314     allowNegative : true,
17315     /**
17316      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17317      */
17318     minValue : Number.NEGATIVE_INFINITY,
17319     /**
17320      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17321      */
17322     maxValue : Number.MAX_VALUE,
17323     /**
17324      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17325      */
17326     minText : "The minimum value for this field is {0}",
17327     /**
17328      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17329      */
17330     maxText : "The maximum value for this field is {0}",
17331     /**
17332      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17333      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17334      */
17335     nanText : "{0} is not a valid number",
17336
17337     // private
17338     initEvents : function(){
17339         Roo.form.NumberField.superclass.initEvents.call(this);
17340         var allowed = "0123456789";
17341         if(this.allowDecimals){
17342             allowed += this.decimalSeparator;
17343         }
17344         if(this.allowNegative){
17345             allowed += "-";
17346         }
17347         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17348         var keyPress = function(e){
17349             var k = e.getKey();
17350             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17351                 return;
17352             }
17353             var c = e.getCharCode();
17354             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17355                 e.stopEvent();
17356             }
17357         };
17358         this.el.on("keypress", keyPress, this);
17359     },
17360
17361     // private
17362     validateValue : function(value){
17363         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17364             return false;
17365         }
17366         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17367              return true;
17368         }
17369         var num = this.parseValue(value);
17370         if(isNaN(num)){
17371             this.markInvalid(String.format(this.nanText, value));
17372             return false;
17373         }
17374         if(num < this.minValue){
17375             this.markInvalid(String.format(this.minText, this.minValue));
17376             return false;
17377         }
17378         if(num > this.maxValue){
17379             this.markInvalid(String.format(this.maxText, this.maxValue));
17380             return false;
17381         }
17382         return true;
17383     },
17384
17385     getValue : function(){
17386         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17387     },
17388
17389     // private
17390     parseValue : function(value){
17391         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17392         return isNaN(value) ? '' : value;
17393     },
17394
17395     // private
17396     fixPrecision : function(value){
17397         var nan = isNaN(value);
17398         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17399             return nan ? '' : value;
17400         }
17401         return parseFloat(value).toFixed(this.decimalPrecision);
17402     },
17403
17404     setValue : function(v){
17405         v = this.fixPrecision(v);
17406         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17407     },
17408
17409     // private
17410     decimalPrecisionFcn : function(v){
17411         return Math.floor(v);
17412     },
17413
17414     beforeBlur : function(){
17415         var v = this.parseValue(this.getRawValue());
17416         if(v){
17417             this.setValue(v);
17418         }
17419     }
17420 });/*
17421  * Based on:
17422  * Ext JS Library 1.1.1
17423  * Copyright(c) 2006-2007, Ext JS, LLC.
17424  *
17425  * Originally Released Under LGPL - original licence link has changed is not relivant.
17426  *
17427  * Fork - LGPL
17428  * <script type="text/javascript">
17429  */
17430  
17431 /**
17432  * @class Roo.form.DateField
17433  * @extends Roo.form.TriggerField
17434  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17435 * @constructor
17436 * Create a new DateField
17437 * @param {Object} config
17438  */
17439 Roo.form.DateField = function(config)
17440 {
17441     Roo.form.DateField.superclass.constructor.call(this, config);
17442     
17443       this.addEvents({
17444          
17445         /**
17446          * @event select
17447          * Fires when a date is selected
17448              * @param {Roo.form.DateField} combo This combo box
17449              * @param {Date} date The date selected
17450              */
17451         'select' : true
17452          
17453     });
17454     
17455     
17456     if(typeof this.minValue == "string") {
17457         this.minValue = this.parseDate(this.minValue);
17458     }
17459     if(typeof this.maxValue == "string") {
17460         this.maxValue = this.parseDate(this.maxValue);
17461     }
17462     this.ddMatch = null;
17463     if(this.disabledDates){
17464         var dd = this.disabledDates;
17465         var re = "(?:";
17466         for(var i = 0; i < dd.length; i++){
17467             re += dd[i];
17468             if(i != dd.length-1) {
17469                 re += "|";
17470             }
17471         }
17472         this.ddMatch = new RegExp(re + ")");
17473     }
17474 };
17475
17476 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17477     /**
17478      * @cfg {String} format
17479      * The default date format string which can be overriden for localization support.  The format must be
17480      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17481      */
17482     format : "m/d/y",
17483     /**
17484      * @cfg {String} altFormats
17485      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17486      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17487      */
17488     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17489     /**
17490      * @cfg {Array} disabledDays
17491      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17492      */
17493     disabledDays : null,
17494     /**
17495      * @cfg {String} disabledDaysText
17496      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17497      */
17498     disabledDaysText : "Disabled",
17499     /**
17500      * @cfg {Array} disabledDates
17501      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17502      * expression so they are very powerful. Some examples:
17503      * <ul>
17504      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17505      * <li>["03/08", "09/16"] would disable those days for every year</li>
17506      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17507      * <li>["03/../2006"] would disable every day in March 2006</li>
17508      * <li>["^03"] would disable every day in every March</li>
17509      * </ul>
17510      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17511      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17512      */
17513     disabledDates : null,
17514     /**
17515      * @cfg {String} disabledDatesText
17516      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17517      */
17518     disabledDatesText : "Disabled",
17519     /**
17520      * @cfg {Date/String} minValue
17521      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17522      * valid format (defaults to null).
17523      */
17524     minValue : null,
17525     /**
17526      * @cfg {Date/String} maxValue
17527      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17528      * valid format (defaults to null).
17529      */
17530     maxValue : null,
17531     /**
17532      * @cfg {String} minText
17533      * The error text to display when the date in the cell is before minValue (defaults to
17534      * 'The date in this field must be after {minValue}').
17535      */
17536     minText : "The date in this field must be equal to or after {0}",
17537     /**
17538      * @cfg {String} maxText
17539      * The error text to display when the date in the cell is after maxValue (defaults to
17540      * 'The date in this field must be before {maxValue}').
17541      */
17542     maxText : "The date in this field must be equal to or before {0}",
17543     /**
17544      * @cfg {String} invalidText
17545      * The error text to display when the date in the field is invalid (defaults to
17546      * '{value} is not a valid date - it must be in the format {format}').
17547      */
17548     invalidText : "{0} is not a valid date - it must be in the format {1}",
17549     /**
17550      * @cfg {String} triggerClass
17551      * An additional CSS class used to style the trigger button.  The trigger will always get the
17552      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17553      * which displays a calendar icon).
17554      */
17555     triggerClass : 'x-form-date-trigger',
17556     
17557
17558     /**
17559      * @cfg {Boolean} useIso
17560      * if enabled, then the date field will use a hidden field to store the 
17561      * real value as iso formated date. default (false)
17562      */ 
17563     useIso : false,
17564     /**
17565      * @cfg {String/Object} autoCreate
17566      * A DomHelper element spec, or true for a default element spec (defaults to
17567      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17568      */ 
17569     // private
17570     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17571     
17572     // private
17573     hiddenField: false,
17574     
17575     onRender : function(ct, position)
17576     {
17577         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17578         if (this.useIso) {
17579             //this.el.dom.removeAttribute('name'); 
17580             Roo.log("Changing name?");
17581             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17582             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17583                     'before', true);
17584             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17585             // prevent input submission
17586             this.hiddenName = this.name;
17587         }
17588             
17589             
17590     },
17591     
17592     // private
17593     validateValue : function(value)
17594     {
17595         value = this.formatDate(value);
17596         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17597             Roo.log('super failed');
17598             return false;
17599         }
17600         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17601              return true;
17602         }
17603         var svalue = value;
17604         value = this.parseDate(value);
17605         if(!value){
17606             Roo.log('parse date failed' + svalue);
17607             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17608             return false;
17609         }
17610         var time = value.getTime();
17611         if(this.minValue && time < this.minValue.getTime()){
17612             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17613             return false;
17614         }
17615         if(this.maxValue && time > this.maxValue.getTime()){
17616             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17617             return false;
17618         }
17619         if(this.disabledDays){
17620             var day = value.getDay();
17621             for(var i = 0; i < this.disabledDays.length; i++) {
17622                 if(day === this.disabledDays[i]){
17623                     this.markInvalid(this.disabledDaysText);
17624                     return false;
17625                 }
17626             }
17627         }
17628         var fvalue = this.formatDate(value);
17629         if(this.ddMatch && this.ddMatch.test(fvalue)){
17630             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17631             return false;
17632         }
17633         return true;
17634     },
17635
17636     // private
17637     // Provides logic to override the default TriggerField.validateBlur which just returns true
17638     validateBlur : function(){
17639         return !this.menu || !this.menu.isVisible();
17640     },
17641     
17642     getName: function()
17643     {
17644         // returns hidden if it's set..
17645         if (!this.rendered) {return ''};
17646         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17647         
17648     },
17649
17650     /**
17651      * Returns the current date value of the date field.
17652      * @return {Date} The date value
17653      */
17654     getValue : function(){
17655         
17656         return  this.hiddenField ?
17657                 this.hiddenField.value :
17658                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17659     },
17660
17661     /**
17662      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17663      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17664      * (the default format used is "m/d/y").
17665      * <br />Usage:
17666      * <pre><code>
17667 //All of these calls set the same date value (May 4, 2006)
17668
17669 //Pass a date object:
17670 var dt = new Date('5/4/06');
17671 dateField.setValue(dt);
17672
17673 //Pass a date string (default format):
17674 dateField.setValue('5/4/06');
17675
17676 //Pass a date string (custom format):
17677 dateField.format = 'Y-m-d';
17678 dateField.setValue('2006-5-4');
17679 </code></pre>
17680      * @param {String/Date} date The date or valid date string
17681      */
17682     setValue : function(date){
17683         if (this.hiddenField) {
17684             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17685         }
17686         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17687         // make sure the value field is always stored as a date..
17688         this.value = this.parseDate(date);
17689         
17690         
17691     },
17692
17693     // private
17694     parseDate : function(value){
17695         if(!value || value instanceof Date){
17696             return value;
17697         }
17698         var v = Date.parseDate(value, this.format);
17699          if (!v && this.useIso) {
17700             v = Date.parseDate(value, 'Y-m-d');
17701         }
17702         if(!v && this.altFormats){
17703             if(!this.altFormatsArray){
17704                 this.altFormatsArray = this.altFormats.split("|");
17705             }
17706             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17707                 v = Date.parseDate(value, this.altFormatsArray[i]);
17708             }
17709         }
17710         return v;
17711     },
17712
17713     // private
17714     formatDate : function(date, fmt){
17715         return (!date || !(date instanceof Date)) ?
17716                date : date.dateFormat(fmt || this.format);
17717     },
17718
17719     // private
17720     menuListeners : {
17721         select: function(m, d){
17722             
17723             this.setValue(d);
17724             this.fireEvent('select', this, d);
17725         },
17726         show : function(){ // retain focus styling
17727             this.onFocus();
17728         },
17729         hide : function(){
17730             this.focus.defer(10, this);
17731             var ml = this.menuListeners;
17732             this.menu.un("select", ml.select,  this);
17733             this.menu.un("show", ml.show,  this);
17734             this.menu.un("hide", ml.hide,  this);
17735         }
17736     },
17737
17738     // private
17739     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17740     onTriggerClick : function(){
17741         if(this.disabled){
17742             return;
17743         }
17744         if(this.menu == null){
17745             this.menu = new Roo.menu.DateMenu();
17746         }
17747         Roo.apply(this.menu.picker,  {
17748             showClear: this.allowBlank,
17749             minDate : this.minValue,
17750             maxDate : this.maxValue,
17751             disabledDatesRE : this.ddMatch,
17752             disabledDatesText : this.disabledDatesText,
17753             disabledDays : this.disabledDays,
17754             disabledDaysText : this.disabledDaysText,
17755             format : this.useIso ? 'Y-m-d' : this.format,
17756             minText : String.format(this.minText, this.formatDate(this.minValue)),
17757             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17758         });
17759         this.menu.on(Roo.apply({}, this.menuListeners, {
17760             scope:this
17761         }));
17762         this.menu.picker.setValue(this.getValue() || new Date());
17763         this.menu.show(this.el, "tl-bl?");
17764     },
17765
17766     beforeBlur : function(){
17767         var v = this.parseDate(this.getRawValue());
17768         if(v){
17769             this.setValue(v);
17770         }
17771     },
17772
17773     /*@
17774      * overide
17775      * 
17776      */
17777     isDirty : function() {
17778         if(this.disabled) {
17779             return false;
17780         }
17781         
17782         if(typeof(this.startValue) === 'undefined'){
17783             return false;
17784         }
17785         
17786         return String(this.getValue()) !== String(this.startValue);
17787         
17788     },
17789     // @overide
17790     cleanLeadingSpace : function(e)
17791     {
17792        return;
17793     }
17794     
17795 });/*
17796  * Based on:
17797  * Ext JS Library 1.1.1
17798  * Copyright(c) 2006-2007, Ext JS, LLC.
17799  *
17800  * Originally Released Under LGPL - original licence link has changed is not relivant.
17801  *
17802  * Fork - LGPL
17803  * <script type="text/javascript">
17804  */
17805  
17806 /**
17807  * @class Roo.form.MonthField
17808  * @extends Roo.form.TriggerField
17809  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17810 * @constructor
17811 * Create a new MonthField
17812 * @param {Object} config
17813  */
17814 Roo.form.MonthField = function(config){
17815     
17816     Roo.form.MonthField.superclass.constructor.call(this, config);
17817     
17818       this.addEvents({
17819          
17820         /**
17821          * @event select
17822          * Fires when a date is selected
17823              * @param {Roo.form.MonthFieeld} combo This combo box
17824              * @param {Date} date The date selected
17825              */
17826         'select' : true
17827          
17828     });
17829     
17830     
17831     if(typeof this.minValue == "string") {
17832         this.minValue = this.parseDate(this.minValue);
17833     }
17834     if(typeof this.maxValue == "string") {
17835         this.maxValue = this.parseDate(this.maxValue);
17836     }
17837     this.ddMatch = null;
17838     if(this.disabledDates){
17839         var dd = this.disabledDates;
17840         var re = "(?:";
17841         for(var i = 0; i < dd.length; i++){
17842             re += dd[i];
17843             if(i != dd.length-1) {
17844                 re += "|";
17845             }
17846         }
17847         this.ddMatch = new RegExp(re + ")");
17848     }
17849 };
17850
17851 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17852     /**
17853      * @cfg {String} format
17854      * The default date format string which can be overriden for localization support.  The format must be
17855      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17856      */
17857     format : "M Y",
17858     /**
17859      * @cfg {String} altFormats
17860      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17861      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17862      */
17863     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17864     /**
17865      * @cfg {Array} disabledDays
17866      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17867      */
17868     disabledDays : [0,1,2,3,4,5,6],
17869     /**
17870      * @cfg {String} disabledDaysText
17871      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17872      */
17873     disabledDaysText : "Disabled",
17874     /**
17875      * @cfg {Array} disabledDates
17876      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17877      * expression so they are very powerful. Some examples:
17878      * <ul>
17879      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17880      * <li>["03/08", "09/16"] would disable those days for every year</li>
17881      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17882      * <li>["03/../2006"] would disable every day in March 2006</li>
17883      * <li>["^03"] would disable every day in every March</li>
17884      * </ul>
17885      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17886      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17887      */
17888     disabledDates : null,
17889     /**
17890      * @cfg {String} disabledDatesText
17891      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17892      */
17893     disabledDatesText : "Disabled",
17894     /**
17895      * @cfg {Date/String} minValue
17896      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17897      * valid format (defaults to null).
17898      */
17899     minValue : null,
17900     /**
17901      * @cfg {Date/String} maxValue
17902      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17903      * valid format (defaults to null).
17904      */
17905     maxValue : null,
17906     /**
17907      * @cfg {String} minText
17908      * The error text to display when the date in the cell is before minValue (defaults to
17909      * 'The date in this field must be after {minValue}').
17910      */
17911     minText : "The date in this field must be equal to or after {0}",
17912     /**
17913      * @cfg {String} maxTextf
17914      * The error text to display when the date in the cell is after maxValue (defaults to
17915      * 'The date in this field must be before {maxValue}').
17916      */
17917     maxText : "The date in this field must be equal to or before {0}",
17918     /**
17919      * @cfg {String} invalidText
17920      * The error text to display when the date in the field is invalid (defaults to
17921      * '{value} is not a valid date - it must be in the format {format}').
17922      */
17923     invalidText : "{0} is not a valid date - it must be in the format {1}",
17924     /**
17925      * @cfg {String} triggerClass
17926      * An additional CSS class used to style the trigger button.  The trigger will always get the
17927      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17928      * which displays a calendar icon).
17929      */
17930     triggerClass : 'x-form-date-trigger',
17931     
17932
17933     /**
17934      * @cfg {Boolean} useIso
17935      * if enabled, then the date field will use a hidden field to store the 
17936      * real value as iso formated date. default (true)
17937      */ 
17938     useIso : true,
17939     /**
17940      * @cfg {String/Object} autoCreate
17941      * A DomHelper element spec, or true for a default element spec (defaults to
17942      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17943      */ 
17944     // private
17945     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17946     
17947     // private
17948     hiddenField: false,
17949     
17950     hideMonthPicker : false,
17951     
17952     onRender : function(ct, position)
17953     {
17954         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
17955         if (this.useIso) {
17956             this.el.dom.removeAttribute('name'); 
17957             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17958                     'before', true);
17959             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17960             // prevent input submission
17961             this.hiddenName = this.name;
17962         }
17963             
17964             
17965     },
17966     
17967     // private
17968     validateValue : function(value)
17969     {
17970         value = this.formatDate(value);
17971         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
17972             return false;
17973         }
17974         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17975              return true;
17976         }
17977         var svalue = value;
17978         value = this.parseDate(value);
17979         if(!value){
17980             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17981             return false;
17982         }
17983         var time = value.getTime();
17984         if(this.minValue && time < this.minValue.getTime()){
17985             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17986             return false;
17987         }
17988         if(this.maxValue && time > this.maxValue.getTime()){
17989             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17990             return false;
17991         }
17992         /*if(this.disabledDays){
17993             var day = value.getDay();
17994             for(var i = 0; i < this.disabledDays.length; i++) {
17995                 if(day === this.disabledDays[i]){
17996                     this.markInvalid(this.disabledDaysText);
17997                     return false;
17998                 }
17999             }
18000         }
18001         */
18002         var fvalue = this.formatDate(value);
18003         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18004             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18005             return false;
18006         }
18007         */
18008         return true;
18009     },
18010
18011     // private
18012     // Provides logic to override the default TriggerField.validateBlur which just returns true
18013     validateBlur : function(){
18014         return !this.menu || !this.menu.isVisible();
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         
18024         
18025         return  this.hiddenField ?
18026                 this.hiddenField.value :
18027                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18028     },
18029
18030     /**
18031      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18032      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18033      * (the default format used is "m/d/y").
18034      * <br />Usage:
18035      * <pre><code>
18036 //All of these calls set the same date value (May 4, 2006)
18037
18038 //Pass a date object:
18039 var dt = new Date('5/4/06');
18040 monthField.setValue(dt);
18041
18042 //Pass a date string (default format):
18043 monthField.setValue('5/4/06');
18044
18045 //Pass a date string (custom format):
18046 monthField.format = 'Y-m-d';
18047 monthField.setValue('2006-5-4');
18048 </code></pre>
18049      * @param {String/Date} date The date or valid date string
18050      */
18051     setValue : function(date){
18052         Roo.log('month setValue' + date);
18053         // can only be first of month..
18054         
18055         var val = this.parseDate(date);
18056         
18057         if (this.hiddenField) {
18058             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18059         }
18060         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18061         this.value = this.parseDate(date);
18062     },
18063
18064     // private
18065     parseDate : function(value){
18066         if(!value || value instanceof Date){
18067             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18068             return value;
18069         }
18070         var v = Date.parseDate(value, this.format);
18071         if (!v && this.useIso) {
18072             v = Date.parseDate(value, 'Y-m-d');
18073         }
18074         if (v) {
18075             // 
18076             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18077         }
18078         
18079         
18080         if(!v && this.altFormats){
18081             if(!this.altFormatsArray){
18082                 this.altFormatsArray = this.altFormats.split("|");
18083             }
18084             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18085                 v = Date.parseDate(value, this.altFormatsArray[i]);
18086             }
18087         }
18088         return v;
18089     },
18090
18091     // private
18092     formatDate : function(date, fmt){
18093         return (!date || !(date instanceof Date)) ?
18094                date : date.dateFormat(fmt || this.format);
18095     },
18096
18097     // private
18098     menuListeners : {
18099         select: function(m, d){
18100             this.setValue(d);
18101             this.fireEvent('select', this, d);
18102         },
18103         show : function(){ // retain focus styling
18104             this.onFocus();
18105         },
18106         hide : function(){
18107             this.focus.defer(10, this);
18108             var ml = this.menuListeners;
18109             this.menu.un("select", ml.select,  this);
18110             this.menu.un("show", ml.show,  this);
18111             this.menu.un("hide", ml.hide,  this);
18112         }
18113     },
18114     // private
18115     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18116     onTriggerClick : function(){
18117         if(this.disabled){
18118             return;
18119         }
18120         if(this.menu == null){
18121             this.menu = new Roo.menu.DateMenu();
18122            
18123         }
18124         
18125         Roo.apply(this.menu.picker,  {
18126             
18127             showClear: this.allowBlank,
18128             minDate : this.minValue,
18129             maxDate : this.maxValue,
18130             disabledDatesRE : this.ddMatch,
18131             disabledDatesText : this.disabledDatesText,
18132             
18133             format : this.useIso ? 'Y-m-d' : this.format,
18134             minText : String.format(this.minText, this.formatDate(this.minValue)),
18135             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18136             
18137         });
18138          this.menu.on(Roo.apply({}, this.menuListeners, {
18139             scope:this
18140         }));
18141        
18142         
18143         var m = this.menu;
18144         var p = m.picker;
18145         
18146         // hide month picker get's called when we called by 'before hide';
18147         
18148         var ignorehide = true;
18149         p.hideMonthPicker  = function(disableAnim){
18150             if (ignorehide) {
18151                 return;
18152             }
18153              if(this.monthPicker){
18154                 Roo.log("hideMonthPicker called");
18155                 if(disableAnim === true){
18156                     this.monthPicker.hide();
18157                 }else{
18158                     this.monthPicker.slideOut('t', {duration:.2});
18159                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18160                     p.fireEvent("select", this, this.value);
18161                     m.hide();
18162                 }
18163             }
18164         }
18165         
18166         Roo.log('picker set value');
18167         Roo.log(this.getValue());
18168         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18169         m.show(this.el, 'tl-bl?');
18170         ignorehide  = false;
18171         // this will trigger hideMonthPicker..
18172         
18173         
18174         // hidden the day picker
18175         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18176         
18177         
18178         
18179       
18180         
18181         p.showMonthPicker.defer(100, p);
18182     
18183         
18184        
18185     },
18186
18187     beforeBlur : function(){
18188         var v = this.parseDate(this.getRawValue());
18189         if(v){
18190             this.setValue(v);
18191         }
18192     }
18193
18194     /** @cfg {Boolean} grow @hide */
18195     /** @cfg {Number} growMin @hide */
18196     /** @cfg {Number} growMax @hide */
18197     /**
18198      * @hide
18199      * @method autoSize
18200      */
18201 });/*
18202  * Based on:
18203  * Ext JS Library 1.1.1
18204  * Copyright(c) 2006-2007, Ext JS, LLC.
18205  *
18206  * Originally Released Under LGPL - original licence link has changed is not relivant.
18207  *
18208  * Fork - LGPL
18209  * <script type="text/javascript">
18210  */
18211  
18212
18213 /**
18214  * @class Roo.form.ComboBox
18215  * @extends Roo.form.TriggerField
18216  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18217  * @constructor
18218  * Create a new ComboBox.
18219  * @param {Object} config Configuration options
18220  */
18221 Roo.form.ComboBox = function(config){
18222     Roo.form.ComboBox.superclass.constructor.call(this, config);
18223     this.addEvents({
18224         /**
18225          * @event expand
18226          * Fires when the dropdown list is expanded
18227              * @param {Roo.form.ComboBox} combo This combo box
18228              */
18229         'expand' : true,
18230         /**
18231          * @event collapse
18232          * Fires when the dropdown list is collapsed
18233              * @param {Roo.form.ComboBox} combo This combo box
18234              */
18235         'collapse' : true,
18236         /**
18237          * @event beforeselect
18238          * Fires before a list item is selected. Return false to cancel the selection.
18239              * @param {Roo.form.ComboBox} combo This combo box
18240              * @param {Roo.data.Record} record The data record returned from the underlying store
18241              * @param {Number} index The index of the selected item in the dropdown list
18242              */
18243         'beforeselect' : true,
18244         /**
18245          * @event select
18246          * Fires when a list item is selected
18247              * @param {Roo.form.ComboBox} combo This combo box
18248              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18249              * @param {Number} index The index of the selected item in the dropdown list
18250              */
18251         'select' : true,
18252         /**
18253          * @event beforequery
18254          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18255          * The event object passed has these properties:
18256              * @param {Roo.form.ComboBox} combo This combo box
18257              * @param {String} query The query
18258              * @param {Boolean} forceAll true to force "all" query
18259              * @param {Boolean} cancel true to cancel the query
18260              * @param {Object} e The query event object
18261              */
18262         'beforequery': true,
18263          /**
18264          * @event add
18265          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18266              * @param {Roo.form.ComboBox} combo This combo box
18267              */
18268         'add' : true,
18269         /**
18270          * @event edit
18271          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18272              * @param {Roo.form.ComboBox} combo This combo box
18273              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18274              */
18275         'edit' : true
18276         
18277         
18278     });
18279     if(this.transform){
18280         this.allowDomMove = false;
18281         var s = Roo.getDom(this.transform);
18282         if(!this.hiddenName){
18283             this.hiddenName = s.name;
18284         }
18285         if(!this.store){
18286             this.mode = 'local';
18287             var d = [], opts = s.options;
18288             for(var i = 0, len = opts.length;i < len; i++){
18289                 var o = opts[i];
18290                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18291                 if(o.selected) {
18292                     this.value = value;
18293                 }
18294                 d.push([value, o.text]);
18295             }
18296             this.store = new Roo.data.SimpleStore({
18297                 'id': 0,
18298                 fields: ['value', 'text'],
18299                 data : d
18300             });
18301             this.valueField = 'value';
18302             this.displayField = 'text';
18303         }
18304         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18305         if(!this.lazyRender){
18306             this.target = true;
18307             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18308             s.parentNode.removeChild(s); // remove it
18309             this.render(this.el.parentNode);
18310         }else{
18311             s.parentNode.removeChild(s); // remove it
18312         }
18313
18314     }
18315     if (this.store) {
18316         this.store = Roo.factory(this.store, Roo.data);
18317     }
18318     
18319     this.selectedIndex = -1;
18320     if(this.mode == 'local'){
18321         if(config.queryDelay === undefined){
18322             this.queryDelay = 10;
18323         }
18324         if(config.minChars === undefined){
18325             this.minChars = 0;
18326         }
18327     }
18328 };
18329
18330 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18331     /**
18332      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18333      */
18334     /**
18335      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18336      * rendering into an Roo.Editor, defaults to false)
18337      */
18338     /**
18339      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18340      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18341      */
18342     /**
18343      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18344      */
18345     /**
18346      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18347      * the dropdown list (defaults to undefined, with no header element)
18348      */
18349
18350      /**
18351      * @cfg {String/Roo.Template} tpl The template to use to render the output
18352      */
18353      
18354     // private
18355     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18356     /**
18357      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18358      */
18359     listWidth: undefined,
18360     /**
18361      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18362      * mode = 'remote' or 'text' if mode = 'local')
18363      */
18364     displayField: undefined,
18365     /**
18366      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18367      * mode = 'remote' or 'value' if mode = 'local'). 
18368      * Note: use of a valueField requires the user make a selection
18369      * in order for a value to be mapped.
18370      */
18371     valueField: undefined,
18372     
18373     
18374     /**
18375      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18376      * field's data value (defaults to the underlying DOM element's name)
18377      */
18378     hiddenName: undefined,
18379     /**
18380      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18381      */
18382     listClass: '',
18383     /**
18384      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18385      */
18386     selectedClass: 'x-combo-selected',
18387     /**
18388      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18389      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18390      * which displays a downward arrow icon).
18391      */
18392     triggerClass : 'x-form-arrow-trigger',
18393     /**
18394      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18395      */
18396     shadow:'sides',
18397     /**
18398      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18399      * anchor positions (defaults to 'tl-bl')
18400      */
18401     listAlign: 'tl-bl?',
18402     /**
18403      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18404      */
18405     maxHeight: 300,
18406     /**
18407      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18408      * query specified by the allQuery config option (defaults to 'query')
18409      */
18410     triggerAction: 'query',
18411     /**
18412      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18413      * (defaults to 4, does not apply if editable = false)
18414      */
18415     minChars : 4,
18416     /**
18417      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18418      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18419      */
18420     typeAhead: false,
18421     /**
18422      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18423      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18424      */
18425     queryDelay: 500,
18426     /**
18427      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18428      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18429      */
18430     pageSize: 0,
18431     /**
18432      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18433      * when editable = true (defaults to false)
18434      */
18435     selectOnFocus:false,
18436     /**
18437      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18438      */
18439     queryParam: 'query',
18440     /**
18441      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18442      * when mode = 'remote' (defaults to 'Loading...')
18443      */
18444     loadingText: 'Loading...',
18445     /**
18446      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18447      */
18448     resizable: false,
18449     /**
18450      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18451      */
18452     handleHeight : 8,
18453     /**
18454      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18455      * traditional select (defaults to true)
18456      */
18457     editable: true,
18458     /**
18459      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18460      */
18461     allQuery: '',
18462     /**
18463      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18464      */
18465     mode: 'remote',
18466     /**
18467      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18468      * listWidth has a higher value)
18469      */
18470     minListWidth : 70,
18471     /**
18472      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18473      * allow the user to set arbitrary text into the field (defaults to false)
18474      */
18475     forceSelection:false,
18476     /**
18477      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18478      * if typeAhead = true (defaults to 250)
18479      */
18480     typeAheadDelay : 250,
18481     /**
18482      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18483      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18484      */
18485     valueNotFoundText : undefined,
18486     /**
18487      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18488      */
18489     blockFocus : false,
18490     
18491     /**
18492      * @cfg {Boolean} disableClear Disable showing of clear button.
18493      */
18494     disableClear : false,
18495     /**
18496      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18497      */
18498     alwaysQuery : false,
18499     
18500     //private
18501     addicon : false,
18502     editicon: false,
18503     
18504     // element that contains real text value.. (when hidden is used..)
18505      
18506     // private
18507     onRender : function(ct, position)
18508     {
18509         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18510         
18511         if(this.hiddenName){
18512             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18513                     'before', true);
18514             this.hiddenField.value =
18515                 this.hiddenValue !== undefined ? this.hiddenValue :
18516                 this.value !== undefined ? this.value : '';
18517
18518             // prevent input submission
18519             this.el.dom.removeAttribute('name');
18520              
18521              
18522         }
18523         
18524         if(Roo.isGecko){
18525             this.el.dom.setAttribute('autocomplete', 'off');
18526         }
18527
18528         var cls = 'x-combo-list';
18529
18530         this.list = new Roo.Layer({
18531             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18532         });
18533
18534         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18535         this.list.setWidth(lw);
18536         this.list.swallowEvent('mousewheel');
18537         this.assetHeight = 0;
18538
18539         if(this.title){
18540             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18541             this.assetHeight += this.header.getHeight();
18542         }
18543
18544         this.innerList = this.list.createChild({cls:cls+'-inner'});
18545         this.innerList.on('mouseover', this.onViewOver, this);
18546         this.innerList.on('mousemove', this.onViewMove, this);
18547         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18548         
18549         if(this.allowBlank && !this.pageSize && !this.disableClear){
18550             this.footer = this.list.createChild({cls:cls+'-ft'});
18551             this.pageTb = new Roo.Toolbar(this.footer);
18552            
18553         }
18554         if(this.pageSize){
18555             this.footer = this.list.createChild({cls:cls+'-ft'});
18556             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18557                     {pageSize: this.pageSize});
18558             
18559         }
18560         
18561         if (this.pageTb && this.allowBlank && !this.disableClear) {
18562             var _this = this;
18563             this.pageTb.add(new Roo.Toolbar.Fill(), {
18564                 cls: 'x-btn-icon x-btn-clear',
18565                 text: '&#160;',
18566                 handler: function()
18567                 {
18568                     _this.collapse();
18569                     _this.clearValue();
18570                     _this.onSelect(false, -1);
18571                 }
18572             });
18573         }
18574         if (this.footer) {
18575             this.assetHeight += this.footer.getHeight();
18576         }
18577         
18578
18579         if(!this.tpl){
18580             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18581         }
18582
18583         this.view = new Roo.View(this.innerList, this.tpl, {
18584             singleSelect:true,
18585             store: this.store,
18586             selectedClass: this.selectedClass
18587         });
18588
18589         this.view.on('click', this.onViewClick, this);
18590
18591         this.store.on('beforeload', this.onBeforeLoad, this);
18592         this.store.on('load', this.onLoad, this);
18593         this.store.on('loadexception', this.onLoadException, this);
18594
18595         if(this.resizable){
18596             this.resizer = new Roo.Resizable(this.list,  {
18597                pinned:true, handles:'se'
18598             });
18599             this.resizer.on('resize', function(r, w, h){
18600                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18601                 this.listWidth = w;
18602                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18603                 this.restrictHeight();
18604             }, this);
18605             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18606         }
18607         if(!this.editable){
18608             this.editable = true;
18609             this.setEditable(false);
18610         }  
18611         
18612         
18613         if (typeof(this.events.add.listeners) != 'undefined') {
18614             
18615             this.addicon = this.wrap.createChild(
18616                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18617        
18618             this.addicon.on('click', function(e) {
18619                 this.fireEvent('add', this);
18620             }, this);
18621         }
18622         if (typeof(this.events.edit.listeners) != 'undefined') {
18623             
18624             this.editicon = this.wrap.createChild(
18625                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18626             if (this.addicon) {
18627                 this.editicon.setStyle('margin-left', '40px');
18628             }
18629             this.editicon.on('click', function(e) {
18630                 
18631                 // we fire even  if inothing is selected..
18632                 this.fireEvent('edit', this, this.lastData );
18633                 
18634             }, this);
18635         }
18636         
18637         
18638         
18639     },
18640
18641     // private
18642     initEvents : function(){
18643         Roo.form.ComboBox.superclass.initEvents.call(this);
18644
18645         this.keyNav = new Roo.KeyNav(this.el, {
18646             "up" : function(e){
18647                 this.inKeyMode = true;
18648                 this.selectPrev();
18649             },
18650
18651             "down" : function(e){
18652                 if(!this.isExpanded()){
18653                     this.onTriggerClick();
18654                 }else{
18655                     this.inKeyMode = true;
18656                     this.selectNext();
18657                 }
18658             },
18659
18660             "enter" : function(e){
18661                 this.onViewClick();
18662                 //return true;
18663             },
18664
18665             "esc" : function(e){
18666                 this.collapse();
18667             },
18668
18669             "tab" : function(e){
18670                 this.onViewClick(false);
18671                 this.fireEvent("specialkey", this, e);
18672                 return true;
18673             },
18674
18675             scope : this,
18676
18677             doRelay : function(foo, bar, hname){
18678                 if(hname == 'down' || this.scope.isExpanded()){
18679                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18680                 }
18681                 return true;
18682             },
18683
18684             forceKeyDown: true
18685         });
18686         this.queryDelay = Math.max(this.queryDelay || 10,
18687                 this.mode == 'local' ? 10 : 250);
18688         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18689         if(this.typeAhead){
18690             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18691         }
18692         if(this.editable !== false){
18693             this.el.on("keyup", this.onKeyUp, this);
18694         }
18695         if(this.forceSelection){
18696             this.on('blur', this.doForce, this);
18697         }
18698     },
18699
18700     onDestroy : function(){
18701         if(this.view){
18702             this.view.setStore(null);
18703             this.view.el.removeAllListeners();
18704             this.view.el.remove();
18705             this.view.purgeListeners();
18706         }
18707         if(this.list){
18708             this.list.destroy();
18709         }
18710         if(this.store){
18711             this.store.un('beforeload', this.onBeforeLoad, this);
18712             this.store.un('load', this.onLoad, this);
18713             this.store.un('loadexception', this.onLoadException, this);
18714         }
18715         Roo.form.ComboBox.superclass.onDestroy.call(this);
18716     },
18717
18718     // private
18719     fireKey : function(e){
18720         if(e.isNavKeyPress() && !this.list.isVisible()){
18721             this.fireEvent("specialkey", this, e);
18722         }
18723     },
18724
18725     // private
18726     onResize: function(w, h){
18727         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18728         
18729         if(typeof w != 'number'){
18730             // we do not handle it!?!?
18731             return;
18732         }
18733         var tw = this.trigger.getWidth();
18734         tw += this.addicon ? this.addicon.getWidth() : 0;
18735         tw += this.editicon ? this.editicon.getWidth() : 0;
18736         var x = w - tw;
18737         this.el.setWidth( this.adjustWidth('input', x));
18738             
18739         this.trigger.setStyle('left', x+'px');
18740         
18741         if(this.list && this.listWidth === undefined){
18742             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18743             this.list.setWidth(lw);
18744             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18745         }
18746         
18747     
18748         
18749     },
18750
18751     /**
18752      * Allow or prevent the user from directly editing the field text.  If false is passed,
18753      * the user will only be able to select from the items defined in the dropdown list.  This method
18754      * is the runtime equivalent of setting the 'editable' config option at config time.
18755      * @param {Boolean} value True to allow the user to directly edit the field text
18756      */
18757     setEditable : function(value){
18758         if(value == this.editable){
18759             return;
18760         }
18761         this.editable = value;
18762         if(!value){
18763             this.el.dom.setAttribute('readOnly', true);
18764             this.el.on('mousedown', this.onTriggerClick,  this);
18765             this.el.addClass('x-combo-noedit');
18766         }else{
18767             this.el.dom.setAttribute('readOnly', false);
18768             this.el.un('mousedown', this.onTriggerClick,  this);
18769             this.el.removeClass('x-combo-noedit');
18770         }
18771     },
18772
18773     // private
18774     onBeforeLoad : function(){
18775         if(!this.hasFocus){
18776             return;
18777         }
18778         this.innerList.update(this.loadingText ?
18779                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18780         this.restrictHeight();
18781         this.selectedIndex = -1;
18782     },
18783
18784     // private
18785     onLoad : function(){
18786         if(!this.hasFocus){
18787             return;
18788         }
18789         if(this.store.getCount() > 0){
18790             this.expand();
18791             this.restrictHeight();
18792             if(this.lastQuery == this.allQuery){
18793                 if(this.editable){
18794                     this.el.dom.select();
18795                 }
18796                 if(!this.selectByValue(this.value, true)){
18797                     this.select(0, true);
18798                 }
18799             }else{
18800                 this.selectNext();
18801                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18802                     this.taTask.delay(this.typeAheadDelay);
18803                 }
18804             }
18805         }else{
18806             this.onEmptyResults();
18807         }
18808         //this.el.focus();
18809     },
18810     // private
18811     onLoadException : function()
18812     {
18813         this.collapse();
18814         Roo.log(this.store.reader.jsonData);
18815         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18816             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18817         }
18818         
18819         
18820     },
18821     // private
18822     onTypeAhead : function(){
18823         if(this.store.getCount() > 0){
18824             var r = this.store.getAt(0);
18825             var newValue = r.data[this.displayField];
18826             var len = newValue.length;
18827             var selStart = this.getRawValue().length;
18828             if(selStart != len){
18829                 this.setRawValue(newValue);
18830                 this.selectText(selStart, newValue.length);
18831             }
18832         }
18833     },
18834
18835     // private
18836     onSelect : function(record, index){
18837         if(this.fireEvent('beforeselect', this, record, index) !== false){
18838             this.setFromData(index > -1 ? record.data : false);
18839             this.collapse();
18840             this.fireEvent('select', this, record, index);
18841         }
18842     },
18843
18844     /**
18845      * Returns the currently selected field value or empty string if no value is set.
18846      * @return {String} value The selected value
18847      */
18848     getValue : function(){
18849         if(this.valueField){
18850             return typeof this.value != 'undefined' ? this.value : '';
18851         }
18852         return Roo.form.ComboBox.superclass.getValue.call(this);
18853     },
18854
18855     /**
18856      * Clears any text/value currently set in the field
18857      */
18858     clearValue : function(){
18859         if(this.hiddenField){
18860             this.hiddenField.value = '';
18861         }
18862         this.value = '';
18863         this.setRawValue('');
18864         this.lastSelectionText = '';
18865         
18866     },
18867
18868     /**
18869      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18870      * will be displayed in the field.  If the value does not match the data value of an existing item,
18871      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18872      * Otherwise the field will be blank (although the value will still be set).
18873      * @param {String} value The value to match
18874      */
18875     setValue : function(v){
18876         var text = v;
18877         if(this.valueField){
18878             var r = this.findRecord(this.valueField, v);
18879             if(r){
18880                 text = r.data[this.displayField];
18881             }else if(this.valueNotFoundText !== undefined){
18882                 text = this.valueNotFoundText;
18883             }
18884         }
18885         this.lastSelectionText = text;
18886         if(this.hiddenField){
18887             this.hiddenField.value = v;
18888         }
18889         Roo.form.ComboBox.superclass.setValue.call(this, text);
18890         this.value = v;
18891     },
18892     /**
18893      * @property {Object} the last set data for the element
18894      */
18895     
18896     lastData : false,
18897     /**
18898      * Sets the value of the field based on a object which is related to the record format for the store.
18899      * @param {Object} value the value to set as. or false on reset?
18900      */
18901     setFromData : function(o){
18902         var dv = ''; // display value
18903         var vv = ''; // value value..
18904         this.lastData = o;
18905         if (this.displayField) {
18906             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18907         } else {
18908             // this is an error condition!!!
18909             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18910         }
18911         
18912         if(this.valueField){
18913             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18914         }
18915         if(this.hiddenField){
18916             this.hiddenField.value = vv;
18917             
18918             this.lastSelectionText = dv;
18919             Roo.form.ComboBox.superclass.setValue.call(this, dv);
18920             this.value = vv;
18921             return;
18922         }
18923         // no hidden field.. - we store the value in 'value', but still display
18924         // display field!!!!
18925         this.lastSelectionText = dv;
18926         Roo.form.ComboBox.superclass.setValue.call(this, dv);
18927         this.value = vv;
18928         
18929         
18930     },
18931     // private
18932     reset : function(){
18933         // overridden so that last data is reset..
18934         this.setValue(this.resetValue);
18935         this.originalValue = this.getValue();
18936         this.clearInvalid();
18937         this.lastData = false;
18938         if (this.view) {
18939             this.view.clearSelections();
18940         }
18941     },
18942     // private
18943     findRecord : function(prop, value){
18944         var record;
18945         if(this.store.getCount() > 0){
18946             this.store.each(function(r){
18947                 if(r.data[prop] == value){
18948                     record = r;
18949                     return false;
18950                 }
18951                 return true;
18952             });
18953         }
18954         return record;
18955     },
18956     
18957     getName: function()
18958     {
18959         // returns hidden if it's set..
18960         if (!this.rendered) {return ''};
18961         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18962         
18963     },
18964     // private
18965     onViewMove : function(e, t){
18966         this.inKeyMode = false;
18967     },
18968
18969     // private
18970     onViewOver : function(e, t){
18971         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18972             return;
18973         }
18974         var item = this.view.findItemFromChild(t);
18975         if(item){
18976             var index = this.view.indexOf(item);
18977             this.select(index, false);
18978         }
18979     },
18980
18981     // private
18982     onViewClick : function(doFocus)
18983     {
18984         var index = this.view.getSelectedIndexes()[0];
18985         var r = this.store.getAt(index);
18986         if(r){
18987             this.onSelect(r, index);
18988         }
18989         if(doFocus !== false && !this.blockFocus){
18990             this.el.focus();
18991         }
18992     },
18993
18994     // private
18995     restrictHeight : function(){
18996         this.innerList.dom.style.height = '';
18997         var inner = this.innerList.dom;
18998         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18999         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19000         this.list.beginUpdate();
19001         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19002         this.list.alignTo(this.el, this.listAlign);
19003         this.list.endUpdate();
19004     },
19005
19006     // private
19007     onEmptyResults : function(){
19008         this.collapse();
19009     },
19010
19011     /**
19012      * Returns true if the dropdown list is expanded, else false.
19013      */
19014     isExpanded : function(){
19015         return this.list.isVisible();
19016     },
19017
19018     /**
19019      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19020      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19021      * @param {String} value The data value of the item to select
19022      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19023      * selected item if it is not currently in view (defaults to true)
19024      * @return {Boolean} True if the value matched an item in the list, else false
19025      */
19026     selectByValue : function(v, scrollIntoView){
19027         if(v !== undefined && v !== null){
19028             var r = this.findRecord(this.valueField || this.displayField, v);
19029             if(r){
19030                 this.select(this.store.indexOf(r), scrollIntoView);
19031                 return true;
19032             }
19033         }
19034         return false;
19035     },
19036
19037     /**
19038      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19039      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19040      * @param {Number} index The zero-based index of the list item to select
19041      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19042      * selected item if it is not currently in view (defaults to true)
19043      */
19044     select : function(index, scrollIntoView){
19045         this.selectedIndex = index;
19046         this.view.select(index);
19047         if(scrollIntoView !== false){
19048             var el = this.view.getNode(index);
19049             if(el){
19050                 this.innerList.scrollChildIntoView(el, false);
19051             }
19052         }
19053     },
19054
19055     // private
19056     selectNext : function(){
19057         var ct = this.store.getCount();
19058         if(ct > 0){
19059             if(this.selectedIndex == -1){
19060                 this.select(0);
19061             }else if(this.selectedIndex < ct-1){
19062                 this.select(this.selectedIndex+1);
19063             }
19064         }
19065     },
19066
19067     // private
19068     selectPrev : function(){
19069         var ct = this.store.getCount();
19070         if(ct > 0){
19071             if(this.selectedIndex == -1){
19072                 this.select(0);
19073             }else if(this.selectedIndex != 0){
19074                 this.select(this.selectedIndex-1);
19075             }
19076         }
19077     },
19078
19079     // private
19080     onKeyUp : function(e){
19081         if(this.editable !== false && !e.isSpecialKey()){
19082             this.lastKey = e.getKey();
19083             this.dqTask.delay(this.queryDelay);
19084         }
19085     },
19086
19087     // private
19088     validateBlur : function(){
19089         return !this.list || !this.list.isVisible();   
19090     },
19091
19092     // private
19093     initQuery : function(){
19094         this.doQuery(this.getRawValue());
19095     },
19096
19097     // private
19098     doForce : function(){
19099         if(this.el.dom.value.length > 0){
19100             this.el.dom.value =
19101                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19102              
19103         }
19104     },
19105
19106     /**
19107      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19108      * query allowing the query action to be canceled if needed.
19109      * @param {String} query The SQL query to execute
19110      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19111      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19112      * saved in the current store (defaults to false)
19113      */
19114     doQuery : function(q, forceAll){
19115         if(q === undefined || q === null){
19116             q = '';
19117         }
19118         var qe = {
19119             query: q,
19120             forceAll: forceAll,
19121             combo: this,
19122             cancel:false
19123         };
19124         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19125             return false;
19126         }
19127         q = qe.query;
19128         forceAll = qe.forceAll;
19129         if(forceAll === true || (q.length >= this.minChars)){
19130             if(this.lastQuery != q || this.alwaysQuery){
19131                 this.lastQuery = q;
19132                 if(this.mode == 'local'){
19133                     this.selectedIndex = -1;
19134                     if(forceAll){
19135                         this.store.clearFilter();
19136                     }else{
19137                         this.store.filter(this.displayField, q);
19138                     }
19139                     this.onLoad();
19140                 }else{
19141                     this.store.baseParams[this.queryParam] = q;
19142                     this.store.load({
19143                         params: this.getParams(q)
19144                     });
19145                     this.expand();
19146                 }
19147             }else{
19148                 this.selectedIndex = -1;
19149                 this.onLoad();   
19150             }
19151         }
19152     },
19153
19154     // private
19155     getParams : function(q){
19156         var p = {};
19157         //p[this.queryParam] = q;
19158         if(this.pageSize){
19159             p.start = 0;
19160             p.limit = this.pageSize;
19161         }
19162         return p;
19163     },
19164
19165     /**
19166      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19167      */
19168     collapse : function(){
19169         if(!this.isExpanded()){
19170             return;
19171         }
19172         this.list.hide();
19173         Roo.get(document).un('mousedown', this.collapseIf, this);
19174         Roo.get(document).un('mousewheel', this.collapseIf, this);
19175         if (!this.editable) {
19176             Roo.get(document).un('keydown', this.listKeyPress, this);
19177         }
19178         this.fireEvent('collapse', this);
19179     },
19180
19181     // private
19182     collapseIf : function(e){
19183         if(!e.within(this.wrap) && !e.within(this.list)){
19184             this.collapse();
19185         }
19186     },
19187
19188     /**
19189      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19190      */
19191     expand : function(){
19192         if(this.isExpanded() || !this.hasFocus){
19193             return;
19194         }
19195         this.list.alignTo(this.el, this.listAlign);
19196         this.list.show();
19197         Roo.get(document).on('mousedown', this.collapseIf, this);
19198         Roo.get(document).on('mousewheel', this.collapseIf, this);
19199         if (!this.editable) {
19200             Roo.get(document).on('keydown', this.listKeyPress, this);
19201         }
19202         
19203         this.fireEvent('expand', this);
19204     },
19205
19206     // private
19207     // Implements the default empty TriggerField.onTriggerClick function
19208     onTriggerClick : function(){
19209         if(this.disabled){
19210             return;
19211         }
19212         if(this.isExpanded()){
19213             this.collapse();
19214             if (!this.blockFocus) {
19215                 this.el.focus();
19216             }
19217             
19218         }else {
19219             this.hasFocus = true;
19220             if(this.triggerAction == 'all') {
19221                 this.doQuery(this.allQuery, true);
19222             } else {
19223                 this.doQuery(this.getRawValue());
19224             }
19225             if (!this.blockFocus) {
19226                 this.el.focus();
19227             }
19228         }
19229     },
19230     listKeyPress : function(e)
19231     {
19232         //Roo.log('listkeypress');
19233         // scroll to first matching element based on key pres..
19234         if (e.isSpecialKey()) {
19235             return false;
19236         }
19237         var k = String.fromCharCode(e.getKey()).toUpperCase();
19238         //Roo.log(k);
19239         var match  = false;
19240         var csel = this.view.getSelectedNodes();
19241         var cselitem = false;
19242         if (csel.length) {
19243             var ix = this.view.indexOf(csel[0]);
19244             cselitem  = this.store.getAt(ix);
19245             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19246                 cselitem = false;
19247             }
19248             
19249         }
19250         
19251         this.store.each(function(v) { 
19252             if (cselitem) {
19253                 // start at existing selection.
19254                 if (cselitem.id == v.id) {
19255                     cselitem = false;
19256                 }
19257                 return;
19258             }
19259                 
19260             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19261                 match = this.store.indexOf(v);
19262                 return false;
19263             }
19264         }, this);
19265         
19266         if (match === false) {
19267             return true; // no more action?
19268         }
19269         // scroll to?
19270         this.view.select(match);
19271         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19272         sn.scrollIntoView(sn.dom.parentNode, false);
19273     } 
19274
19275     /** 
19276     * @cfg {Boolean} grow 
19277     * @hide 
19278     */
19279     /** 
19280     * @cfg {Number} growMin 
19281     * @hide 
19282     */
19283     /** 
19284     * @cfg {Number} growMax 
19285     * @hide 
19286     */
19287     /**
19288      * @hide
19289      * @method autoSize
19290      */
19291 });/*
19292  * Copyright(c) 2010-2012, Roo J Solutions Limited
19293  *
19294  * Licence LGPL
19295  *
19296  */
19297
19298 /**
19299  * @class Roo.form.ComboBoxArray
19300  * @extends Roo.form.TextField
19301  * A facebook style adder... for lists of email / people / countries  etc...
19302  * pick multiple items from a combo box, and shows each one.
19303  *
19304  *  Fred [x]  Brian [x]  [Pick another |v]
19305  *
19306  *
19307  *  For this to work: it needs various extra information
19308  *    - normal combo problay has
19309  *      name, hiddenName
19310  *    + displayField, valueField
19311  *
19312  *    For our purpose...
19313  *
19314  *
19315  *   If we change from 'extends' to wrapping...
19316  *   
19317  *  
19318  *
19319  
19320  
19321  * @constructor
19322  * Create a new ComboBoxArray.
19323  * @param {Object} config Configuration options
19324  */
19325  
19326
19327 Roo.form.ComboBoxArray = function(config)
19328 {
19329     this.addEvents({
19330         /**
19331          * @event beforeremove
19332          * Fires before remove the value from the list
19333              * @param {Roo.form.ComboBoxArray} _self This combo box array
19334              * @param {Roo.form.ComboBoxArray.Item} item removed item
19335              */
19336         'beforeremove' : true,
19337         /**
19338          * @event remove
19339          * Fires when remove the value from the list
19340              * @param {Roo.form.ComboBoxArray} _self This combo box array
19341              * @param {Roo.form.ComboBoxArray.Item} item removed item
19342              */
19343         'remove' : true
19344         
19345         
19346     });
19347     
19348     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19349     
19350     this.items = new Roo.util.MixedCollection(false);
19351     
19352     // construct the child combo...
19353     
19354     
19355     
19356     
19357    
19358     
19359 }
19360
19361  
19362 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19363
19364     /**
19365      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19366      */
19367     
19368     lastData : false,
19369     
19370     // behavies liek a hiddne field
19371     inputType:      'hidden',
19372     /**
19373      * @cfg {Number} width The width of the box that displays the selected element
19374      */ 
19375     width:          300,
19376
19377     
19378     
19379     /**
19380      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19381      */
19382     name : false,
19383     /**
19384      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19385      */
19386     hiddenName : false,
19387       /**
19388      * @cfg {String} seperator    The value seperator normally ',' 
19389      */
19390     seperator : ',',
19391     
19392     // private the array of items that are displayed..
19393     items  : false,
19394     // private - the hidden field el.
19395     hiddenEl : false,
19396     // private - the filed el..
19397     el : false,
19398     
19399     //validateValue : function() { return true; }, // all values are ok!
19400     //onAddClick: function() { },
19401     
19402     onRender : function(ct, position) 
19403     {
19404         
19405         // create the standard hidden element
19406         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19407         
19408         
19409         // give fake names to child combo;
19410         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19411         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19412         
19413         this.combo = Roo.factory(this.combo, Roo.form);
19414         this.combo.onRender(ct, position);
19415         if (typeof(this.combo.width) != 'undefined') {
19416             this.combo.onResize(this.combo.width,0);
19417         }
19418         
19419         this.combo.initEvents();
19420         
19421         // assigned so form know we need to do this..
19422         this.store          = this.combo.store;
19423         this.valueField     = this.combo.valueField;
19424         this.displayField   = this.combo.displayField ;
19425         
19426         
19427         this.combo.wrap.addClass('x-cbarray-grp');
19428         
19429         var cbwrap = this.combo.wrap.createChild(
19430             {tag: 'div', cls: 'x-cbarray-cb'},
19431             this.combo.el.dom
19432         );
19433         
19434              
19435         this.hiddenEl = this.combo.wrap.createChild({
19436             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19437         });
19438         this.el = this.combo.wrap.createChild({
19439             tag: 'input',  type:'hidden' , name: this.name, value : ''
19440         });
19441          //   this.el.dom.removeAttribute("name");
19442         
19443         
19444         this.outerWrap = this.combo.wrap;
19445         this.wrap = cbwrap;
19446         
19447         this.outerWrap.setWidth(this.width);
19448         this.outerWrap.dom.removeChild(this.el.dom);
19449         
19450         this.wrap.dom.appendChild(this.el.dom);
19451         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19452         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19453         
19454         this.combo.trigger.setStyle('position','relative');
19455         this.combo.trigger.setStyle('left', '0px');
19456         this.combo.trigger.setStyle('top', '2px');
19457         
19458         this.combo.el.setStyle('vertical-align', 'text-bottom');
19459         
19460         //this.trigger.setStyle('vertical-align', 'top');
19461         
19462         // this should use the code from combo really... on('add' ....)
19463         if (this.adder) {
19464             
19465         
19466             this.adder = this.outerWrap.createChild(
19467                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19468             var _t = this;
19469             this.adder.on('click', function(e) {
19470                 _t.fireEvent('adderclick', this, e);
19471             }, _t);
19472         }
19473         //var _t = this;
19474         //this.adder.on('click', this.onAddClick, _t);
19475         
19476         
19477         this.combo.on('select', function(cb, rec, ix) {
19478             this.addItem(rec.data);
19479             
19480             cb.setValue('');
19481             cb.el.dom.value = '';
19482             //cb.lastData = rec.data;
19483             // add to list
19484             
19485         }, this);
19486         
19487         
19488     },
19489     
19490     
19491     getName: function()
19492     {
19493         // returns hidden if it's set..
19494         if (!this.rendered) {return ''};
19495         return  this.hiddenName ? this.hiddenName : this.name;
19496         
19497     },
19498     
19499     
19500     onResize: function(w, h){
19501         
19502         return;
19503         // not sure if this is needed..
19504         //this.combo.onResize(w,h);
19505         
19506         if(typeof w != 'number'){
19507             // we do not handle it!?!?
19508             return;
19509         }
19510         var tw = this.combo.trigger.getWidth();
19511         tw += this.addicon ? this.addicon.getWidth() : 0;
19512         tw += this.editicon ? this.editicon.getWidth() : 0;
19513         var x = w - tw;
19514         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19515             
19516         this.combo.trigger.setStyle('left', '0px');
19517         
19518         if(this.list && this.listWidth === undefined){
19519             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19520             this.list.setWidth(lw);
19521             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19522         }
19523         
19524     
19525         
19526     },
19527     
19528     addItem: function(rec)
19529     {
19530         var valueField = this.combo.valueField;
19531         var displayField = this.combo.displayField;
19532         
19533         if (this.items.indexOfKey(rec[valueField]) > -1) {
19534             //console.log("GOT " + rec.data.id);
19535             return;
19536         }
19537         
19538         var x = new Roo.form.ComboBoxArray.Item({
19539             //id : rec[this.idField],
19540             data : rec,
19541             displayField : displayField ,
19542             tipField : displayField ,
19543             cb : this
19544         });
19545         // use the 
19546         this.items.add(rec[valueField],x);
19547         // add it before the element..
19548         this.updateHiddenEl();
19549         x.render(this.outerWrap, this.wrap.dom);
19550         // add the image handler..
19551     },
19552     
19553     updateHiddenEl : function()
19554     {
19555         this.validate();
19556         if (!this.hiddenEl) {
19557             return;
19558         }
19559         var ar = [];
19560         var idField = this.combo.valueField;
19561         
19562         this.items.each(function(f) {
19563             ar.push(f.data[idField]);
19564         });
19565         this.hiddenEl.dom.value = ar.join(this.seperator);
19566         this.validate();
19567     },
19568     
19569     reset : function()
19570     {
19571         this.items.clear();
19572         
19573         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19574            el.remove();
19575         });
19576         
19577         this.el.dom.value = '';
19578         if (this.hiddenEl) {
19579             this.hiddenEl.dom.value = '';
19580         }
19581         
19582     },
19583     getValue: function()
19584     {
19585         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19586     },
19587     setValue: function(v) // not a valid action - must use addItems..
19588     {
19589         
19590         this.reset();
19591          
19592         if (this.store.isLocal && (typeof(v) == 'string')) {
19593             // then we can use the store to find the values..
19594             // comma seperated at present.. this needs to allow JSON based encoding..
19595             this.hiddenEl.value  = v;
19596             var v_ar = [];
19597             Roo.each(v.split(this.seperator), function(k) {
19598                 Roo.log("CHECK " + this.valueField + ',' + k);
19599                 var li = this.store.query(this.valueField, k);
19600                 if (!li.length) {
19601                     return;
19602                 }
19603                 var add = {};
19604                 add[this.valueField] = k;
19605                 add[this.displayField] = li.item(0).data[this.displayField];
19606                 
19607                 this.addItem(add);
19608             }, this) 
19609              
19610         }
19611         if (typeof(v) == 'object' ) {
19612             // then let's assume it's an array of objects..
19613             Roo.each(v, function(l) {
19614                 var add = l;
19615                 if (typeof(l) == 'string') {
19616                     add = {};
19617                     add[this.valueField] = l;
19618                     add[this.displayField] = l
19619                 }
19620                 this.addItem(add);
19621             }, this);
19622              
19623         }
19624         
19625         
19626     },
19627     setFromData: function(v)
19628     {
19629         // this recieves an object, if setValues is called.
19630         this.reset();
19631         this.el.dom.value = v[this.displayField];
19632         this.hiddenEl.dom.value = v[this.valueField];
19633         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19634             return;
19635         }
19636         var kv = v[this.valueField];
19637         var dv = v[this.displayField];
19638         kv = typeof(kv) != 'string' ? '' : kv;
19639         dv = typeof(dv) != 'string' ? '' : dv;
19640         
19641         
19642         var keys = kv.split(this.seperator);
19643         var display = dv.split(this.seperator);
19644         for (var i = 0 ; i < keys.length; i++) {
19645             add = {};
19646             add[this.valueField] = keys[i];
19647             add[this.displayField] = display[i];
19648             this.addItem(add);
19649         }
19650       
19651         
19652     },
19653     
19654     /**
19655      * Validates the combox array value
19656      * @return {Boolean} True if the value is valid, else false
19657      */
19658     validate : function(){
19659         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19660             this.clearInvalid();
19661             return true;
19662         }
19663         return false;
19664     },
19665     
19666     validateValue : function(value){
19667         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19668         
19669     },
19670     
19671     /*@
19672      * overide
19673      * 
19674      */
19675     isDirty : function() {
19676         if(this.disabled) {
19677             return false;
19678         }
19679         
19680         try {
19681             var d = Roo.decode(String(this.originalValue));
19682         } catch (e) {
19683             return String(this.getValue()) !== String(this.originalValue);
19684         }
19685         
19686         var originalValue = [];
19687         
19688         for (var i = 0; i < d.length; i++){
19689             originalValue.push(d[i][this.valueField]);
19690         }
19691         
19692         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19693         
19694     }
19695     
19696 });
19697
19698
19699
19700 /**
19701  * @class Roo.form.ComboBoxArray.Item
19702  * @extends Roo.BoxComponent
19703  * A selected item in the list
19704  *  Fred [x]  Brian [x]  [Pick another |v]
19705  * 
19706  * @constructor
19707  * Create a new item.
19708  * @param {Object} config Configuration options
19709  */
19710  
19711 Roo.form.ComboBoxArray.Item = function(config) {
19712     config.id = Roo.id();
19713     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19714 }
19715
19716 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19717     data : {},
19718     cb: false,
19719     displayField : false,
19720     tipField : false,
19721     
19722     
19723     defaultAutoCreate : {
19724         tag: 'div',
19725         cls: 'x-cbarray-item',
19726         cn : [ 
19727             { tag: 'div' },
19728             {
19729                 tag: 'img',
19730                 width:16,
19731                 height : 16,
19732                 src : Roo.BLANK_IMAGE_URL ,
19733                 align: 'center'
19734             }
19735         ]
19736         
19737     },
19738     
19739  
19740     onRender : function(ct, position)
19741     {
19742         Roo.form.Field.superclass.onRender.call(this, ct, position);
19743         
19744         if(!this.el){
19745             var cfg = this.getAutoCreate();
19746             this.el = ct.createChild(cfg, position);
19747         }
19748         
19749         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19750         
19751         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19752             this.cb.renderer(this.data) :
19753             String.format('{0}',this.data[this.displayField]);
19754         
19755             
19756         this.el.child('div').dom.setAttribute('qtip',
19757                         String.format('{0}',this.data[this.tipField])
19758         );
19759         
19760         this.el.child('img').on('click', this.remove, this);
19761         
19762     },
19763    
19764     remove : function()
19765     {
19766         if(this.cb.disabled){
19767             return;
19768         }
19769         
19770         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19771             this.cb.items.remove(this);
19772             this.el.child('img').un('click', this.remove, this);
19773             this.el.remove();
19774             this.cb.updateHiddenEl();
19775
19776             this.cb.fireEvent('remove', this.cb, this);
19777         }
19778         
19779     }
19780 });/*
19781  * RooJS Library 1.1.1
19782  * Copyright(c) 2008-2011  Alan Knowles
19783  *
19784  * License - LGPL
19785  */
19786  
19787
19788 /**
19789  * @class Roo.form.ComboNested
19790  * @extends Roo.form.ComboBox
19791  * A combobox for that allows selection of nested items in a list,
19792  * eg.
19793  *
19794  *  Book
19795  *    -> red
19796  *    -> green
19797  *  Table
19798  *    -> square
19799  *      ->red
19800  *      ->green
19801  *    -> rectangle
19802  *      ->green
19803  *      
19804  * 
19805  * @constructor
19806  * Create a new ComboNested
19807  * @param {Object} config Configuration options
19808  */
19809 Roo.form.ComboNested = function(config){
19810     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19811     // should verify some data...
19812     // like
19813     // hiddenName = required..
19814     // displayField = required
19815     // valudField == required
19816     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19817     var _t = this;
19818     Roo.each(req, function(e) {
19819         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19820             throw "Roo.form.ComboNested : missing value for: " + e;
19821         }
19822     });
19823      
19824     
19825 };
19826
19827 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19828    
19829     /*
19830      * @config {Number} max Number of columns to show
19831      */
19832     
19833     maxColumns : 3,
19834    
19835     list : null, // the outermost div..
19836     innerLists : null, // the
19837     views : null,
19838     stores : null,
19839     // private
19840     loadingChildren : false,
19841     
19842     onRender : function(ct, position)
19843     {
19844         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19845         
19846         if(this.hiddenName){
19847             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19848                     'before', true);
19849             this.hiddenField.value =
19850                 this.hiddenValue !== undefined ? this.hiddenValue :
19851                 this.value !== undefined ? this.value : '';
19852
19853             // prevent input submission
19854             this.el.dom.removeAttribute('name');
19855              
19856              
19857         }
19858         
19859         if(Roo.isGecko){
19860             this.el.dom.setAttribute('autocomplete', 'off');
19861         }
19862
19863         var cls = 'x-combo-list';
19864
19865         this.list = new Roo.Layer({
19866             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19867         });
19868
19869         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19870         this.list.setWidth(lw);
19871         this.list.swallowEvent('mousewheel');
19872         this.assetHeight = 0;
19873
19874         if(this.title){
19875             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19876             this.assetHeight += this.header.getHeight();
19877         }
19878         this.innerLists = [];
19879         this.views = [];
19880         this.stores = [];
19881         for (var i =0 ; i < this.maxColumns; i++) {
19882             this.onRenderList( cls, i);
19883         }
19884         
19885         // always needs footer, as we are going to have an 'OK' button.
19886         this.footer = this.list.createChild({cls:cls+'-ft'});
19887         this.pageTb = new Roo.Toolbar(this.footer);  
19888         var _this = this;
19889         this.pageTb.add(  {
19890             
19891             text: 'Done',
19892             handler: function()
19893             {
19894                 _this.collapse();
19895             }
19896         });
19897         
19898         if ( this.allowBlank && !this.disableClear) {
19899             
19900             this.pageTb.add(new Roo.Toolbar.Fill(), {
19901                 cls: 'x-btn-icon x-btn-clear',
19902                 text: '&#160;',
19903                 handler: function()
19904                 {
19905                     _this.collapse();
19906                     _this.clearValue();
19907                     _this.onSelect(false, -1);
19908                 }
19909             });
19910         }
19911         if (this.footer) {
19912             this.assetHeight += this.footer.getHeight();
19913         }
19914         
19915     },
19916     onRenderList : function (  cls, i)
19917     {
19918         
19919         var lw = Math.floor(
19920                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19921         );
19922         
19923         this.list.setWidth(lw); // default to '1'
19924
19925         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19926         //il.on('mouseover', this.onViewOver, this, { list:  i });
19927         //il.on('mousemove', this.onViewMove, this, { list:  i });
19928         il.setWidth(lw);
19929         il.setStyle({ 'overflow-x' : 'hidden'});
19930
19931         if(!this.tpl){
19932             this.tpl = new Roo.Template({
19933                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19934                 isEmpty: function (value, allValues) {
19935                     //Roo.log(value);
19936                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19937                     return dl ? 'has-children' : 'no-children'
19938                 }
19939             });
19940         }
19941         
19942         var store  = this.store;
19943         if (i > 0) {
19944             store  = new Roo.data.SimpleStore({
19945                 //fields : this.store.reader.meta.fields,
19946                 reader : this.store.reader,
19947                 data : [ ]
19948             });
19949         }
19950         this.stores[i]  = store;
19951                   
19952         var view = this.views[i] = new Roo.View(
19953             il,
19954             this.tpl,
19955             {
19956                 singleSelect:true,
19957                 store: store,
19958                 selectedClass: this.selectedClass
19959             }
19960         );
19961         view.getEl().setWidth(lw);
19962         view.getEl().setStyle({
19963             position: i < 1 ? 'relative' : 'absolute',
19964             top: 0,
19965             left: (i * lw ) + 'px',
19966             display : i > 0 ? 'none' : 'block'
19967         });
19968         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
19969         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
19970         //view.on('click', this.onViewClick, this, { list : i });
19971
19972         store.on('beforeload', this.onBeforeLoad, this);
19973         store.on('load',  this.onLoad, this, { list  : i});
19974         store.on('loadexception', this.onLoadException, this);
19975
19976         // hide the other vies..
19977         
19978         
19979         
19980     },
19981       
19982     restrictHeight : function()
19983     {
19984         var mh = 0;
19985         Roo.each(this.innerLists, function(il,i) {
19986             var el = this.views[i].getEl();
19987             el.dom.style.height = '';
19988             var inner = el.dom;
19989             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
19990             // only adjust heights on other ones..
19991             mh = Math.max(h, mh);
19992             if (i < 1) {
19993                 
19994                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19995                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19996                
19997             }
19998             
19999             
20000         }, this);
20001         
20002         this.list.beginUpdate();
20003         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20004         this.list.alignTo(this.el, this.listAlign);
20005         this.list.endUpdate();
20006         
20007     },
20008      
20009     
20010     // -- store handlers..
20011     // private
20012     onBeforeLoad : function()
20013     {
20014         if(!this.hasFocus){
20015             return;
20016         }
20017         this.innerLists[0].update(this.loadingText ?
20018                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20019         this.restrictHeight();
20020         this.selectedIndex = -1;
20021     },
20022     // private
20023     onLoad : function(a,b,c,d)
20024     {
20025         if (!this.loadingChildren) {
20026             // then we are loading the top level. - hide the children
20027             for (var i = 1;i < this.views.length; i++) {
20028                 this.views[i].getEl().setStyle({ display : 'none' });
20029             }
20030             var lw = Math.floor(
20031                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20032             );
20033         
20034              this.list.setWidth(lw); // default to '1'
20035
20036             
20037         }
20038         if(!this.hasFocus){
20039             return;
20040         }
20041         
20042         if(this.store.getCount() > 0) {
20043             this.expand();
20044             this.restrictHeight();   
20045         } else {
20046             this.onEmptyResults();
20047         }
20048         
20049         if (!this.loadingChildren) {
20050             this.selectActive();
20051         }
20052         /*
20053         this.stores[1].loadData([]);
20054         this.stores[2].loadData([]);
20055         this.views
20056         */    
20057     
20058         //this.el.focus();
20059     },
20060     
20061     
20062     // private
20063     onLoadException : function()
20064     {
20065         this.collapse();
20066         Roo.log(this.store.reader.jsonData);
20067         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20068             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20069         }
20070         
20071         
20072     },
20073     // no cleaning of leading spaces on blur here.
20074     cleanLeadingSpace : function(e) { },
20075     
20076
20077     onSelectChange : function (view, sels, opts )
20078     {
20079         var ix = view.getSelectedIndexes();
20080          
20081         if (opts.list > this.maxColumns - 2) {
20082             if (view.store.getCount()<  1) {
20083                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20084
20085             } else  {
20086                 if (ix.length) {
20087                     // used to clear ?? but if we are loading unselected 
20088                     this.setFromData(view.store.getAt(ix[0]).data);
20089                 }
20090                 
20091             }
20092             
20093             return;
20094         }
20095         
20096         if (!ix.length) {
20097             // this get's fired when trigger opens..
20098            // this.setFromData({});
20099             var str = this.stores[opts.list+1];
20100             str.data.clear(); // removeall wihtout the fire events..
20101             return;
20102         }
20103         
20104         var rec = view.store.getAt(ix[0]);
20105          
20106         this.setFromData(rec.data);
20107         this.fireEvent('select', this, rec, ix[0]);
20108         
20109         var lw = Math.floor(
20110              (
20111                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20112              ) / this.maxColumns
20113         );
20114         this.loadingChildren = true;
20115         this.stores[opts.list+1].loadDataFromChildren( rec );
20116         this.loadingChildren = false;
20117         var dl = this.stores[opts.list+1]. getTotalCount();
20118         
20119         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20120         
20121         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20122         for (var i = opts.list+2; i < this.views.length;i++) {
20123             this.views[i].getEl().setStyle({ display : 'none' });
20124         }
20125         
20126         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20127         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20128         
20129         if (this.isLoading) {
20130            // this.selectActive(opts.list);
20131         }
20132          
20133     },
20134     
20135     
20136     
20137     
20138     onDoubleClick : function()
20139     {
20140         this.collapse(); //??
20141     },
20142     
20143      
20144     
20145     
20146     
20147     // private
20148     recordToStack : function(store, prop, value, stack)
20149     {
20150         var cstore = new Roo.data.SimpleStore({
20151             //fields : this.store.reader.meta.fields, // we need array reader.. for
20152             reader : this.store.reader,
20153             data : [ ]
20154         });
20155         var _this = this;
20156         var record  = false;
20157         var srec = false;
20158         if(store.getCount() < 1){
20159             return false;
20160         }
20161         store.each(function(r){
20162             if(r.data[prop] == value){
20163                 record = r;
20164             srec = r;
20165                 return false;
20166             }
20167             if (r.data.cn && r.data.cn.length) {
20168                 cstore.loadDataFromChildren( r);
20169                 var cret = _this.recordToStack(cstore, prop, value, stack);
20170                 if (cret !== false) {
20171                     record = cret;
20172                     srec = r;
20173                     return false;
20174                 }
20175             }
20176              
20177             return true;
20178         });
20179         if (record == false) {
20180             return false
20181         }
20182         stack.unshift(srec);
20183         return record;
20184     },
20185     
20186     /*
20187      * find the stack of stores that match our value.
20188      *
20189      * 
20190      */
20191     
20192     selectActive : function ()
20193     {
20194         // if store is not loaded, then we will need to wait for that to happen first.
20195         var stack = [];
20196         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20197         for (var i = 0; i < stack.length; i++ ) {
20198             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20199         }
20200         
20201     }
20202         
20203          
20204     
20205     
20206     
20207     
20208 });/*
20209  * Based on:
20210  * Ext JS Library 1.1.1
20211  * Copyright(c) 2006-2007, Ext JS, LLC.
20212  *
20213  * Originally Released Under LGPL - original licence link has changed is not relivant.
20214  *
20215  * Fork - LGPL
20216  * <script type="text/javascript">
20217  */
20218 /**
20219  * @class Roo.form.Checkbox
20220  * @extends Roo.form.Field
20221  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20222  * @constructor
20223  * Creates a new Checkbox
20224  * @param {Object} config Configuration options
20225  */
20226 Roo.form.Checkbox = function(config){
20227     Roo.form.Checkbox.superclass.constructor.call(this, config);
20228     this.addEvents({
20229         /**
20230          * @event check
20231          * Fires when the checkbox is checked or unchecked.
20232              * @param {Roo.form.Checkbox} this This checkbox
20233              * @param {Boolean} checked The new checked value
20234              */
20235         check : true
20236     });
20237 };
20238
20239 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20240     /**
20241      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20242      */
20243     focusClass : undefined,
20244     /**
20245      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20246      */
20247     fieldClass: "x-form-field",
20248     /**
20249      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20250      */
20251     checked: false,
20252     /**
20253      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20254      * {tag: "input", type: "checkbox", autocomplete: "off"})
20255      */
20256     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20257     /**
20258      * @cfg {String} boxLabel The text that appears beside the checkbox
20259      */
20260     boxLabel : "",
20261     /**
20262      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20263      */  
20264     inputValue : '1',
20265     /**
20266      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20267      */
20268      valueOff: '0', // value when not checked..
20269
20270     actionMode : 'viewEl', 
20271     //
20272     // private
20273     itemCls : 'x-menu-check-item x-form-item',
20274     groupClass : 'x-menu-group-item',
20275     inputType : 'hidden',
20276     
20277     
20278     inSetChecked: false, // check that we are not calling self...
20279     
20280     inputElement: false, // real input element?
20281     basedOn: false, // ????
20282     
20283     isFormField: true, // not sure where this is needed!!!!
20284
20285     onResize : function(){
20286         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20287         if(!this.boxLabel){
20288             this.el.alignTo(this.wrap, 'c-c');
20289         }
20290     },
20291
20292     initEvents : function(){
20293         Roo.form.Checkbox.superclass.initEvents.call(this);
20294         this.el.on("click", this.onClick,  this);
20295         this.el.on("change", this.onClick,  this);
20296     },
20297
20298
20299     getResizeEl : function(){
20300         return this.wrap;
20301     },
20302
20303     getPositionEl : function(){
20304         return this.wrap;
20305     },
20306
20307     // private
20308     onRender : function(ct, position){
20309         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20310         /*
20311         if(this.inputValue !== undefined){
20312             this.el.dom.value = this.inputValue;
20313         }
20314         */
20315         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20316         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20317         var viewEl = this.wrap.createChild({ 
20318             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20319         this.viewEl = viewEl;   
20320         this.wrap.on('click', this.onClick,  this); 
20321         
20322         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20323         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20324         
20325         
20326         
20327         if(this.boxLabel){
20328             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20329         //    viewEl.on('click', this.onClick,  this); 
20330         }
20331         //if(this.checked){
20332             this.setChecked(this.checked);
20333         //}else{
20334             //this.checked = this.el.dom;
20335         //}
20336
20337     },
20338
20339     // private
20340     initValue : Roo.emptyFn,
20341
20342     /**
20343      * Returns the checked state of the checkbox.
20344      * @return {Boolean} True if checked, else false
20345      */
20346     getValue : function(){
20347         if(this.el){
20348             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20349         }
20350         return this.valueOff;
20351         
20352     },
20353
20354         // private
20355     onClick : function(){ 
20356         if (this.disabled) {
20357             return;
20358         }
20359         this.setChecked(!this.checked);
20360
20361         //if(this.el.dom.checked != this.checked){
20362         //    this.setValue(this.el.dom.checked);
20363        // }
20364     },
20365
20366     /**
20367      * Sets the checked state of the checkbox.
20368      * On is always based on a string comparison between inputValue and the param.
20369      * @param {Boolean/String} value - the value to set 
20370      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20371      */
20372     setValue : function(v,suppressEvent){
20373         
20374         
20375         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20376         //if(this.el && this.el.dom){
20377         //    this.el.dom.checked = this.checked;
20378         //    this.el.dom.defaultChecked = this.checked;
20379         //}
20380         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20381         //this.fireEvent("check", this, this.checked);
20382     },
20383     // private..
20384     setChecked : function(state,suppressEvent)
20385     {
20386         if (this.inSetChecked) {
20387             this.checked = state;
20388             return;
20389         }
20390         
20391     
20392         if(this.wrap){
20393             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20394         }
20395         this.checked = state;
20396         if(suppressEvent !== true){
20397             this.fireEvent('check', this, state);
20398         }
20399         this.inSetChecked = true;
20400         this.el.dom.value = state ? this.inputValue : this.valueOff;
20401         this.inSetChecked = false;
20402         
20403     },
20404     // handle setting of hidden value by some other method!!?!?
20405     setFromHidden: function()
20406     {
20407         if(!this.el){
20408             return;
20409         }
20410         //console.log("SET FROM HIDDEN");
20411         //alert('setFrom hidden');
20412         this.setValue(this.el.dom.value);
20413     },
20414     
20415     onDestroy : function()
20416     {
20417         if(this.viewEl){
20418             Roo.get(this.viewEl).remove();
20419         }
20420          
20421         Roo.form.Checkbox.superclass.onDestroy.call(this);
20422     },
20423     
20424     setBoxLabel : function(str)
20425     {
20426         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20427     }
20428
20429 });/*
20430  * Based on:
20431  * Ext JS Library 1.1.1
20432  * Copyright(c) 2006-2007, Ext JS, LLC.
20433  *
20434  * Originally Released Under LGPL - original licence link has changed is not relivant.
20435  *
20436  * Fork - LGPL
20437  * <script type="text/javascript">
20438  */
20439  
20440 /**
20441  * @class Roo.form.Radio
20442  * @extends Roo.form.Checkbox
20443  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20444  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20445  * @constructor
20446  * Creates a new Radio
20447  * @param {Object} config Configuration options
20448  */
20449 Roo.form.Radio = function(){
20450     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20451 };
20452 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20453     inputType: 'radio',
20454
20455     /**
20456      * If this radio is part of a group, it will return the selected value
20457      * @return {String}
20458      */
20459     getGroupValue : function(){
20460         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20461     },
20462     
20463     
20464     onRender : function(ct, position){
20465         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20466         
20467         if(this.inputValue !== undefined){
20468             this.el.dom.value = this.inputValue;
20469         }
20470          
20471         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20472         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20473         //var viewEl = this.wrap.createChild({ 
20474         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20475         //this.viewEl = viewEl;   
20476         //this.wrap.on('click', this.onClick,  this); 
20477         
20478         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20479         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20480         
20481         
20482         
20483         if(this.boxLabel){
20484             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20485         //    viewEl.on('click', this.onClick,  this); 
20486         }
20487          if(this.checked){
20488             this.el.dom.checked =   'checked' ;
20489         }
20490          
20491     } 
20492     
20493     
20494 });//<script type="text/javascript">
20495
20496 /*
20497  * Based  Ext JS Library 1.1.1
20498  * Copyright(c) 2006-2007, Ext JS, LLC.
20499  * LGPL
20500  *
20501  */
20502  
20503 /**
20504  * @class Roo.HtmlEditorCore
20505  * @extends Roo.Component
20506  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20507  *
20508  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20509  */
20510
20511 Roo.HtmlEditorCore = function(config){
20512     
20513     
20514     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20515     
20516     
20517     this.addEvents({
20518         /**
20519          * @event initialize
20520          * Fires when the editor is fully initialized (including the iframe)
20521          * @param {Roo.HtmlEditorCore} this
20522          */
20523         initialize: true,
20524         /**
20525          * @event activate
20526          * Fires when the editor is first receives the focus. Any insertion must wait
20527          * until after this event.
20528          * @param {Roo.HtmlEditorCore} this
20529          */
20530         activate: true,
20531          /**
20532          * @event beforesync
20533          * Fires before the textarea is updated with content from the editor iframe. Return false
20534          * to cancel the sync.
20535          * @param {Roo.HtmlEditorCore} this
20536          * @param {String} html
20537          */
20538         beforesync: true,
20539          /**
20540          * @event beforepush
20541          * Fires before the iframe editor is updated with content from the textarea. Return false
20542          * to cancel the push.
20543          * @param {Roo.HtmlEditorCore} this
20544          * @param {String} html
20545          */
20546         beforepush: true,
20547          /**
20548          * @event sync
20549          * Fires when the textarea is updated with content from the editor iframe.
20550          * @param {Roo.HtmlEditorCore} this
20551          * @param {String} html
20552          */
20553         sync: true,
20554          /**
20555          * @event push
20556          * Fires when the iframe editor is updated with content from the textarea.
20557          * @param {Roo.HtmlEditorCore} this
20558          * @param {String} html
20559          */
20560         push: true,
20561         
20562         /**
20563          * @event editorevent
20564          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20565          * @param {Roo.HtmlEditorCore} this
20566          */
20567         editorevent: true
20568         
20569     });
20570     
20571     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20572     
20573     // defaults : white / black...
20574     this.applyBlacklists();
20575     
20576     
20577     
20578 };
20579
20580
20581 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20582
20583
20584      /**
20585      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20586      */
20587     
20588     owner : false,
20589     
20590      /**
20591      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20592      *                        Roo.resizable.
20593      */
20594     resizable : false,
20595      /**
20596      * @cfg {Number} height (in pixels)
20597      */   
20598     height: 300,
20599    /**
20600      * @cfg {Number} width (in pixels)
20601      */   
20602     width: 500,
20603     
20604     /**
20605      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20606      * 
20607      */
20608     stylesheets: false,
20609     
20610     // id of frame..
20611     frameId: false,
20612     
20613     // private properties
20614     validationEvent : false,
20615     deferHeight: true,
20616     initialized : false,
20617     activated : false,
20618     sourceEditMode : false,
20619     onFocus : Roo.emptyFn,
20620     iframePad:3,
20621     hideMode:'offsets',
20622     
20623     clearUp: true,
20624     
20625     // blacklist + whitelisted elements..
20626     black: false,
20627     white: false,
20628      
20629     bodyCls : '',
20630
20631     /**
20632      * Protected method that will not generally be called directly. It
20633      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20634      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20635      */
20636     getDocMarkup : function(){
20637         // body styles..
20638         var st = '';
20639         
20640         // inherit styels from page...?? 
20641         if (this.stylesheets === false) {
20642             
20643             Roo.get(document.head).select('style').each(function(node) {
20644                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20645             });
20646             
20647             Roo.get(document.head).select('link').each(function(node) { 
20648                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20649             });
20650             
20651         } else if (!this.stylesheets.length) {
20652                 // simple..
20653                 st = '<style type="text/css">' +
20654                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20655                    '</style>';
20656         } else {
20657             for (var i in this.stylesheets) { 
20658                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
20659             }
20660             
20661         }
20662         
20663         st +=  '<style type="text/css">' +
20664             'IMG { cursor: pointer } ' +
20665         '</style>';
20666
20667         var cls = 'roo-htmleditor-body';
20668         
20669         if(this.bodyCls.length){
20670             cls += ' ' + this.bodyCls;
20671         }
20672         
20673         return '<html><head>' + st  +
20674             //<style type="text/css">' +
20675             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20676             //'</style>' +
20677             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
20678     },
20679
20680     // private
20681     onRender : function(ct, position)
20682     {
20683         var _t = this;
20684         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20685         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20686         
20687         
20688         this.el.dom.style.border = '0 none';
20689         this.el.dom.setAttribute('tabIndex', -1);
20690         this.el.addClass('x-hidden hide');
20691         
20692         
20693         
20694         if(Roo.isIE){ // fix IE 1px bogus margin
20695             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20696         }
20697        
20698         
20699         this.frameId = Roo.id();
20700         
20701          
20702         
20703         var iframe = this.owner.wrap.createChild({
20704             tag: 'iframe',
20705             cls: 'form-control', // bootstrap..
20706             id: this.frameId,
20707             name: this.frameId,
20708             frameBorder : 'no',
20709             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20710         }, this.el
20711         );
20712         
20713         
20714         this.iframe = iframe.dom;
20715
20716          this.assignDocWin();
20717         
20718         this.doc.designMode = 'on';
20719        
20720         this.doc.open();
20721         this.doc.write(this.getDocMarkup());
20722         this.doc.close();
20723
20724         
20725         var task = { // must defer to wait for browser to be ready
20726             run : function(){
20727                 //console.log("run task?" + this.doc.readyState);
20728                 this.assignDocWin();
20729                 if(this.doc.body || this.doc.readyState == 'complete'){
20730                     try {
20731                         this.doc.designMode="on";
20732                     } catch (e) {
20733                         return;
20734                     }
20735                     Roo.TaskMgr.stop(task);
20736                     this.initEditor.defer(10, this);
20737                 }
20738             },
20739             interval : 10,
20740             duration: 10000,
20741             scope: this
20742         };
20743         Roo.TaskMgr.start(task);
20744
20745     },
20746
20747     // private
20748     onResize : function(w, h)
20749     {
20750          Roo.log('resize: ' +w + ',' + h );
20751         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20752         if(!this.iframe){
20753             return;
20754         }
20755         if(typeof w == 'number'){
20756             
20757             this.iframe.style.width = w + 'px';
20758         }
20759         if(typeof h == 'number'){
20760             
20761             this.iframe.style.height = h + 'px';
20762             if(this.doc){
20763                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20764             }
20765         }
20766         
20767     },
20768
20769     /**
20770      * Toggles the editor between standard and source edit mode.
20771      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20772      */
20773     toggleSourceEdit : function(sourceEditMode){
20774         
20775         this.sourceEditMode = sourceEditMode === true;
20776         
20777         if(this.sourceEditMode){
20778  
20779             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20780             
20781         }else{
20782             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20783             //this.iframe.className = '';
20784             this.deferFocus();
20785         }
20786         //this.setSize(this.owner.wrap.getSize());
20787         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20788     },
20789
20790     
20791   
20792
20793     /**
20794      * Protected method that will not generally be called directly. If you need/want
20795      * custom HTML cleanup, this is the method you should override.
20796      * @param {String} html The HTML to be cleaned
20797      * return {String} The cleaned HTML
20798      */
20799     cleanHtml : function(html){
20800         html = String(html);
20801         if(html.length > 5){
20802             if(Roo.isSafari){ // strip safari nonsense
20803                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20804             }
20805         }
20806         if(html == '&nbsp;'){
20807             html = '';
20808         }
20809         return html;
20810     },
20811
20812     /**
20813      * HTML Editor -> Textarea
20814      * Protected method that will not generally be called directly. Syncs the contents
20815      * of the editor iframe with the textarea.
20816      */
20817     syncValue : function(){
20818         if(this.initialized){
20819             var bd = (this.doc.body || this.doc.documentElement);
20820             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20821             var html = bd.innerHTML;
20822             if(Roo.isSafari){
20823                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20824                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20825                 if(m && m[1]){
20826                     html = '<div style="'+m[0]+'">' + html + '</div>';
20827                 }
20828             }
20829             html = this.cleanHtml(html);
20830             // fix up the special chars.. normaly like back quotes in word...
20831             // however we do not want to do this with chinese..
20832             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20833                 
20834                 var cc = match.charCodeAt();
20835
20836                 // Get the character value, handling surrogate pairs
20837                 if (match.length == 2) {
20838                     // It's a surrogate pair, calculate the Unicode code point
20839                     var high = match.charCodeAt(0) - 0xD800;
20840                     var low  = match.charCodeAt(1) - 0xDC00;
20841                     cc = (high * 0x400) + low + 0x10000;
20842                 }  else if (
20843                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20844                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20845                     (cc >= 0xf900 && cc < 0xfb00 )
20846                 ) {
20847                         return match;
20848                 }  
20849          
20850                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20851                 return "&#" + cc + ";";
20852                 
20853                 
20854             });
20855             
20856             
20857              
20858             if(this.owner.fireEvent('beforesync', this, html) !== false){
20859                 this.el.dom.value = html;
20860                 this.owner.fireEvent('sync', this, html);
20861             }
20862         }
20863     },
20864
20865     /**
20866      * Protected method that will not generally be called directly. Pushes the value of the textarea
20867      * into the iframe editor.
20868      */
20869     pushValue : function(){
20870         if(this.initialized){
20871             var v = this.el.dom.value.trim();
20872             
20873 //            if(v.length < 1){
20874 //                v = '&#160;';
20875 //            }
20876             
20877             if(this.owner.fireEvent('beforepush', this, v) !== false){
20878                 var d = (this.doc.body || this.doc.documentElement);
20879                 d.innerHTML = v;
20880                 this.cleanUpPaste();
20881                 this.el.dom.value = d.innerHTML;
20882                 this.owner.fireEvent('push', this, v);
20883             }
20884         }
20885     },
20886
20887     // private
20888     deferFocus : function(){
20889         this.focus.defer(10, this);
20890     },
20891
20892     // doc'ed in Field
20893     focus : function(){
20894         if(this.win && !this.sourceEditMode){
20895             this.win.focus();
20896         }else{
20897             this.el.focus();
20898         }
20899     },
20900     
20901     assignDocWin: function()
20902     {
20903         var iframe = this.iframe;
20904         
20905          if(Roo.isIE){
20906             this.doc = iframe.contentWindow.document;
20907             this.win = iframe.contentWindow;
20908         } else {
20909 //            if (!Roo.get(this.frameId)) {
20910 //                return;
20911 //            }
20912 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20913 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20914             
20915             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20916                 return;
20917             }
20918             
20919             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20920             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20921         }
20922     },
20923     
20924     // private
20925     initEditor : function(){
20926         //console.log("INIT EDITOR");
20927         this.assignDocWin();
20928         
20929         
20930         
20931         this.doc.designMode="on";
20932         this.doc.open();
20933         this.doc.write(this.getDocMarkup());
20934         this.doc.close();
20935         
20936         var dbody = (this.doc.body || this.doc.documentElement);
20937         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20938         // this copies styles from the containing element into thsi one..
20939         // not sure why we need all of this..
20940         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20941         
20942         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20943         //ss['background-attachment'] = 'fixed'; // w3c
20944         dbody.bgProperties = 'fixed'; // ie
20945         //Roo.DomHelper.applyStyles(dbody, ss);
20946         Roo.EventManager.on(this.doc, {
20947             //'mousedown': this.onEditorEvent,
20948             'mouseup': this.onEditorEvent,
20949             'dblclick': this.onEditorEvent,
20950             'click': this.onEditorEvent,
20951             'keyup': this.onEditorEvent,
20952             buffer:100,
20953             scope: this
20954         });
20955         if(Roo.isGecko){
20956             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20957         }
20958         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20959             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20960         }
20961         this.initialized = true;
20962
20963         this.owner.fireEvent('initialize', this);
20964         this.pushValue();
20965     },
20966
20967     // private
20968     onDestroy : function(){
20969         
20970         
20971         
20972         if(this.rendered){
20973             
20974             //for (var i =0; i < this.toolbars.length;i++) {
20975             //    // fixme - ask toolbars for heights?
20976             //    this.toolbars[i].onDestroy();
20977            // }
20978             
20979             //this.wrap.dom.innerHTML = '';
20980             //this.wrap.remove();
20981         }
20982     },
20983
20984     // private
20985     onFirstFocus : function(){
20986         
20987         this.assignDocWin();
20988         
20989         
20990         this.activated = true;
20991          
20992     
20993         if(Roo.isGecko){ // prevent silly gecko errors
20994             this.win.focus();
20995             var s = this.win.getSelection();
20996             if(!s.focusNode || s.focusNode.nodeType != 3){
20997                 var r = s.getRangeAt(0);
20998                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20999                 r.collapse(true);
21000                 this.deferFocus();
21001             }
21002             try{
21003                 this.execCmd('useCSS', true);
21004                 this.execCmd('styleWithCSS', false);
21005             }catch(e){}
21006         }
21007         this.owner.fireEvent('activate', this);
21008     },
21009
21010     // private
21011     adjustFont: function(btn){
21012         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21013         //if(Roo.isSafari){ // safari
21014         //    adjust *= 2;
21015        // }
21016         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21017         if(Roo.isSafari){ // safari
21018             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21019             v =  (v < 10) ? 10 : v;
21020             v =  (v > 48) ? 48 : v;
21021             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21022             
21023         }
21024         
21025         
21026         v = Math.max(1, v+adjust);
21027         
21028         this.execCmd('FontSize', v  );
21029     },
21030
21031     onEditorEvent : function(e)
21032     {
21033         this.owner.fireEvent('editorevent', this, e);
21034       //  this.updateToolbar();
21035         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21036     },
21037
21038     insertTag : function(tg)
21039     {
21040         // could be a bit smarter... -> wrap the current selected tRoo..
21041         if (tg.toLowerCase() == 'span' ||
21042             tg.toLowerCase() == 'code' ||
21043             tg.toLowerCase() == 'sup' ||
21044             tg.toLowerCase() == 'sub' 
21045             ) {
21046             
21047             range = this.createRange(this.getSelection());
21048             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21049             wrappingNode.appendChild(range.extractContents());
21050             range.insertNode(wrappingNode);
21051
21052             return;
21053             
21054             
21055             
21056         }
21057         this.execCmd("formatblock",   tg);
21058         
21059     },
21060     
21061     insertText : function(txt)
21062     {
21063         
21064         
21065         var range = this.createRange();
21066         range.deleteContents();
21067                //alert(Sender.getAttribute('label'));
21068                
21069         range.insertNode(this.doc.createTextNode(txt));
21070     } ,
21071     
21072      
21073
21074     /**
21075      * Executes a Midas editor command on the editor document and performs necessary focus and
21076      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21077      * @param {String} cmd The Midas command
21078      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21079      */
21080     relayCmd : function(cmd, value){
21081         this.win.focus();
21082         this.execCmd(cmd, value);
21083         this.owner.fireEvent('editorevent', this);
21084         //this.updateToolbar();
21085         this.owner.deferFocus();
21086     },
21087
21088     /**
21089      * Executes a Midas editor command directly on the editor document.
21090      * For visual commands, you should use {@link #relayCmd} instead.
21091      * <b>This should only be called after the editor is initialized.</b>
21092      * @param {String} cmd The Midas command
21093      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21094      */
21095     execCmd : function(cmd, value){
21096         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21097         this.syncValue();
21098     },
21099  
21100  
21101    
21102     /**
21103      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21104      * to insert tRoo.
21105      * @param {String} text | dom node.. 
21106      */
21107     insertAtCursor : function(text)
21108     {
21109         
21110         if(!this.activated){
21111             return;
21112         }
21113         /*
21114         if(Roo.isIE){
21115             this.win.focus();
21116             var r = this.doc.selection.createRange();
21117             if(r){
21118                 r.collapse(true);
21119                 r.pasteHTML(text);
21120                 this.syncValue();
21121                 this.deferFocus();
21122             
21123             }
21124             return;
21125         }
21126         */
21127         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21128             this.win.focus();
21129             
21130             
21131             // from jquery ui (MIT licenced)
21132             var range, node;
21133             var win = this.win;
21134             
21135             if (win.getSelection && win.getSelection().getRangeAt) {
21136                 range = win.getSelection().getRangeAt(0);
21137                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21138                 range.insertNode(node);
21139             } else if (win.document.selection && win.document.selection.createRange) {
21140                 // no firefox support
21141                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21142                 win.document.selection.createRange().pasteHTML(txt);
21143             } else {
21144                 // no firefox support
21145                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21146                 this.execCmd('InsertHTML', txt);
21147             } 
21148             
21149             this.syncValue();
21150             
21151             this.deferFocus();
21152         }
21153     },
21154  // private
21155     mozKeyPress : function(e){
21156         if(e.ctrlKey){
21157             var c = e.getCharCode(), cmd;
21158           
21159             if(c > 0){
21160                 c = String.fromCharCode(c).toLowerCase();
21161                 switch(c){
21162                     case 'b':
21163                         cmd = 'bold';
21164                         break;
21165                     case 'i':
21166                         cmd = 'italic';
21167                         break;
21168                     
21169                     case 'u':
21170                         cmd = 'underline';
21171                         break;
21172                     
21173                     case 'v':
21174                         this.cleanUpPaste.defer(100, this);
21175                         return;
21176                         
21177                 }
21178                 if(cmd){
21179                     this.win.focus();
21180                     this.execCmd(cmd);
21181                     this.deferFocus();
21182                     e.preventDefault();
21183                 }
21184                 
21185             }
21186         }
21187     },
21188
21189     // private
21190     fixKeys : function(){ // load time branching for fastest keydown performance
21191         if(Roo.isIE){
21192             return function(e){
21193                 var k = e.getKey(), r;
21194                 if(k == e.TAB){
21195                     e.stopEvent();
21196                     r = this.doc.selection.createRange();
21197                     if(r){
21198                         r.collapse(true);
21199                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21200                         this.deferFocus();
21201                     }
21202                     return;
21203                 }
21204                 
21205                 if(k == e.ENTER){
21206                     r = this.doc.selection.createRange();
21207                     if(r){
21208                         var target = r.parentElement();
21209                         if(!target || target.tagName.toLowerCase() != 'li'){
21210                             e.stopEvent();
21211                             r.pasteHTML('<br />');
21212                             r.collapse(false);
21213                             r.select();
21214                         }
21215                     }
21216                 }
21217                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21218                     this.cleanUpPaste.defer(100, this);
21219                     return;
21220                 }
21221                 
21222                 
21223             };
21224         }else if(Roo.isOpera){
21225             return function(e){
21226                 var k = e.getKey();
21227                 if(k == e.TAB){
21228                     e.stopEvent();
21229                     this.win.focus();
21230                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21231                     this.deferFocus();
21232                 }
21233                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21234                     this.cleanUpPaste.defer(100, this);
21235                     return;
21236                 }
21237                 
21238             };
21239         }else if(Roo.isSafari){
21240             return function(e){
21241                 var k = e.getKey();
21242                 
21243                 if(k == e.TAB){
21244                     e.stopEvent();
21245                     this.execCmd('InsertText','\t');
21246                     this.deferFocus();
21247                     return;
21248                 }
21249                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21250                     this.cleanUpPaste.defer(100, this);
21251                     return;
21252                 }
21253                 
21254              };
21255         }
21256     }(),
21257     
21258     getAllAncestors: function()
21259     {
21260         var p = this.getSelectedNode();
21261         var a = [];
21262         if (!p) {
21263             a.push(p); // push blank onto stack..
21264             p = this.getParentElement();
21265         }
21266         
21267         
21268         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21269             a.push(p);
21270             p = p.parentNode;
21271         }
21272         a.push(this.doc.body);
21273         return a;
21274     },
21275     lastSel : false,
21276     lastSelNode : false,
21277     
21278     
21279     getSelection : function() 
21280     {
21281         this.assignDocWin();
21282         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21283     },
21284     
21285     getSelectedNode: function() 
21286     {
21287         // this may only work on Gecko!!!
21288         
21289         // should we cache this!!!!
21290         
21291         
21292         
21293          
21294         var range = this.createRange(this.getSelection()).cloneRange();
21295         
21296         if (Roo.isIE) {
21297             var parent = range.parentElement();
21298             while (true) {
21299                 var testRange = range.duplicate();
21300                 testRange.moveToElementText(parent);
21301                 if (testRange.inRange(range)) {
21302                     break;
21303                 }
21304                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21305                     break;
21306                 }
21307                 parent = parent.parentElement;
21308             }
21309             return parent;
21310         }
21311         
21312         // is ancestor a text element.
21313         var ac =  range.commonAncestorContainer;
21314         if (ac.nodeType == 3) {
21315             ac = ac.parentNode;
21316         }
21317         
21318         var ar = ac.childNodes;
21319          
21320         var nodes = [];
21321         var other_nodes = [];
21322         var has_other_nodes = false;
21323         for (var i=0;i<ar.length;i++) {
21324             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21325                 continue;
21326             }
21327             // fullly contained node.
21328             
21329             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21330                 nodes.push(ar[i]);
21331                 continue;
21332             }
21333             
21334             // probably selected..
21335             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21336                 other_nodes.push(ar[i]);
21337                 continue;
21338             }
21339             // outer..
21340             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21341                 continue;
21342             }
21343             
21344             
21345             has_other_nodes = true;
21346         }
21347         if (!nodes.length && other_nodes.length) {
21348             nodes= other_nodes;
21349         }
21350         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21351             return false;
21352         }
21353         
21354         return nodes[0];
21355     },
21356     createRange: function(sel)
21357     {
21358         // this has strange effects when using with 
21359         // top toolbar - not sure if it's a great idea.
21360         //this.editor.contentWindow.focus();
21361         if (typeof sel != "undefined") {
21362             try {
21363                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21364             } catch(e) {
21365                 return this.doc.createRange();
21366             }
21367         } else {
21368             return this.doc.createRange();
21369         }
21370     },
21371     getParentElement: function()
21372     {
21373         
21374         this.assignDocWin();
21375         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21376         
21377         var range = this.createRange(sel);
21378          
21379         try {
21380             var p = range.commonAncestorContainer;
21381             while (p.nodeType == 3) { // text node
21382                 p = p.parentNode;
21383             }
21384             return p;
21385         } catch (e) {
21386             return null;
21387         }
21388     
21389     },
21390     /***
21391      *
21392      * Range intersection.. the hard stuff...
21393      *  '-1' = before
21394      *  '0' = hits..
21395      *  '1' = after.
21396      *         [ -- selected range --- ]
21397      *   [fail]                        [fail]
21398      *
21399      *    basically..
21400      *      if end is before start or  hits it. fail.
21401      *      if start is after end or hits it fail.
21402      *
21403      *   if either hits (but other is outside. - then it's not 
21404      *   
21405      *    
21406      **/
21407     
21408     
21409     // @see http://www.thismuchiknow.co.uk/?p=64.
21410     rangeIntersectsNode : function(range, node)
21411     {
21412         var nodeRange = node.ownerDocument.createRange();
21413         try {
21414             nodeRange.selectNode(node);
21415         } catch (e) {
21416             nodeRange.selectNodeContents(node);
21417         }
21418     
21419         var rangeStartRange = range.cloneRange();
21420         rangeStartRange.collapse(true);
21421     
21422         var rangeEndRange = range.cloneRange();
21423         rangeEndRange.collapse(false);
21424     
21425         var nodeStartRange = nodeRange.cloneRange();
21426         nodeStartRange.collapse(true);
21427     
21428         var nodeEndRange = nodeRange.cloneRange();
21429         nodeEndRange.collapse(false);
21430     
21431         return rangeStartRange.compareBoundaryPoints(
21432                  Range.START_TO_START, nodeEndRange) == -1 &&
21433                rangeEndRange.compareBoundaryPoints(
21434                  Range.START_TO_START, nodeStartRange) == 1;
21435         
21436          
21437     },
21438     rangeCompareNode : function(range, node)
21439     {
21440         var nodeRange = node.ownerDocument.createRange();
21441         try {
21442             nodeRange.selectNode(node);
21443         } catch (e) {
21444             nodeRange.selectNodeContents(node);
21445         }
21446         
21447         
21448         range.collapse(true);
21449     
21450         nodeRange.collapse(true);
21451      
21452         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21453         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21454          
21455         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21456         
21457         var nodeIsBefore   =  ss == 1;
21458         var nodeIsAfter    = ee == -1;
21459         
21460         if (nodeIsBefore && nodeIsAfter) {
21461             return 0; // outer
21462         }
21463         if (!nodeIsBefore && nodeIsAfter) {
21464             return 1; //right trailed.
21465         }
21466         
21467         if (nodeIsBefore && !nodeIsAfter) {
21468             return 2;  // left trailed.
21469         }
21470         // fully contined.
21471         return 3;
21472     },
21473
21474     // private? - in a new class?
21475     cleanUpPaste :  function()
21476     {
21477         // cleans up the whole document..
21478         Roo.log('cleanuppaste');
21479         
21480         this.cleanUpChildren(this.doc.body);
21481         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21482         if (clean != this.doc.body.innerHTML) {
21483             this.doc.body.innerHTML = clean;
21484         }
21485         
21486     },
21487     
21488     cleanWordChars : function(input) {// change the chars to hex code
21489         var he = Roo.HtmlEditorCore;
21490         
21491         var output = input;
21492         Roo.each(he.swapCodes, function(sw) { 
21493             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21494             
21495             output = output.replace(swapper, sw[1]);
21496         });
21497         
21498         return output;
21499     },
21500     
21501     
21502     cleanUpChildren : function (n)
21503     {
21504         if (!n.childNodes.length) {
21505             return;
21506         }
21507         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21508            this.cleanUpChild(n.childNodes[i]);
21509         }
21510     },
21511     
21512     
21513         
21514     
21515     cleanUpChild : function (node)
21516     {
21517         var ed = this;
21518         //console.log(node);
21519         if (node.nodeName == "#text") {
21520             // clean up silly Windows -- stuff?
21521             return; 
21522         }
21523         if (node.nodeName == "#comment") {
21524             node.parentNode.removeChild(node);
21525             // clean up silly Windows -- stuff?
21526             return; 
21527         }
21528         var lcname = node.tagName.toLowerCase();
21529         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21530         // whitelist of tags..
21531         
21532         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21533             // remove node.
21534             node.parentNode.removeChild(node);
21535             return;
21536             
21537         }
21538         
21539         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21540         
21541         // spans with no attributes - just remove them..
21542         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21543             remove_keep_children = true;
21544         }
21545         
21546         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21547         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21548         
21549         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21550         //    remove_keep_children = true;
21551         //}
21552         
21553         if (remove_keep_children) {
21554             this.cleanUpChildren(node);
21555             // inserts everything just before this node...
21556             while (node.childNodes.length) {
21557                 var cn = node.childNodes[0];
21558                 node.removeChild(cn);
21559                 node.parentNode.insertBefore(cn, node);
21560             }
21561             node.parentNode.removeChild(node);
21562             return;
21563         }
21564         
21565         if (!node.attributes || !node.attributes.length) {
21566             
21567           
21568             
21569             
21570             this.cleanUpChildren(node);
21571             return;
21572         }
21573         
21574         function cleanAttr(n,v)
21575         {
21576             
21577             if (v.match(/^\./) || v.match(/^\//)) {
21578                 return;
21579             }
21580             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21581                 return;
21582             }
21583             if (v.match(/^#/)) {
21584                 return;
21585             }
21586             if (v.match(/^\{/)) { // allow template editing.
21587                 return;
21588             }
21589 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21590             node.removeAttribute(n);
21591             
21592         }
21593         
21594         var cwhite = this.cwhite;
21595         var cblack = this.cblack;
21596             
21597         function cleanStyle(n,v)
21598         {
21599             if (v.match(/expression/)) { //XSS?? should we even bother..
21600                 node.removeAttribute(n);
21601                 return;
21602             }
21603             
21604             var parts = v.split(/;/);
21605             var clean = [];
21606             
21607             Roo.each(parts, function(p) {
21608                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21609                 if (!p.length) {
21610                     return true;
21611                 }
21612                 var l = p.split(':').shift().replace(/\s+/g,'');
21613                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21614                 
21615                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21616 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21617                     //node.removeAttribute(n);
21618                     return true;
21619                 }
21620                 //Roo.log()
21621                 // only allow 'c whitelisted system attributes'
21622                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21623 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21624                     //node.removeAttribute(n);
21625                     return true;
21626                 }
21627                 
21628                 
21629                  
21630                 
21631                 clean.push(p);
21632                 return true;
21633             });
21634             if (clean.length) { 
21635                 node.setAttribute(n, clean.join(';'));
21636             } else {
21637                 node.removeAttribute(n);
21638             }
21639             
21640         }
21641         
21642         
21643         for (var i = node.attributes.length-1; i > -1 ; i--) {
21644             var a = node.attributes[i];
21645             //console.log(a);
21646             
21647             if (a.name.toLowerCase().substr(0,2)=='on')  {
21648                 node.removeAttribute(a.name);
21649                 continue;
21650             }
21651             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21652                 node.removeAttribute(a.name);
21653                 continue;
21654             }
21655             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21656                 cleanAttr(a.name,a.value); // fixme..
21657                 continue;
21658             }
21659             if (a.name == 'style') {
21660                 cleanStyle(a.name,a.value);
21661                 continue;
21662             }
21663             /// clean up MS crap..
21664             // tecnically this should be a list of valid class'es..
21665             
21666             
21667             if (a.name == 'class') {
21668                 if (a.value.match(/^Mso/)) {
21669                     node.removeAttribute('class');
21670                 }
21671                 
21672                 if (a.value.match(/^body$/)) {
21673                     node.removeAttribute('class');
21674                 }
21675                 continue;
21676             }
21677             
21678             // style cleanup!?
21679             // class cleanup?
21680             
21681         }
21682         
21683         
21684         this.cleanUpChildren(node);
21685         
21686         
21687     },
21688     
21689     /**
21690      * Clean up MS wordisms...
21691      */
21692     cleanWord : function(node)
21693     {
21694         if (!node) {
21695             this.cleanWord(this.doc.body);
21696             return;
21697         }
21698         
21699         if(
21700                 node.nodeName == 'SPAN' &&
21701                 !node.hasAttributes() &&
21702                 node.childNodes.length == 1 &&
21703                 node.firstChild.nodeName == "#text"  
21704         ) {
21705             var textNode = node.firstChild;
21706             node.removeChild(textNode);
21707             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21708                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21709             }
21710             node.parentNode.insertBefore(textNode, node);
21711             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21712                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21713             }
21714             node.parentNode.removeChild(node);
21715         }
21716         
21717         if (node.nodeName == "#text") {
21718             // clean up silly Windows -- stuff?
21719             return; 
21720         }
21721         if (node.nodeName == "#comment") {
21722             node.parentNode.removeChild(node);
21723             // clean up silly Windows -- stuff?
21724             return; 
21725         }
21726         
21727         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21728             node.parentNode.removeChild(node);
21729             return;
21730         }
21731         //Roo.log(node.tagName);
21732         // remove - but keep children..
21733         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21734             //Roo.log('-- removed');
21735             while (node.childNodes.length) {
21736                 var cn = node.childNodes[0];
21737                 node.removeChild(cn);
21738                 node.parentNode.insertBefore(cn, node);
21739                 // move node to parent - and clean it..
21740                 this.cleanWord(cn);
21741             }
21742             node.parentNode.removeChild(node);
21743             /// no need to iterate chidlren = it's got none..
21744             //this.iterateChildren(node, this.cleanWord);
21745             return;
21746         }
21747         // clean styles
21748         if (node.className.length) {
21749             
21750             var cn = node.className.split(/\W+/);
21751             var cna = [];
21752             Roo.each(cn, function(cls) {
21753                 if (cls.match(/Mso[a-zA-Z]+/)) {
21754                     return;
21755                 }
21756                 cna.push(cls);
21757             });
21758             node.className = cna.length ? cna.join(' ') : '';
21759             if (!cna.length) {
21760                 node.removeAttribute("class");
21761             }
21762         }
21763         
21764         if (node.hasAttribute("lang")) {
21765             node.removeAttribute("lang");
21766         }
21767         
21768         if (node.hasAttribute("style")) {
21769             
21770             var styles = node.getAttribute("style").split(";");
21771             var nstyle = [];
21772             Roo.each(styles, function(s) {
21773                 if (!s.match(/:/)) {
21774                     return;
21775                 }
21776                 var kv = s.split(":");
21777                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21778                     return;
21779                 }
21780                 // what ever is left... we allow.
21781                 nstyle.push(s);
21782             });
21783             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21784             if (!nstyle.length) {
21785                 node.removeAttribute('style');
21786             }
21787         }
21788         this.iterateChildren(node, this.cleanWord);
21789         
21790         
21791         
21792     },
21793     /**
21794      * iterateChildren of a Node, calling fn each time, using this as the scole..
21795      * @param {DomNode} node node to iterate children of.
21796      * @param {Function} fn method of this class to call on each item.
21797      */
21798     iterateChildren : function(node, fn)
21799     {
21800         if (!node.childNodes.length) {
21801                 return;
21802         }
21803         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21804            fn.call(this, node.childNodes[i])
21805         }
21806     },
21807     
21808     
21809     /**
21810      * cleanTableWidths.
21811      *
21812      * Quite often pasting from word etc.. results in tables with column and widths.
21813      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21814      *
21815      */
21816     cleanTableWidths : function(node)
21817     {
21818          
21819          
21820         if (!node) {
21821             this.cleanTableWidths(this.doc.body);
21822             return;
21823         }
21824         
21825         // ignore list...
21826         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21827             return; 
21828         }
21829         Roo.log(node.tagName);
21830         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21831             this.iterateChildren(node, this.cleanTableWidths);
21832             return;
21833         }
21834         if (node.hasAttribute('width')) {
21835             node.removeAttribute('width');
21836         }
21837         
21838          
21839         if (node.hasAttribute("style")) {
21840             // pretty basic...
21841             
21842             var styles = node.getAttribute("style").split(";");
21843             var nstyle = [];
21844             Roo.each(styles, function(s) {
21845                 if (!s.match(/:/)) {
21846                     return;
21847                 }
21848                 var kv = s.split(":");
21849                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21850                     return;
21851                 }
21852                 // what ever is left... we allow.
21853                 nstyle.push(s);
21854             });
21855             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21856             if (!nstyle.length) {
21857                 node.removeAttribute('style');
21858             }
21859         }
21860         
21861         this.iterateChildren(node, this.cleanTableWidths);
21862         
21863         
21864     },
21865     
21866     
21867     
21868     
21869     domToHTML : function(currentElement, depth, nopadtext) {
21870         
21871         depth = depth || 0;
21872         nopadtext = nopadtext || false;
21873     
21874         if (!currentElement) {
21875             return this.domToHTML(this.doc.body);
21876         }
21877         
21878         //Roo.log(currentElement);
21879         var j;
21880         var allText = false;
21881         var nodeName = currentElement.nodeName;
21882         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21883         
21884         if  (nodeName == '#text') {
21885             
21886             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21887         }
21888         
21889         
21890         var ret = '';
21891         if (nodeName != 'BODY') {
21892              
21893             var i = 0;
21894             // Prints the node tagName, such as <A>, <IMG>, etc
21895             if (tagName) {
21896                 var attr = [];
21897                 for(i = 0; i < currentElement.attributes.length;i++) {
21898                     // quoting?
21899                     var aname = currentElement.attributes.item(i).name;
21900                     if (!currentElement.attributes.item(i).value.length) {
21901                         continue;
21902                     }
21903                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21904                 }
21905                 
21906                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21907             } 
21908             else {
21909                 
21910                 // eack
21911             }
21912         } else {
21913             tagName = false;
21914         }
21915         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21916             return ret;
21917         }
21918         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21919             nopadtext = true;
21920         }
21921         
21922         
21923         // Traverse the tree
21924         i = 0;
21925         var currentElementChild = currentElement.childNodes.item(i);
21926         var allText = true;
21927         var innerHTML  = '';
21928         lastnode = '';
21929         while (currentElementChild) {
21930             // Formatting code (indent the tree so it looks nice on the screen)
21931             var nopad = nopadtext;
21932             if (lastnode == 'SPAN') {
21933                 nopad  = true;
21934             }
21935             // text
21936             if  (currentElementChild.nodeName == '#text') {
21937                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21938                 toadd = nopadtext ? toadd : toadd.trim();
21939                 if (!nopad && toadd.length > 80) {
21940                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21941                 }
21942                 innerHTML  += toadd;
21943                 
21944                 i++;
21945                 currentElementChild = currentElement.childNodes.item(i);
21946                 lastNode = '';
21947                 continue;
21948             }
21949             allText = false;
21950             
21951             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21952                 
21953             // Recursively traverse the tree structure of the child node
21954             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21955             lastnode = currentElementChild.nodeName;
21956             i++;
21957             currentElementChild=currentElement.childNodes.item(i);
21958         }
21959         
21960         ret += innerHTML;
21961         
21962         if (!allText) {
21963                 // The remaining code is mostly for formatting the tree
21964             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21965         }
21966         
21967         
21968         if (tagName) {
21969             ret+= "</"+tagName+">";
21970         }
21971         return ret;
21972         
21973     },
21974         
21975     applyBlacklists : function()
21976     {
21977         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21978         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21979         
21980         this.white = [];
21981         this.black = [];
21982         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21983             if (b.indexOf(tag) > -1) {
21984                 return;
21985             }
21986             this.white.push(tag);
21987             
21988         }, this);
21989         
21990         Roo.each(w, function(tag) {
21991             if (b.indexOf(tag) > -1) {
21992                 return;
21993             }
21994             if (this.white.indexOf(tag) > -1) {
21995                 return;
21996             }
21997             this.white.push(tag);
21998             
21999         }, this);
22000         
22001         
22002         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22003             if (w.indexOf(tag) > -1) {
22004                 return;
22005             }
22006             this.black.push(tag);
22007             
22008         }, this);
22009         
22010         Roo.each(b, function(tag) {
22011             if (w.indexOf(tag) > -1) {
22012                 return;
22013             }
22014             if (this.black.indexOf(tag) > -1) {
22015                 return;
22016             }
22017             this.black.push(tag);
22018             
22019         }, this);
22020         
22021         
22022         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22023         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22024         
22025         this.cwhite = [];
22026         this.cblack = [];
22027         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22028             if (b.indexOf(tag) > -1) {
22029                 return;
22030             }
22031             this.cwhite.push(tag);
22032             
22033         }, this);
22034         
22035         Roo.each(w, function(tag) {
22036             if (b.indexOf(tag) > -1) {
22037                 return;
22038             }
22039             if (this.cwhite.indexOf(tag) > -1) {
22040                 return;
22041             }
22042             this.cwhite.push(tag);
22043             
22044         }, this);
22045         
22046         
22047         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22048             if (w.indexOf(tag) > -1) {
22049                 return;
22050             }
22051             this.cblack.push(tag);
22052             
22053         }, this);
22054         
22055         Roo.each(b, function(tag) {
22056             if (w.indexOf(tag) > -1) {
22057                 return;
22058             }
22059             if (this.cblack.indexOf(tag) > -1) {
22060                 return;
22061             }
22062             this.cblack.push(tag);
22063             
22064         }, this);
22065     },
22066     
22067     setStylesheets : function(stylesheets)
22068     {
22069         if(typeof(stylesheets) == 'string'){
22070             Roo.get(this.iframe.contentDocument.head).createChild({
22071                 tag : 'link',
22072                 rel : 'stylesheet',
22073                 type : 'text/css',
22074                 href : stylesheets
22075             });
22076             
22077             return;
22078         }
22079         var _this = this;
22080      
22081         Roo.each(stylesheets, function(s) {
22082             if(!s.length){
22083                 return;
22084             }
22085             
22086             Roo.get(_this.iframe.contentDocument.head).createChild({
22087                 tag : 'link',
22088                 rel : 'stylesheet',
22089                 type : 'text/css',
22090                 href : s
22091             });
22092         });
22093
22094         
22095     },
22096     
22097     removeStylesheets : function()
22098     {
22099         var _this = this;
22100         
22101         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22102             s.remove();
22103         });
22104     },
22105     
22106     setStyle : function(style)
22107     {
22108         Roo.get(this.iframe.contentDocument.head).createChild({
22109             tag : 'style',
22110             type : 'text/css',
22111             html : style
22112         });
22113
22114         return;
22115     }
22116     
22117     // hide stuff that is not compatible
22118     /**
22119      * @event blur
22120      * @hide
22121      */
22122     /**
22123      * @event change
22124      * @hide
22125      */
22126     /**
22127      * @event focus
22128      * @hide
22129      */
22130     /**
22131      * @event specialkey
22132      * @hide
22133      */
22134     /**
22135      * @cfg {String} fieldClass @hide
22136      */
22137     /**
22138      * @cfg {String} focusClass @hide
22139      */
22140     /**
22141      * @cfg {String} autoCreate @hide
22142      */
22143     /**
22144      * @cfg {String} inputType @hide
22145      */
22146     /**
22147      * @cfg {String} invalidClass @hide
22148      */
22149     /**
22150      * @cfg {String} invalidText @hide
22151      */
22152     /**
22153      * @cfg {String} msgFx @hide
22154      */
22155     /**
22156      * @cfg {String} validateOnBlur @hide
22157      */
22158 });
22159
22160 Roo.HtmlEditorCore.white = [
22161         'area', 'br', 'img', 'input', 'hr', 'wbr',
22162         
22163        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22164        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22165        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22166        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22167        'table',   'ul',         'xmp', 
22168        
22169        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22170       'thead',   'tr', 
22171      
22172       'dir', 'menu', 'ol', 'ul', 'dl',
22173        
22174       'embed',  'object'
22175 ];
22176
22177
22178 Roo.HtmlEditorCore.black = [
22179     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22180         'applet', // 
22181         'base',   'basefont', 'bgsound', 'blink',  'body', 
22182         'frame',  'frameset', 'head',    'html',   'ilayer', 
22183         'iframe', 'layer',  'link',     'meta',    'object',   
22184         'script', 'style' ,'title',  'xml' // clean later..
22185 ];
22186 Roo.HtmlEditorCore.clean = [
22187     'script', 'style', 'title', 'xml'
22188 ];
22189 Roo.HtmlEditorCore.remove = [
22190     'font'
22191 ];
22192 // attributes..
22193
22194 Roo.HtmlEditorCore.ablack = [
22195     'on'
22196 ];
22197     
22198 Roo.HtmlEditorCore.aclean = [ 
22199     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22200 ];
22201
22202 // protocols..
22203 Roo.HtmlEditorCore.pwhite= [
22204         'http',  'https',  'mailto'
22205 ];
22206
22207 // white listed style attributes.
22208 Roo.HtmlEditorCore.cwhite= [
22209       //  'text-align', /// default is to allow most things..
22210       
22211          
22212 //        'font-size'//??
22213 ];
22214
22215 // black listed style attributes.
22216 Roo.HtmlEditorCore.cblack= [
22217       //  'font-size' -- this can be set by the project 
22218 ];
22219
22220
22221 Roo.HtmlEditorCore.swapCodes   =[ 
22222     [    8211, "&#8211;" ], 
22223     [    8212, "&#8212;" ], 
22224     [    8216,  "'" ],  
22225     [    8217, "'" ],  
22226     [    8220, '"' ],  
22227     [    8221, '"' ],  
22228     [    8226, "*" ],  
22229     [    8230, "..." ]
22230 ]; 
22231
22232     //<script type="text/javascript">
22233
22234 /*
22235  * Ext JS Library 1.1.1
22236  * Copyright(c) 2006-2007, Ext JS, LLC.
22237  * Licence LGPL
22238  * 
22239  */
22240  
22241  
22242 Roo.form.HtmlEditor = function(config){
22243     
22244     
22245     
22246     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22247     
22248     if (!this.toolbars) {
22249         this.toolbars = [];
22250     }
22251     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22252     
22253     
22254 };
22255
22256 /**
22257  * @class Roo.form.HtmlEditor
22258  * @extends Roo.form.Field
22259  * Provides a lightweight HTML Editor component.
22260  *
22261  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22262  * 
22263  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22264  * supported by this editor.</b><br/><br/>
22265  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22266  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22267  */
22268 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22269     /**
22270      * @cfg {Boolean} clearUp
22271      */
22272     clearUp : true,
22273       /**
22274      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22275      */
22276     toolbars : false,
22277    
22278      /**
22279      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22280      *                        Roo.resizable.
22281      */
22282     resizable : false,
22283      /**
22284      * @cfg {Number} height (in pixels)
22285      */   
22286     height: 300,
22287    /**
22288      * @cfg {Number} width (in pixels)
22289      */   
22290     width: 500,
22291     
22292     /**
22293      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22294      * 
22295      */
22296     stylesheets: false,
22297     
22298     
22299      /**
22300      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22301      * 
22302      */
22303     cblack: false,
22304     /**
22305      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22306      * 
22307      */
22308     cwhite: false,
22309     
22310      /**
22311      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22312      * 
22313      */
22314     black: false,
22315     /**
22316      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22317      * 
22318      */
22319     white: false,
22320     
22321     // id of frame..
22322     frameId: false,
22323     
22324     // private properties
22325     validationEvent : false,
22326     deferHeight: true,
22327     initialized : false,
22328     activated : false,
22329     
22330     onFocus : Roo.emptyFn,
22331     iframePad:3,
22332     hideMode:'offsets',
22333     
22334     actionMode : 'container', // defaults to hiding it...
22335     
22336     defaultAutoCreate : { // modified by initCompnoent..
22337         tag: "textarea",
22338         style:"width:500px;height:300px;",
22339         autocomplete: "new-password"
22340     },
22341
22342     // private
22343     initComponent : function(){
22344         this.addEvents({
22345             /**
22346              * @event initialize
22347              * Fires when the editor is fully initialized (including the iframe)
22348              * @param {HtmlEditor} this
22349              */
22350             initialize: true,
22351             /**
22352              * @event activate
22353              * Fires when the editor is first receives the focus. Any insertion must wait
22354              * until after this event.
22355              * @param {HtmlEditor} this
22356              */
22357             activate: true,
22358              /**
22359              * @event beforesync
22360              * Fires before the textarea is updated with content from the editor iframe. Return false
22361              * to cancel the sync.
22362              * @param {HtmlEditor} this
22363              * @param {String} html
22364              */
22365             beforesync: true,
22366              /**
22367              * @event beforepush
22368              * Fires before the iframe editor is updated with content from the textarea. Return false
22369              * to cancel the push.
22370              * @param {HtmlEditor} this
22371              * @param {String} html
22372              */
22373             beforepush: true,
22374              /**
22375              * @event sync
22376              * Fires when the textarea is updated with content from the editor iframe.
22377              * @param {HtmlEditor} this
22378              * @param {String} html
22379              */
22380             sync: true,
22381              /**
22382              * @event push
22383              * Fires when the iframe editor is updated with content from the textarea.
22384              * @param {HtmlEditor} this
22385              * @param {String} html
22386              */
22387             push: true,
22388              /**
22389              * @event editmodechange
22390              * Fires when the editor switches edit modes
22391              * @param {HtmlEditor} this
22392              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22393              */
22394             editmodechange: true,
22395             /**
22396              * @event editorevent
22397              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22398              * @param {HtmlEditor} this
22399              */
22400             editorevent: true,
22401             /**
22402              * @event firstfocus
22403              * Fires when on first focus - needed by toolbars..
22404              * @param {HtmlEditor} this
22405              */
22406             firstfocus: true,
22407             /**
22408              * @event autosave
22409              * Auto save the htmlEditor value as a file into Events
22410              * @param {HtmlEditor} this
22411              */
22412             autosave: true,
22413             /**
22414              * @event savedpreview
22415              * preview the saved version of htmlEditor
22416              * @param {HtmlEditor} this
22417              */
22418             savedpreview: true,
22419             
22420             /**
22421             * @event stylesheetsclick
22422             * Fires when press the Sytlesheets button
22423             * @param {Roo.HtmlEditorCore} this
22424             */
22425             stylesheetsclick: true
22426         });
22427         this.defaultAutoCreate =  {
22428             tag: "textarea",
22429             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22430             autocomplete: "new-password"
22431         };
22432     },
22433
22434     /**
22435      * Protected method that will not generally be called directly. It
22436      * is called when the editor creates its toolbar. Override this method if you need to
22437      * add custom toolbar buttons.
22438      * @param {HtmlEditor} editor
22439      */
22440     createToolbar : function(editor){
22441         Roo.log("create toolbars");
22442         if (!editor.toolbars || !editor.toolbars.length) {
22443             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22444         }
22445         
22446         for (var i =0 ; i < editor.toolbars.length;i++) {
22447             editor.toolbars[i] = Roo.factory(
22448                     typeof(editor.toolbars[i]) == 'string' ?
22449                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22450                 Roo.form.HtmlEditor);
22451             editor.toolbars[i].init(editor);
22452         }
22453          
22454         
22455     },
22456
22457      
22458     // private
22459     onRender : function(ct, position)
22460     {
22461         var _t = this;
22462         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22463         
22464         this.wrap = this.el.wrap({
22465             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22466         });
22467         
22468         this.editorcore.onRender(ct, position);
22469          
22470         if (this.resizable) {
22471             this.resizeEl = new Roo.Resizable(this.wrap, {
22472                 pinned : true,
22473                 wrap: true,
22474                 dynamic : true,
22475                 minHeight : this.height,
22476                 height: this.height,
22477                 handles : this.resizable,
22478                 width: this.width,
22479                 listeners : {
22480                     resize : function(r, w, h) {
22481                         _t.onResize(w,h); // -something
22482                     }
22483                 }
22484             });
22485             
22486         }
22487         this.createToolbar(this);
22488        
22489         
22490         if(!this.width){
22491             this.setSize(this.wrap.getSize());
22492         }
22493         if (this.resizeEl) {
22494             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22495             // should trigger onReize..
22496         }
22497         
22498         this.keyNav = new Roo.KeyNav(this.el, {
22499             
22500             "tab" : function(e){
22501                 e.preventDefault();
22502                 
22503                 var value = this.getValue();
22504                 
22505                 var start = this.el.dom.selectionStart;
22506                 var end = this.el.dom.selectionEnd;
22507                 
22508                 if(!e.shiftKey){
22509                     
22510                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22511                     this.el.dom.setSelectionRange(end + 1, end + 1);
22512                     return;
22513                 }
22514                 
22515                 var f = value.substring(0, start).split("\t");
22516                 
22517                 if(f.pop().length != 0){
22518                     return;
22519                 }
22520                 
22521                 this.setValue(f.join("\t") + value.substring(end));
22522                 this.el.dom.setSelectionRange(start - 1, start - 1);
22523                 
22524             },
22525             
22526             "home" : function(e){
22527                 e.preventDefault();
22528                 
22529                 var curr = this.el.dom.selectionStart;
22530                 var lines = this.getValue().split("\n");
22531                 
22532                 if(!lines.length){
22533                     return;
22534                 }
22535                 
22536                 if(e.ctrlKey){
22537                     this.el.dom.setSelectionRange(0, 0);
22538                     return;
22539                 }
22540                 
22541                 var pos = 0;
22542                 
22543                 for (var i = 0; i < lines.length;i++) {
22544                     pos += lines[i].length;
22545                     
22546                     if(i != 0){
22547                         pos += 1;
22548                     }
22549                     
22550                     if(pos < curr){
22551                         continue;
22552                     }
22553                     
22554                     pos -= lines[i].length;
22555                     
22556                     break;
22557                 }
22558                 
22559                 if(!e.shiftKey){
22560                     this.el.dom.setSelectionRange(pos, pos);
22561                     return;
22562                 }
22563                 
22564                 this.el.dom.selectionStart = pos;
22565                 this.el.dom.selectionEnd = curr;
22566             },
22567             
22568             "end" : function(e){
22569                 e.preventDefault();
22570                 
22571                 var curr = this.el.dom.selectionStart;
22572                 var lines = this.getValue().split("\n");
22573                 
22574                 if(!lines.length){
22575                     return;
22576                 }
22577                 
22578                 if(e.ctrlKey){
22579                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22580                     return;
22581                 }
22582                 
22583                 var pos = 0;
22584                 
22585                 for (var i = 0; i < lines.length;i++) {
22586                     
22587                     pos += lines[i].length;
22588                     
22589                     if(i != 0){
22590                         pos += 1;
22591                     }
22592                     
22593                     if(pos < curr){
22594                         continue;
22595                     }
22596                     
22597                     break;
22598                 }
22599                 
22600                 if(!e.shiftKey){
22601                     this.el.dom.setSelectionRange(pos, pos);
22602                     return;
22603                 }
22604                 
22605                 this.el.dom.selectionStart = curr;
22606                 this.el.dom.selectionEnd = pos;
22607             },
22608
22609             scope : this,
22610
22611             doRelay : function(foo, bar, hname){
22612                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22613             },
22614
22615             forceKeyDown: true
22616         });
22617         
22618 //        if(this.autosave && this.w){
22619 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22620 //        }
22621     },
22622
22623     // private
22624     onResize : function(w, h)
22625     {
22626         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22627         var ew = false;
22628         var eh = false;
22629         
22630         if(this.el ){
22631             if(typeof w == 'number'){
22632                 var aw = w - this.wrap.getFrameWidth('lr');
22633                 this.el.setWidth(this.adjustWidth('textarea', aw));
22634                 ew = aw;
22635             }
22636             if(typeof h == 'number'){
22637                 var tbh = 0;
22638                 for (var i =0; i < this.toolbars.length;i++) {
22639                     // fixme - ask toolbars for heights?
22640                     tbh += this.toolbars[i].tb.el.getHeight();
22641                     if (this.toolbars[i].footer) {
22642                         tbh += this.toolbars[i].footer.el.getHeight();
22643                     }
22644                 }
22645                 
22646                 
22647                 
22648                 
22649                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22650                 ah -= 5; // knock a few pixes off for look..
22651 //                Roo.log(ah);
22652                 this.el.setHeight(this.adjustWidth('textarea', ah));
22653                 var eh = ah;
22654             }
22655         }
22656         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22657         this.editorcore.onResize(ew,eh);
22658         
22659     },
22660
22661     /**
22662      * Toggles the editor between standard and source edit mode.
22663      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22664      */
22665     toggleSourceEdit : function(sourceEditMode)
22666     {
22667         this.editorcore.toggleSourceEdit(sourceEditMode);
22668         
22669         if(this.editorcore.sourceEditMode){
22670             Roo.log('editor - showing textarea');
22671             
22672 //            Roo.log('in');
22673 //            Roo.log(this.syncValue());
22674             this.editorcore.syncValue();
22675             this.el.removeClass('x-hidden');
22676             this.el.dom.removeAttribute('tabIndex');
22677             this.el.focus();
22678             
22679             for (var i = 0; i < this.toolbars.length; i++) {
22680                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22681                     this.toolbars[i].tb.hide();
22682                     this.toolbars[i].footer.hide();
22683                 }
22684             }
22685             
22686         }else{
22687             Roo.log('editor - hiding textarea');
22688 //            Roo.log('out')
22689 //            Roo.log(this.pushValue()); 
22690             this.editorcore.pushValue();
22691             
22692             this.el.addClass('x-hidden');
22693             this.el.dom.setAttribute('tabIndex', -1);
22694             
22695             for (var i = 0; i < this.toolbars.length; i++) {
22696                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22697                     this.toolbars[i].tb.show();
22698                     this.toolbars[i].footer.show();
22699                 }
22700             }
22701             
22702             //this.deferFocus();
22703         }
22704         
22705         this.setSize(this.wrap.getSize());
22706         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22707         
22708         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22709     },
22710  
22711     // private (for BoxComponent)
22712     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22713
22714     // private (for BoxComponent)
22715     getResizeEl : function(){
22716         return this.wrap;
22717     },
22718
22719     // private (for BoxComponent)
22720     getPositionEl : function(){
22721         return this.wrap;
22722     },
22723
22724     // private
22725     initEvents : function(){
22726         this.originalValue = this.getValue();
22727     },
22728
22729     /**
22730      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22731      * @method
22732      */
22733     markInvalid : Roo.emptyFn,
22734     /**
22735      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22736      * @method
22737      */
22738     clearInvalid : Roo.emptyFn,
22739
22740     setValue : function(v){
22741         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22742         this.editorcore.pushValue();
22743     },
22744
22745      
22746     // private
22747     deferFocus : function(){
22748         this.focus.defer(10, this);
22749     },
22750
22751     // doc'ed in Field
22752     focus : function(){
22753         this.editorcore.focus();
22754         
22755     },
22756       
22757
22758     // private
22759     onDestroy : function(){
22760         
22761         
22762         
22763         if(this.rendered){
22764             
22765             for (var i =0; i < this.toolbars.length;i++) {
22766                 // fixme - ask toolbars for heights?
22767                 this.toolbars[i].onDestroy();
22768             }
22769             
22770             this.wrap.dom.innerHTML = '';
22771             this.wrap.remove();
22772         }
22773     },
22774
22775     // private
22776     onFirstFocus : function(){
22777         //Roo.log("onFirstFocus");
22778         this.editorcore.onFirstFocus();
22779          for (var i =0; i < this.toolbars.length;i++) {
22780             this.toolbars[i].onFirstFocus();
22781         }
22782         
22783     },
22784     
22785     // private
22786     syncValue : function()
22787     {
22788         this.editorcore.syncValue();
22789     },
22790     
22791     pushValue : function()
22792     {
22793         this.editorcore.pushValue();
22794     },
22795     
22796     setStylesheets : function(stylesheets)
22797     {
22798         this.editorcore.setStylesheets(stylesheets);
22799     },
22800     
22801     removeStylesheets : function()
22802     {
22803         this.editorcore.removeStylesheets();
22804     }
22805      
22806     
22807     // hide stuff that is not compatible
22808     /**
22809      * @event blur
22810      * @hide
22811      */
22812     /**
22813      * @event change
22814      * @hide
22815      */
22816     /**
22817      * @event focus
22818      * @hide
22819      */
22820     /**
22821      * @event specialkey
22822      * @hide
22823      */
22824     /**
22825      * @cfg {String} fieldClass @hide
22826      */
22827     /**
22828      * @cfg {String} focusClass @hide
22829      */
22830     /**
22831      * @cfg {String} autoCreate @hide
22832      */
22833     /**
22834      * @cfg {String} inputType @hide
22835      */
22836     /**
22837      * @cfg {String} invalidClass @hide
22838      */
22839     /**
22840      * @cfg {String} invalidText @hide
22841      */
22842     /**
22843      * @cfg {String} msgFx @hide
22844      */
22845     /**
22846      * @cfg {String} validateOnBlur @hide
22847      */
22848 });
22849  
22850     // <script type="text/javascript">
22851 /*
22852  * Based on
22853  * Ext JS Library 1.1.1
22854  * Copyright(c) 2006-2007, Ext JS, LLC.
22855  *  
22856  
22857  */
22858
22859 /**
22860  * @class Roo.form.HtmlEditorToolbar1
22861  * Basic Toolbar
22862  * 
22863  * Usage:
22864  *
22865  new Roo.form.HtmlEditor({
22866     ....
22867     toolbars : [
22868         new Roo.form.HtmlEditorToolbar1({
22869             disable : { fonts: 1 , format: 1, ..., ... , ...],
22870             btns : [ .... ]
22871         })
22872     }
22873      
22874  * 
22875  * @cfg {Object} disable List of elements to disable..
22876  * @cfg {Array} btns List of additional buttons.
22877  * 
22878  * 
22879  * NEEDS Extra CSS? 
22880  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22881  */
22882  
22883 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22884 {
22885     
22886     Roo.apply(this, config);
22887     
22888     // default disabled, based on 'good practice'..
22889     this.disable = this.disable || {};
22890     Roo.applyIf(this.disable, {
22891         fontSize : true,
22892         colors : true,
22893         specialElements : true
22894     });
22895     
22896     
22897     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22898     // dont call parent... till later.
22899 }
22900
22901 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22902     
22903     tb: false,
22904     
22905     rendered: false,
22906     
22907     editor : false,
22908     editorcore : false,
22909     /**
22910      * @cfg {Object} disable  List of toolbar elements to disable
22911          
22912      */
22913     disable : false,
22914     
22915     
22916      /**
22917      * @cfg {String} createLinkText The default text for the create link prompt
22918      */
22919     createLinkText : 'Please enter the URL for the link:',
22920     /**
22921      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22922      */
22923     defaultLinkValue : 'http:/'+'/',
22924    
22925     
22926       /**
22927      * @cfg {Array} fontFamilies An array of available font families
22928      */
22929     fontFamilies : [
22930         'Arial',
22931         'Courier New',
22932         'Tahoma',
22933         'Times New Roman',
22934         'Verdana'
22935     ],
22936     
22937     specialChars : [
22938            "&#169;",
22939           "&#174;",     
22940           "&#8482;",    
22941           "&#163;" ,    
22942          // "&#8212;",    
22943           "&#8230;",    
22944           "&#247;" ,    
22945         //  "&#225;" ,     ?? a acute?
22946            "&#8364;"    , //Euro
22947        //   "&#8220;"    ,
22948         //  "&#8221;"    ,
22949         //  "&#8226;"    ,
22950           "&#176;"  //   , // degrees
22951
22952          // "&#233;"     , // e ecute
22953          // "&#250;"     , // u ecute?
22954     ],
22955     
22956     specialElements : [
22957         {
22958             text: "Insert Table",
22959             xtype: 'MenuItem',
22960             xns : Roo.Menu,
22961             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22962                 
22963         },
22964         {    
22965             text: "Insert Image",
22966             xtype: 'MenuItem',
22967             xns : Roo.Menu,
22968             ihtml : '<img src="about:blank"/>'
22969             
22970         }
22971         
22972          
22973     ],
22974     
22975     
22976     inputElements : [ 
22977             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22978             "input:submit", "input:button", "select", "textarea", "label" ],
22979     formats : [
22980         ["p"] ,  
22981         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22982         ["pre"],[ "code"], 
22983         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22984         ['div'],['span'],
22985         ['sup'],['sub']
22986     ],
22987     
22988     cleanStyles : [
22989         "font-size"
22990     ],
22991      /**
22992      * @cfg {String} defaultFont default font to use.
22993      */
22994     defaultFont: 'tahoma',
22995    
22996     fontSelect : false,
22997     
22998     
22999     formatCombo : false,
23000     
23001     init : function(editor)
23002     {
23003         this.editor = editor;
23004         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23005         var editorcore = this.editorcore;
23006         
23007         var _t = this;
23008         
23009         var fid = editorcore.frameId;
23010         var etb = this;
23011         function btn(id, toggle, handler){
23012             var xid = fid + '-'+ id ;
23013             return {
23014                 id : xid,
23015                 cmd : id,
23016                 cls : 'x-btn-icon x-edit-'+id,
23017                 enableToggle:toggle !== false,
23018                 scope: _t, // was editor...
23019                 handler:handler||_t.relayBtnCmd,
23020                 clickEvent:'mousedown',
23021                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23022                 tabIndex:-1
23023             };
23024         }
23025         
23026         
23027         
23028         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23029         this.tb = tb;
23030          // stop form submits
23031         tb.el.on('click', function(e){
23032             e.preventDefault(); // what does this do?
23033         });
23034
23035         if(!this.disable.font) { // && !Roo.isSafari){
23036             /* why no safari for fonts 
23037             editor.fontSelect = tb.el.createChild({
23038                 tag:'select',
23039                 tabIndex: -1,
23040                 cls:'x-font-select',
23041                 html: this.createFontOptions()
23042             });
23043             
23044             editor.fontSelect.on('change', function(){
23045                 var font = editor.fontSelect.dom.value;
23046                 editor.relayCmd('fontname', font);
23047                 editor.deferFocus();
23048             }, editor);
23049             
23050             tb.add(
23051                 editor.fontSelect.dom,
23052                 '-'
23053             );
23054             */
23055             
23056         };
23057         if(!this.disable.formats){
23058             this.formatCombo = new Roo.form.ComboBox({
23059                 store: new Roo.data.SimpleStore({
23060                     id : 'tag',
23061                     fields: ['tag'],
23062                     data : this.formats // from states.js
23063                 }),
23064                 blockFocus : true,
23065                 name : '',
23066                 //autoCreate : {tag: "div",  size: "20"},
23067                 displayField:'tag',
23068                 typeAhead: false,
23069                 mode: 'local',
23070                 editable : false,
23071                 triggerAction: 'all',
23072                 emptyText:'Add tag',
23073                 selectOnFocus:true,
23074                 width:135,
23075                 listeners : {
23076                     'select': function(c, r, i) {
23077                         editorcore.insertTag(r.get('tag'));
23078                         editor.focus();
23079                     }
23080                 }
23081
23082             });
23083             tb.addField(this.formatCombo);
23084             
23085         }
23086         
23087         if(!this.disable.format){
23088             tb.add(
23089                 btn('bold'),
23090                 btn('italic'),
23091                 btn('underline'),
23092                 btn('strikethrough')
23093             );
23094         };
23095         if(!this.disable.fontSize){
23096             tb.add(
23097                 '-',
23098                 
23099                 
23100                 btn('increasefontsize', false, editorcore.adjustFont),
23101                 btn('decreasefontsize', false, editorcore.adjustFont)
23102             );
23103         };
23104         
23105         
23106         if(!this.disable.colors){
23107             tb.add(
23108                 '-', {
23109                     id:editorcore.frameId +'-forecolor',
23110                     cls:'x-btn-icon x-edit-forecolor',
23111                     clickEvent:'mousedown',
23112                     tooltip: this.buttonTips['forecolor'] || undefined,
23113                     tabIndex:-1,
23114                     menu : new Roo.menu.ColorMenu({
23115                         allowReselect: true,
23116                         focus: Roo.emptyFn,
23117                         value:'000000',
23118                         plain:true,
23119                         selectHandler: function(cp, color){
23120                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23121                             editor.deferFocus();
23122                         },
23123                         scope: editorcore,
23124                         clickEvent:'mousedown'
23125                     })
23126                 }, {
23127                     id:editorcore.frameId +'backcolor',
23128                     cls:'x-btn-icon x-edit-backcolor',
23129                     clickEvent:'mousedown',
23130                     tooltip: this.buttonTips['backcolor'] || undefined,
23131                     tabIndex:-1,
23132                     menu : new Roo.menu.ColorMenu({
23133                         focus: Roo.emptyFn,
23134                         value:'FFFFFF',
23135                         plain:true,
23136                         allowReselect: true,
23137                         selectHandler: function(cp, color){
23138                             if(Roo.isGecko){
23139                                 editorcore.execCmd('useCSS', false);
23140                                 editorcore.execCmd('hilitecolor', color);
23141                                 editorcore.execCmd('useCSS', true);
23142                                 editor.deferFocus();
23143                             }else{
23144                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23145                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23146                                 editor.deferFocus();
23147                             }
23148                         },
23149                         scope:editorcore,
23150                         clickEvent:'mousedown'
23151                     })
23152                 }
23153             );
23154         };
23155         // now add all the items...
23156         
23157
23158         if(!this.disable.alignments){
23159             tb.add(
23160                 '-',
23161                 btn('justifyleft'),
23162                 btn('justifycenter'),
23163                 btn('justifyright')
23164             );
23165         };
23166
23167         //if(!Roo.isSafari){
23168             if(!this.disable.links){
23169                 tb.add(
23170                     '-',
23171                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23172                 );
23173             };
23174
23175             if(!this.disable.lists){
23176                 tb.add(
23177                     '-',
23178                     btn('insertorderedlist'),
23179                     btn('insertunorderedlist')
23180                 );
23181             }
23182             if(!this.disable.sourceEdit){
23183                 tb.add(
23184                     '-',
23185                     btn('sourceedit', true, function(btn){
23186                         this.toggleSourceEdit(btn.pressed);
23187                     })
23188                 );
23189             }
23190         //}
23191         
23192         var smenu = { };
23193         // special menu.. - needs to be tidied up..
23194         if (!this.disable.special) {
23195             smenu = {
23196                 text: "&#169;",
23197                 cls: 'x-edit-none',
23198                 
23199                 menu : {
23200                     items : []
23201                 }
23202             };
23203             for (var i =0; i < this.specialChars.length; i++) {
23204                 smenu.menu.items.push({
23205                     
23206                     html: this.specialChars[i],
23207                     handler: function(a,b) {
23208                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23209                         //editor.insertAtCursor(a.html);
23210                         
23211                     },
23212                     tabIndex:-1
23213                 });
23214             }
23215             
23216             
23217             tb.add(smenu);
23218             
23219             
23220         }
23221         
23222         var cmenu = { };
23223         if (!this.disable.cleanStyles) {
23224             cmenu = {
23225                 cls: 'x-btn-icon x-btn-clear',
23226                 
23227                 menu : {
23228                     items : []
23229                 }
23230             };
23231             for (var i =0; i < this.cleanStyles.length; i++) {
23232                 cmenu.menu.items.push({
23233                     actiontype : this.cleanStyles[i],
23234                     html: 'Remove ' + this.cleanStyles[i],
23235                     handler: function(a,b) {
23236 //                        Roo.log(a);
23237 //                        Roo.log(b);
23238                         var c = Roo.get(editorcore.doc.body);
23239                         c.select('[style]').each(function(s) {
23240                             s.dom.style.removeProperty(a.actiontype);
23241                         });
23242                         editorcore.syncValue();
23243                     },
23244                     tabIndex:-1
23245                 });
23246             }
23247              cmenu.menu.items.push({
23248                 actiontype : 'tablewidths',
23249                 html: 'Remove Table Widths',
23250                 handler: function(a,b) {
23251                     editorcore.cleanTableWidths();
23252                     editorcore.syncValue();
23253                 },
23254                 tabIndex:-1
23255             });
23256             cmenu.menu.items.push({
23257                 actiontype : 'word',
23258                 html: 'Remove MS Word Formating',
23259                 handler: function(a,b) {
23260                     editorcore.cleanWord();
23261                     editorcore.syncValue();
23262                 },
23263                 tabIndex:-1
23264             });
23265             
23266             cmenu.menu.items.push({
23267                 actiontype : 'all',
23268                 html: 'Remove All Styles',
23269                 handler: function(a,b) {
23270                     
23271                     var c = Roo.get(editorcore.doc.body);
23272                     c.select('[style]').each(function(s) {
23273                         s.dom.removeAttribute('style');
23274                     });
23275                     editorcore.syncValue();
23276                 },
23277                 tabIndex:-1
23278             });
23279             
23280             cmenu.menu.items.push({
23281                 actiontype : 'all',
23282                 html: 'Remove All CSS Classes',
23283                 handler: function(a,b) {
23284                     
23285                     var c = Roo.get(editorcore.doc.body);
23286                     c.select('[class]').each(function(s) {
23287                         s.dom.removeAttribute('class');
23288                     });
23289                     editorcore.cleanWord();
23290                     editorcore.syncValue();
23291                 },
23292                 tabIndex:-1
23293             });
23294             
23295              cmenu.menu.items.push({
23296                 actiontype : 'tidy',
23297                 html: 'Tidy HTML Source',
23298                 handler: function(a,b) {
23299                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23300                     editorcore.syncValue();
23301                 },
23302                 tabIndex:-1
23303             });
23304             
23305             
23306             tb.add(cmenu);
23307         }
23308          
23309         if (!this.disable.specialElements) {
23310             var semenu = {
23311                 text: "Other;",
23312                 cls: 'x-edit-none',
23313                 menu : {
23314                     items : []
23315                 }
23316             };
23317             for (var i =0; i < this.specialElements.length; i++) {
23318                 semenu.menu.items.push(
23319                     Roo.apply({ 
23320                         handler: function(a,b) {
23321                             editor.insertAtCursor(this.ihtml);
23322                         }
23323                     }, this.specialElements[i])
23324                 );
23325                     
23326             }
23327             
23328             tb.add(semenu);
23329             
23330             
23331         }
23332          
23333         
23334         if (this.btns) {
23335             for(var i =0; i< this.btns.length;i++) {
23336                 var b = Roo.factory(this.btns[i],Roo.form);
23337                 b.cls =  'x-edit-none';
23338                 
23339                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23340                     b.cls += ' x-init-enable';
23341                 }
23342                 
23343                 b.scope = editorcore;
23344                 tb.add(b);
23345             }
23346         
23347         }
23348         
23349         
23350         
23351         // disable everything...
23352         
23353         this.tb.items.each(function(item){
23354             
23355            if(
23356                 item.id != editorcore.frameId+ '-sourceedit' && 
23357                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23358             ){
23359                 
23360                 item.disable();
23361             }
23362         });
23363         this.rendered = true;
23364         
23365         // the all the btns;
23366         editor.on('editorevent', this.updateToolbar, this);
23367         // other toolbars need to implement this..
23368         //editor.on('editmodechange', this.updateToolbar, this);
23369     },
23370     
23371     
23372     relayBtnCmd : function(btn) {
23373         this.editorcore.relayCmd(btn.cmd);
23374     },
23375     // private used internally
23376     createLink : function(){
23377         Roo.log("create link?");
23378         var url = prompt(this.createLinkText, this.defaultLinkValue);
23379         if(url && url != 'http:/'+'/'){
23380             this.editorcore.relayCmd('createlink', url);
23381         }
23382     },
23383
23384     
23385     /**
23386      * Protected method that will not generally be called directly. It triggers
23387      * a toolbar update by reading the markup state of the current selection in the editor.
23388      */
23389     updateToolbar: function(){
23390
23391         if(!this.editorcore.activated){
23392             this.editor.onFirstFocus();
23393             return;
23394         }
23395
23396         var btns = this.tb.items.map, 
23397             doc = this.editorcore.doc,
23398             frameId = this.editorcore.frameId;
23399
23400         if(!this.disable.font && !Roo.isSafari){
23401             /*
23402             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23403             if(name != this.fontSelect.dom.value){
23404                 this.fontSelect.dom.value = name;
23405             }
23406             */
23407         }
23408         if(!this.disable.format){
23409             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23410             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23411             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23412             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23413         }
23414         if(!this.disable.alignments){
23415             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23416             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23417             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23418         }
23419         if(!Roo.isSafari && !this.disable.lists){
23420             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23421             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23422         }
23423         
23424         var ans = this.editorcore.getAllAncestors();
23425         if (this.formatCombo) {
23426             
23427             
23428             var store = this.formatCombo.store;
23429             this.formatCombo.setValue("");
23430             for (var i =0; i < ans.length;i++) {
23431                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23432                     // select it..
23433                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23434                     break;
23435                 }
23436             }
23437         }
23438         
23439         
23440         
23441         // hides menus... - so this cant be on a menu...
23442         Roo.menu.MenuMgr.hideAll();
23443
23444         //this.editorsyncValue();
23445     },
23446    
23447     
23448     createFontOptions : function(){
23449         var buf = [], fs = this.fontFamilies, ff, lc;
23450         
23451         
23452         
23453         for(var i = 0, len = fs.length; i< len; i++){
23454             ff = fs[i];
23455             lc = ff.toLowerCase();
23456             buf.push(
23457                 '<option value="',lc,'" style="font-family:',ff,';"',
23458                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23459                     ff,
23460                 '</option>'
23461             );
23462         }
23463         return buf.join('');
23464     },
23465     
23466     toggleSourceEdit : function(sourceEditMode){
23467         
23468         Roo.log("toolbar toogle");
23469         if(sourceEditMode === undefined){
23470             sourceEditMode = !this.sourceEditMode;
23471         }
23472         this.sourceEditMode = sourceEditMode === true;
23473         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23474         // just toggle the button?
23475         if(btn.pressed !== this.sourceEditMode){
23476             btn.toggle(this.sourceEditMode);
23477             return;
23478         }
23479         
23480         if(sourceEditMode){
23481             Roo.log("disabling buttons");
23482             this.tb.items.each(function(item){
23483                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23484                     item.disable();
23485                 }
23486             });
23487           
23488         }else{
23489             Roo.log("enabling buttons");
23490             if(this.editorcore.initialized){
23491                 this.tb.items.each(function(item){
23492                     item.enable();
23493                 });
23494             }
23495             
23496         }
23497         Roo.log("calling toggole on editor");
23498         // tell the editor that it's been pressed..
23499         this.editor.toggleSourceEdit(sourceEditMode);
23500        
23501     },
23502      /**
23503      * Object collection of toolbar tooltips for the buttons in the editor. The key
23504      * is the command id associated with that button and the value is a valid QuickTips object.
23505      * For example:
23506 <pre><code>
23507 {
23508     bold : {
23509         title: 'Bold (Ctrl+B)',
23510         text: 'Make the selected text bold.',
23511         cls: 'x-html-editor-tip'
23512     },
23513     italic : {
23514         title: 'Italic (Ctrl+I)',
23515         text: 'Make the selected text italic.',
23516         cls: 'x-html-editor-tip'
23517     },
23518     ...
23519 </code></pre>
23520     * @type Object
23521      */
23522     buttonTips : {
23523         bold : {
23524             title: 'Bold (Ctrl+B)',
23525             text: 'Make the selected text bold.',
23526             cls: 'x-html-editor-tip'
23527         },
23528         italic : {
23529             title: 'Italic (Ctrl+I)',
23530             text: 'Make the selected text italic.',
23531             cls: 'x-html-editor-tip'
23532         },
23533         underline : {
23534             title: 'Underline (Ctrl+U)',
23535             text: 'Underline the selected text.',
23536             cls: 'x-html-editor-tip'
23537         },
23538         strikethrough : {
23539             title: 'Strikethrough',
23540             text: 'Strikethrough the selected text.',
23541             cls: 'x-html-editor-tip'
23542         },
23543         increasefontsize : {
23544             title: 'Grow Text',
23545             text: 'Increase the font size.',
23546             cls: 'x-html-editor-tip'
23547         },
23548         decreasefontsize : {
23549             title: 'Shrink Text',
23550             text: 'Decrease the font size.',
23551             cls: 'x-html-editor-tip'
23552         },
23553         backcolor : {
23554             title: 'Text Highlight Color',
23555             text: 'Change the background color of the selected text.',
23556             cls: 'x-html-editor-tip'
23557         },
23558         forecolor : {
23559             title: 'Font Color',
23560             text: 'Change the color of the selected text.',
23561             cls: 'x-html-editor-tip'
23562         },
23563         justifyleft : {
23564             title: 'Align Text Left',
23565             text: 'Align text to the left.',
23566             cls: 'x-html-editor-tip'
23567         },
23568         justifycenter : {
23569             title: 'Center Text',
23570             text: 'Center text in the editor.',
23571             cls: 'x-html-editor-tip'
23572         },
23573         justifyright : {
23574             title: 'Align Text Right',
23575             text: 'Align text to the right.',
23576             cls: 'x-html-editor-tip'
23577         },
23578         insertunorderedlist : {
23579             title: 'Bullet List',
23580             text: 'Start a bulleted list.',
23581             cls: 'x-html-editor-tip'
23582         },
23583         insertorderedlist : {
23584             title: 'Numbered List',
23585             text: 'Start a numbered list.',
23586             cls: 'x-html-editor-tip'
23587         },
23588         createlink : {
23589             title: 'Hyperlink',
23590             text: 'Make the selected text a hyperlink.',
23591             cls: 'x-html-editor-tip'
23592         },
23593         sourceedit : {
23594             title: 'Source Edit',
23595             text: 'Switch to source editing mode.',
23596             cls: 'x-html-editor-tip'
23597         }
23598     },
23599     // private
23600     onDestroy : function(){
23601         if(this.rendered){
23602             
23603             this.tb.items.each(function(item){
23604                 if(item.menu){
23605                     item.menu.removeAll();
23606                     if(item.menu.el){
23607                         item.menu.el.destroy();
23608                     }
23609                 }
23610                 item.destroy();
23611             });
23612              
23613         }
23614     },
23615     onFirstFocus: function() {
23616         this.tb.items.each(function(item){
23617            item.enable();
23618         });
23619     }
23620 });
23621
23622
23623
23624
23625 // <script type="text/javascript">
23626 /*
23627  * Based on
23628  * Ext JS Library 1.1.1
23629  * Copyright(c) 2006-2007, Ext JS, LLC.
23630  *  
23631  
23632  */
23633
23634  
23635 /**
23636  * @class Roo.form.HtmlEditor.ToolbarContext
23637  * Context Toolbar
23638  * 
23639  * Usage:
23640  *
23641  new Roo.form.HtmlEditor({
23642     ....
23643     toolbars : [
23644         { xtype: 'ToolbarStandard', styles : {} }
23645         { xtype: 'ToolbarContext', disable : {} }
23646     ]
23647 })
23648
23649      
23650  * 
23651  * @config : {Object} disable List of elements to disable.. (not done yet.)
23652  * @config : {Object} styles  Map of styles available.
23653  * 
23654  */
23655
23656 Roo.form.HtmlEditor.ToolbarContext = function(config)
23657 {
23658     
23659     Roo.apply(this, config);
23660     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23661     // dont call parent... till later.
23662     this.styles = this.styles || {};
23663 }
23664
23665  
23666
23667 Roo.form.HtmlEditor.ToolbarContext.types = {
23668     'IMG' : {
23669         width : {
23670             title: "Width",
23671             width: 40
23672         },
23673         height:  {
23674             title: "Height",
23675             width: 40
23676         },
23677         align: {
23678             title: "Align",
23679             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23680             width : 80
23681             
23682         },
23683         border: {
23684             title: "Border",
23685             width: 40
23686         },
23687         alt: {
23688             title: "Alt",
23689             width: 120
23690         },
23691         src : {
23692             title: "Src",
23693             width: 220
23694         }
23695         
23696     },
23697     'A' : {
23698         name : {
23699             title: "Name",
23700             width: 50
23701         },
23702         target:  {
23703             title: "Target",
23704             width: 120
23705         },
23706         href:  {
23707             title: "Href",
23708             width: 220
23709         } // border?
23710         
23711     },
23712     'TABLE' : {
23713         rows : {
23714             title: "Rows",
23715             width: 20
23716         },
23717         cols : {
23718             title: "Cols",
23719             width: 20
23720         },
23721         width : {
23722             title: "Width",
23723             width: 40
23724         },
23725         height : {
23726             title: "Height",
23727             width: 40
23728         },
23729         border : {
23730             title: "Border",
23731             width: 20
23732         }
23733     },
23734     'TD' : {
23735         width : {
23736             title: "Width",
23737             width: 40
23738         },
23739         height : {
23740             title: "Height",
23741             width: 40
23742         },   
23743         align: {
23744             title: "Align",
23745             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23746             width: 80
23747         },
23748         valign: {
23749             title: "Valign",
23750             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23751             width: 80
23752         },
23753         colspan: {
23754             title: "Colspan",
23755             width: 20
23756             
23757         },
23758          'font-family'  : {
23759             title : "Font",
23760             style : 'fontFamily',
23761             displayField: 'display',
23762             optname : 'font-family',
23763             width: 140
23764         }
23765     },
23766     'INPUT' : {
23767         name : {
23768             title: "name",
23769             width: 120
23770         },
23771         value : {
23772             title: "Value",
23773             width: 120
23774         },
23775         width : {
23776             title: "Width",
23777             width: 40
23778         }
23779     },
23780     'LABEL' : {
23781         'for' : {
23782             title: "For",
23783             width: 120
23784         }
23785     },
23786     'TEXTAREA' : {
23787           name : {
23788             title: "name",
23789             width: 120
23790         },
23791         rows : {
23792             title: "Rows",
23793             width: 20
23794         },
23795         cols : {
23796             title: "Cols",
23797             width: 20
23798         }
23799     },
23800     'SELECT' : {
23801         name : {
23802             title: "name",
23803             width: 120
23804         },
23805         selectoptions : {
23806             title: "Options",
23807             width: 200
23808         }
23809     },
23810     
23811     // should we really allow this??
23812     // should this just be 
23813     'BODY' : {
23814         title : {
23815             title: "Title",
23816             width: 200,
23817             disabled : true
23818         }
23819     },
23820     'SPAN' : {
23821         'font-family'  : {
23822             title : "Font",
23823             style : 'fontFamily',
23824             displayField: 'display',
23825             optname : 'font-family',
23826             width: 140
23827         }
23828     },
23829     'DIV' : {
23830         'font-family'  : {
23831             title : "Font",
23832             style : 'fontFamily',
23833             displayField: 'display',
23834             optname : 'font-family',
23835             width: 140
23836         }
23837     },
23838      'P' : {
23839         'font-family'  : {
23840             title : "Font",
23841             style : 'fontFamily',
23842             displayField: 'display',
23843             optname : 'font-family',
23844             width: 140
23845         }
23846     },
23847     
23848     '*' : {
23849         // empty..
23850     }
23851
23852 };
23853
23854 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23855 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23856
23857 Roo.form.HtmlEditor.ToolbarContext.options = {
23858         'font-family'  : [ 
23859                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23860                 [ 'Courier New', 'Courier New'],
23861                 [ 'Tahoma', 'Tahoma'],
23862                 [ 'Times New Roman,serif', 'Times'],
23863                 [ 'Verdana','Verdana' ]
23864         ]
23865 };
23866
23867 // fixme - these need to be configurable..
23868  
23869
23870 //Roo.form.HtmlEditor.ToolbarContext.types
23871
23872
23873 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23874     
23875     tb: false,
23876     
23877     rendered: false,
23878     
23879     editor : false,
23880     editorcore : false,
23881     /**
23882      * @cfg {Object} disable  List of toolbar elements to disable
23883          
23884      */
23885     disable : false,
23886     /**
23887      * @cfg {Object} styles List of styles 
23888      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23889      *
23890      * These must be defined in the page, so they get rendered correctly..
23891      * .headline { }
23892      * TD.underline { }
23893      * 
23894      */
23895     styles : false,
23896     
23897     options: false,
23898     
23899     toolbars : false,
23900     
23901     init : function(editor)
23902     {
23903         this.editor = editor;
23904         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23905         var editorcore = this.editorcore;
23906         
23907         var fid = editorcore.frameId;
23908         var etb = this;
23909         function btn(id, toggle, handler){
23910             var xid = fid + '-'+ id ;
23911             return {
23912                 id : xid,
23913                 cmd : id,
23914                 cls : 'x-btn-icon x-edit-'+id,
23915                 enableToggle:toggle !== false,
23916                 scope: editorcore, // was editor...
23917                 handler:handler||editorcore.relayBtnCmd,
23918                 clickEvent:'mousedown',
23919                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23920                 tabIndex:-1
23921             };
23922         }
23923         // create a new element.
23924         var wdiv = editor.wrap.createChild({
23925                 tag: 'div'
23926             }, editor.wrap.dom.firstChild.nextSibling, true);
23927         
23928         // can we do this more than once??
23929         
23930          // stop form submits
23931       
23932  
23933         // disable everything...
23934         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23935         this.toolbars = {};
23936            
23937         for (var i in  ty) {
23938           
23939             this.toolbars[i] = this.buildToolbar(ty[i],i);
23940         }
23941         this.tb = this.toolbars.BODY;
23942         this.tb.el.show();
23943         this.buildFooter();
23944         this.footer.show();
23945         editor.on('hide', function( ) { this.footer.hide() }, this);
23946         editor.on('show', function( ) { this.footer.show() }, this);
23947         
23948          
23949         this.rendered = true;
23950         
23951         // the all the btns;
23952         editor.on('editorevent', this.updateToolbar, this);
23953         // other toolbars need to implement this..
23954         //editor.on('editmodechange', this.updateToolbar, this);
23955     },
23956     
23957     
23958     
23959     /**
23960      * Protected method that will not generally be called directly. It triggers
23961      * a toolbar update by reading the markup state of the current selection in the editor.
23962      *
23963      * Note you can force an update by calling on('editorevent', scope, false)
23964      */
23965     updateToolbar: function(editor,ev,sel){
23966
23967         //Roo.log(ev);
23968         // capture mouse up - this is handy for selecting images..
23969         // perhaps should go somewhere else...
23970         if(!this.editorcore.activated){
23971              this.editor.onFirstFocus();
23972             return;
23973         }
23974         
23975         
23976         
23977         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23978         // selectNode - might want to handle IE?
23979         if (ev &&
23980             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23981             ev.target && ev.target.tagName == 'IMG') {
23982             // they have click on an image...
23983             // let's see if we can change the selection...
23984             sel = ev.target;
23985          
23986               var nodeRange = sel.ownerDocument.createRange();
23987             try {
23988                 nodeRange.selectNode(sel);
23989             } catch (e) {
23990                 nodeRange.selectNodeContents(sel);
23991             }
23992             //nodeRange.collapse(true);
23993             var s = this.editorcore.win.getSelection();
23994             s.removeAllRanges();
23995             s.addRange(nodeRange);
23996         }  
23997         
23998       
23999         var updateFooter = sel ? false : true;
24000         
24001         
24002         var ans = this.editorcore.getAllAncestors();
24003         
24004         // pick
24005         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24006         
24007         if (!sel) { 
24008             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24009             sel = sel ? sel : this.editorcore.doc.body;
24010             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24011             
24012         }
24013         // pick a menu that exists..
24014         var tn = sel.tagName.toUpperCase();
24015         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24016         
24017         tn = sel.tagName.toUpperCase();
24018         
24019         var lastSel = this.tb.selectedNode;
24020         
24021         this.tb.selectedNode = sel;
24022         
24023         // if current menu does not match..
24024         
24025         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24026                 
24027             this.tb.el.hide();
24028             ///console.log("show: " + tn);
24029             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24030             this.tb.el.show();
24031             // update name
24032             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24033             
24034             
24035             // update attributes
24036             if (this.tb.fields) {
24037                 this.tb.fields.each(function(e) {
24038                     if (e.stylename) {
24039                         e.setValue(sel.style[e.stylename]);
24040                         return;
24041                     } 
24042                    e.setValue(sel.getAttribute(e.attrname));
24043                 });
24044             }
24045             
24046             var hasStyles = false;
24047             for(var i in this.styles) {
24048                 hasStyles = true;
24049                 break;
24050             }
24051             
24052             // update styles
24053             if (hasStyles) { 
24054                 var st = this.tb.fields.item(0);
24055                 
24056                 st.store.removeAll();
24057                
24058                 
24059                 var cn = sel.className.split(/\s+/);
24060                 
24061                 var avs = [];
24062                 if (this.styles['*']) {
24063                     
24064                     Roo.each(this.styles['*'], function(v) {
24065                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24066                     });
24067                 }
24068                 if (this.styles[tn]) { 
24069                     Roo.each(this.styles[tn], function(v) {
24070                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24071                     });
24072                 }
24073                 
24074                 st.store.loadData(avs);
24075                 st.collapse();
24076                 st.setValue(cn);
24077             }
24078             // flag our selected Node.
24079             this.tb.selectedNode = sel;
24080            
24081            
24082             Roo.menu.MenuMgr.hideAll();
24083
24084         }
24085         
24086         if (!updateFooter) {
24087             //this.footDisp.dom.innerHTML = ''; 
24088             return;
24089         }
24090         // update the footer
24091         //
24092         var html = '';
24093         
24094         this.footerEls = ans.reverse();
24095         Roo.each(this.footerEls, function(a,i) {
24096             if (!a) { return; }
24097             html += html.length ? ' &gt; '  :  '';
24098             
24099             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24100             
24101         });
24102        
24103         // 
24104         var sz = this.footDisp.up('td').getSize();
24105         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24106         this.footDisp.dom.style.marginLeft = '5px';
24107         
24108         this.footDisp.dom.style.overflow = 'hidden';
24109         
24110         this.footDisp.dom.innerHTML = html;
24111             
24112         //this.editorsyncValue();
24113     },
24114      
24115     
24116    
24117        
24118     // private
24119     onDestroy : function(){
24120         if(this.rendered){
24121             
24122             this.tb.items.each(function(item){
24123                 if(item.menu){
24124                     item.menu.removeAll();
24125                     if(item.menu.el){
24126                         item.menu.el.destroy();
24127                     }
24128                 }
24129                 item.destroy();
24130             });
24131              
24132         }
24133     },
24134     onFirstFocus: function() {
24135         // need to do this for all the toolbars..
24136         this.tb.items.each(function(item){
24137            item.enable();
24138         });
24139     },
24140     buildToolbar: function(tlist, nm)
24141     {
24142         var editor = this.editor;
24143         var editorcore = this.editorcore;
24144          // create a new element.
24145         var wdiv = editor.wrap.createChild({
24146                 tag: 'div'
24147             }, editor.wrap.dom.firstChild.nextSibling, true);
24148         
24149        
24150         var tb = new Roo.Toolbar(wdiv);
24151         // add the name..
24152         
24153         tb.add(nm+ ":&nbsp;");
24154         
24155         var styles = [];
24156         for(var i in this.styles) {
24157             styles.push(i);
24158         }
24159         
24160         // styles...
24161         if (styles && styles.length) {
24162             
24163             // this needs a multi-select checkbox...
24164             tb.addField( new Roo.form.ComboBox({
24165                 store: new Roo.data.SimpleStore({
24166                     id : 'val',
24167                     fields: ['val', 'selected'],
24168                     data : [] 
24169                 }),
24170                 name : '-roo-edit-className',
24171                 attrname : 'className',
24172                 displayField: 'val',
24173                 typeAhead: false,
24174                 mode: 'local',
24175                 editable : false,
24176                 triggerAction: 'all',
24177                 emptyText:'Select Style',
24178                 selectOnFocus:true,
24179                 width: 130,
24180                 listeners : {
24181                     'select': function(c, r, i) {
24182                         // initial support only for on class per el..
24183                         tb.selectedNode.className =  r ? r.get('val') : '';
24184                         editorcore.syncValue();
24185                     }
24186                 }
24187     
24188             }));
24189         }
24190         
24191         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24192         var tbops = tbc.options;
24193         
24194         for (var i in tlist) {
24195             
24196             var item = tlist[i];
24197             tb.add(item.title + ":&nbsp;");
24198             
24199             
24200             //optname == used so you can configure the options available..
24201             var opts = item.opts ? item.opts : false;
24202             if (item.optname) {
24203                 opts = tbops[item.optname];
24204            
24205             }
24206             
24207             if (opts) {
24208                 // opts == pulldown..
24209                 tb.addField( new Roo.form.ComboBox({
24210                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24211                         id : 'val',
24212                         fields: ['val', 'display'],
24213                         data : opts  
24214                     }),
24215                     name : '-roo-edit-' + i,
24216                     attrname : i,
24217                     stylename : item.style ? item.style : false,
24218                     displayField: item.displayField ? item.displayField : 'val',
24219                     valueField :  'val',
24220                     typeAhead: false,
24221                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24222                     editable : false,
24223                     triggerAction: 'all',
24224                     emptyText:'Select',
24225                     selectOnFocus:true,
24226                     width: item.width ? item.width  : 130,
24227                     listeners : {
24228                         'select': function(c, r, i) {
24229                             if (c.stylename) {
24230                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24231                                 return;
24232                             }
24233                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24234                         }
24235                     }
24236
24237                 }));
24238                 continue;
24239                     
24240                  
24241                 
24242                 tb.addField( new Roo.form.TextField({
24243                     name: i,
24244                     width: 100,
24245                     //allowBlank:false,
24246                     value: ''
24247                 }));
24248                 continue;
24249             }
24250             tb.addField( new Roo.form.TextField({
24251                 name: '-roo-edit-' + i,
24252                 attrname : i,
24253                 
24254                 width: item.width,
24255                 //allowBlank:true,
24256                 value: '',
24257                 listeners: {
24258                     'change' : function(f, nv, ov) {
24259                         tb.selectedNode.setAttribute(f.attrname, nv);
24260                         editorcore.syncValue();
24261                     }
24262                 }
24263             }));
24264              
24265         }
24266         
24267         var _this = this;
24268         
24269         if(nm == 'BODY'){
24270             tb.addSeparator();
24271         
24272             tb.addButton( {
24273                 text: 'Stylesheets',
24274
24275                 listeners : {
24276                     click : function ()
24277                     {
24278                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24279                     }
24280                 }
24281             });
24282         }
24283         
24284         tb.addFill();
24285         tb.addButton( {
24286             text: 'Remove Tag',
24287     
24288             listeners : {
24289                 click : function ()
24290                 {
24291                     // remove
24292                     // undo does not work.
24293                      
24294                     var sn = tb.selectedNode;
24295                     
24296                     var pn = sn.parentNode;
24297                     
24298                     var stn =  sn.childNodes[0];
24299                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24300                     while (sn.childNodes.length) {
24301                         var node = sn.childNodes[0];
24302                         sn.removeChild(node);
24303                         //Roo.log(node);
24304                         pn.insertBefore(node, sn);
24305                         
24306                     }
24307                     pn.removeChild(sn);
24308                     var range = editorcore.createRange();
24309         
24310                     range.setStart(stn,0);
24311                     range.setEnd(en,0); //????
24312                     //range.selectNode(sel);
24313                     
24314                     
24315                     var selection = editorcore.getSelection();
24316                     selection.removeAllRanges();
24317                     selection.addRange(range);
24318                     
24319                     
24320                     
24321                     //_this.updateToolbar(null, null, pn);
24322                     _this.updateToolbar(null, null, null);
24323                     _this.footDisp.dom.innerHTML = ''; 
24324                 }
24325             }
24326             
24327                     
24328                 
24329             
24330         });
24331         
24332         
24333         tb.el.on('click', function(e){
24334             e.preventDefault(); // what does this do?
24335         });
24336         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24337         tb.el.hide();
24338         tb.name = nm;
24339         // dont need to disable them... as they will get hidden
24340         return tb;
24341          
24342         
24343     },
24344     buildFooter : function()
24345     {
24346         
24347         var fel = this.editor.wrap.createChild();
24348         this.footer = new Roo.Toolbar(fel);
24349         // toolbar has scrolly on left / right?
24350         var footDisp= new Roo.Toolbar.Fill();
24351         var _t = this;
24352         this.footer.add(
24353             {
24354                 text : '&lt;',
24355                 xtype: 'Button',
24356                 handler : function() {
24357                     _t.footDisp.scrollTo('left',0,true)
24358                 }
24359             }
24360         );
24361         this.footer.add( footDisp );
24362         this.footer.add( 
24363             {
24364                 text : '&gt;',
24365                 xtype: 'Button',
24366                 handler : function() {
24367                     // no animation..
24368                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24369                 }
24370             }
24371         );
24372         var fel = Roo.get(footDisp.el);
24373         fel.addClass('x-editor-context');
24374         this.footDispWrap = fel; 
24375         this.footDispWrap.overflow  = 'hidden';
24376         
24377         this.footDisp = fel.createChild();
24378         this.footDispWrap.on('click', this.onContextClick, this)
24379         
24380         
24381     },
24382     onContextClick : function (ev,dom)
24383     {
24384         ev.preventDefault();
24385         var  cn = dom.className;
24386         //Roo.log(cn);
24387         if (!cn.match(/x-ed-loc-/)) {
24388             return;
24389         }
24390         var n = cn.split('-').pop();
24391         var ans = this.footerEls;
24392         var sel = ans[n];
24393         
24394          // pick
24395         var range = this.editorcore.createRange();
24396         
24397         range.selectNodeContents(sel);
24398         //range.selectNode(sel);
24399         
24400         
24401         var selection = this.editorcore.getSelection();
24402         selection.removeAllRanges();
24403         selection.addRange(range);
24404         
24405         
24406         
24407         this.updateToolbar(null, null, sel);
24408         
24409         
24410     }
24411     
24412     
24413     
24414     
24415     
24416 });
24417
24418
24419
24420
24421
24422 /*
24423  * Based on:
24424  * Ext JS Library 1.1.1
24425  * Copyright(c) 2006-2007, Ext JS, LLC.
24426  *
24427  * Originally Released Under LGPL - original licence link has changed is not relivant.
24428  *
24429  * Fork - LGPL
24430  * <script type="text/javascript">
24431  */
24432  
24433 /**
24434  * @class Roo.form.BasicForm
24435  * @extends Roo.util.Observable
24436  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24437  * @constructor
24438  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24439  * @param {Object} config Configuration options
24440  */
24441 Roo.form.BasicForm = function(el, config){
24442     this.allItems = [];
24443     this.childForms = [];
24444     Roo.apply(this, config);
24445     /*
24446      * The Roo.form.Field items in this form.
24447      * @type MixedCollection
24448      */
24449      
24450      
24451     this.items = new Roo.util.MixedCollection(false, function(o){
24452         return o.id || (o.id = Roo.id());
24453     });
24454     this.addEvents({
24455         /**
24456          * @event beforeaction
24457          * Fires before any action is performed. Return false to cancel the action.
24458          * @param {Form} this
24459          * @param {Action} action The action to be performed
24460          */
24461         beforeaction: true,
24462         /**
24463          * @event actionfailed
24464          * Fires when an action fails.
24465          * @param {Form} this
24466          * @param {Action} action The action that failed
24467          */
24468         actionfailed : true,
24469         /**
24470          * @event actioncomplete
24471          * Fires when an action is completed.
24472          * @param {Form} this
24473          * @param {Action} action The action that completed
24474          */
24475         actioncomplete : true
24476     });
24477     if(el){
24478         this.initEl(el);
24479     }
24480     Roo.form.BasicForm.superclass.constructor.call(this);
24481     
24482     Roo.form.BasicForm.popover.apply();
24483 };
24484
24485 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24486     /**
24487      * @cfg {String} method
24488      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24489      */
24490     /**
24491      * @cfg {DataReader} reader
24492      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24493      * This is optional as there is built-in support for processing JSON.
24494      */
24495     /**
24496      * @cfg {DataReader} errorReader
24497      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24498      * This is completely optional as there is built-in support for processing JSON.
24499      */
24500     /**
24501      * @cfg {String} url
24502      * The URL to use for form actions if one isn't supplied in the action options.
24503      */
24504     /**
24505      * @cfg {Boolean} fileUpload
24506      * Set to true if this form is a file upload.
24507      */
24508      
24509     /**
24510      * @cfg {Object} baseParams
24511      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24512      */
24513      /**
24514      
24515     /**
24516      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24517      */
24518     timeout: 30,
24519
24520     // private
24521     activeAction : null,
24522
24523     /**
24524      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24525      * or setValues() data instead of when the form was first created.
24526      */
24527     trackResetOnLoad : false,
24528     
24529     
24530     /**
24531      * childForms - used for multi-tab forms
24532      * @type {Array}
24533      */
24534     childForms : false,
24535     
24536     /**
24537      * allItems - full list of fields.
24538      * @type {Array}
24539      */
24540     allItems : false,
24541     
24542     /**
24543      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24544      * element by passing it or its id or mask the form itself by passing in true.
24545      * @type Mixed
24546      */
24547     waitMsgTarget : false,
24548     
24549     /**
24550      * @type Boolean
24551      */
24552     disableMask : false,
24553     
24554     /**
24555      * @cfg {Boolean} errorMask (true|false) default false
24556      */
24557     errorMask : false,
24558     
24559     /**
24560      * @cfg {Number} maskOffset Default 100
24561      */
24562     maskOffset : 100,
24563
24564     // private
24565     initEl : function(el){
24566         this.el = Roo.get(el);
24567         this.id = this.el.id || Roo.id();
24568         this.el.on('submit', this.onSubmit, this);
24569         this.el.addClass('x-form');
24570     },
24571
24572     // private
24573     onSubmit : function(e){
24574         e.stopEvent();
24575     },
24576
24577     /**
24578      * Returns true if client-side validation on the form is successful.
24579      * @return Boolean
24580      */
24581     isValid : function(){
24582         var valid = true;
24583         var target = false;
24584         this.items.each(function(f){
24585             if(f.validate()){
24586                 return;
24587             }
24588             
24589             valid = false;
24590                 
24591             if(!target && f.el.isVisible(true)){
24592                 target = f;
24593             }
24594         });
24595         
24596         if(this.errorMask && !valid){
24597             Roo.form.BasicForm.popover.mask(this, target);
24598         }
24599         
24600         return valid;
24601     },
24602     /**
24603      * Returns array of invalid form fields.
24604      * @return Array
24605      */
24606     
24607     invalidFields : function()
24608     {
24609         var ret = [];
24610         this.items.each(function(f){
24611             if(f.validate()){
24612                 return;
24613             }
24614             ret.push(f);
24615             
24616         });
24617         
24618         return ret;
24619     },
24620     
24621     
24622     /**
24623      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24624      * @return Boolean
24625      */
24626     isDirty : function(){
24627         var dirty = false;
24628         this.items.each(function(f){
24629            if(f.isDirty()){
24630                dirty = true;
24631                return false;
24632            }
24633         });
24634         return dirty;
24635     },
24636     
24637     /**
24638      * Returns true if any fields in this form have changed since their original load. (New version)
24639      * @return Boolean
24640      */
24641     
24642     hasChanged : function()
24643     {
24644         var dirty = false;
24645         this.items.each(function(f){
24646            if(f.hasChanged()){
24647                dirty = true;
24648                return false;
24649            }
24650         });
24651         return dirty;
24652         
24653     },
24654     /**
24655      * Resets all hasChanged to 'false' -
24656      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24657      * So hasChanged storage is only to be used for this purpose
24658      * @return Boolean
24659      */
24660     resetHasChanged : function()
24661     {
24662         this.items.each(function(f){
24663            f.resetHasChanged();
24664         });
24665         
24666     },
24667     
24668     
24669     /**
24670      * Performs a predefined action (submit or load) or custom actions you define on this form.
24671      * @param {String} actionName The name of the action type
24672      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24673      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24674      * accept other config options):
24675      * <pre>
24676 Property          Type             Description
24677 ----------------  ---------------  ----------------------------------------------------------------------------------
24678 url               String           The url for the action (defaults to the form's url)
24679 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24680 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24681 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24682                                    validate the form on the client (defaults to false)
24683      * </pre>
24684      * @return {BasicForm} this
24685      */
24686     doAction : function(action, options){
24687         if(typeof action == 'string'){
24688             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24689         }
24690         if(this.fireEvent('beforeaction', this, action) !== false){
24691             this.beforeAction(action);
24692             action.run.defer(100, action);
24693         }
24694         return this;
24695     },
24696
24697     /**
24698      * Shortcut to do a submit action.
24699      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24700      * @return {BasicForm} this
24701      */
24702     submit : function(options){
24703         this.doAction('submit', options);
24704         return this;
24705     },
24706
24707     /**
24708      * Shortcut to do a load action.
24709      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24710      * @return {BasicForm} this
24711      */
24712     load : function(options){
24713         this.doAction('load', options);
24714         return this;
24715     },
24716
24717     /**
24718      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24719      * @param {Record} record The record to edit
24720      * @return {BasicForm} this
24721      */
24722     updateRecord : function(record){
24723         record.beginEdit();
24724         var fs = record.fields;
24725         fs.each(function(f){
24726             var field = this.findField(f.name);
24727             if(field){
24728                 record.set(f.name, field.getValue());
24729             }
24730         }, this);
24731         record.endEdit();
24732         return this;
24733     },
24734
24735     /**
24736      * Loads an Roo.data.Record into this form.
24737      * @param {Record} record The record to load
24738      * @return {BasicForm} this
24739      */
24740     loadRecord : function(record){
24741         this.setValues(record.data);
24742         return this;
24743     },
24744
24745     // private
24746     beforeAction : function(action){
24747         var o = action.options;
24748         
24749         if(!this.disableMask) {
24750             if(this.waitMsgTarget === true){
24751                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24752             }else if(this.waitMsgTarget){
24753                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24754                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24755             }else {
24756                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24757             }
24758         }
24759         
24760          
24761     },
24762
24763     // private
24764     afterAction : function(action, success){
24765         this.activeAction = null;
24766         var o = action.options;
24767         
24768         if(!this.disableMask) {
24769             if(this.waitMsgTarget === true){
24770                 this.el.unmask();
24771             }else if(this.waitMsgTarget){
24772                 this.waitMsgTarget.unmask();
24773             }else{
24774                 Roo.MessageBox.updateProgress(1);
24775                 Roo.MessageBox.hide();
24776             }
24777         }
24778         
24779         if(success){
24780             if(o.reset){
24781                 this.reset();
24782             }
24783             Roo.callback(o.success, o.scope, [this, action]);
24784             this.fireEvent('actioncomplete', this, action);
24785             
24786         }else{
24787             
24788             // failure condition..
24789             // we have a scenario where updates need confirming.
24790             // eg. if a locking scenario exists..
24791             // we look for { errors : { needs_confirm : true }} in the response.
24792             if (
24793                 (typeof(action.result) != 'undefined')  &&
24794                 (typeof(action.result.errors) != 'undefined')  &&
24795                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24796            ){
24797                 var _t = this;
24798                 Roo.MessageBox.confirm(
24799                     "Change requires confirmation",
24800                     action.result.errorMsg,
24801                     function(r) {
24802                         if (r != 'yes') {
24803                             return;
24804                         }
24805                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24806                     }
24807                     
24808                 );
24809                 
24810                 
24811                 
24812                 return;
24813             }
24814             
24815             Roo.callback(o.failure, o.scope, [this, action]);
24816             // show an error message if no failed handler is set..
24817             if (!this.hasListener('actionfailed')) {
24818                 Roo.MessageBox.alert("Error",
24819                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24820                         action.result.errorMsg :
24821                         "Saving Failed, please check your entries or try again"
24822                 );
24823             }
24824             
24825             this.fireEvent('actionfailed', this, action);
24826         }
24827         
24828     },
24829
24830     /**
24831      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24832      * @param {String} id The value to search for
24833      * @return Field
24834      */
24835     findField : function(id){
24836         var field = this.items.get(id);
24837         if(!field){
24838             this.items.each(function(f){
24839                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24840                     field = f;
24841                     return false;
24842                 }
24843             });
24844         }
24845         return field || null;
24846     },
24847
24848     /**
24849      * Add a secondary form to this one, 
24850      * Used to provide tabbed forms. One form is primary, with hidden values 
24851      * which mirror the elements from the other forms.
24852      * 
24853      * @param {Roo.form.Form} form to add.
24854      * 
24855      */
24856     addForm : function(form)
24857     {
24858        
24859         if (this.childForms.indexOf(form) > -1) {
24860             // already added..
24861             return;
24862         }
24863         this.childForms.push(form);
24864         var n = '';
24865         Roo.each(form.allItems, function (fe) {
24866             
24867             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24868             if (this.findField(n)) { // already added..
24869                 return;
24870             }
24871             var add = new Roo.form.Hidden({
24872                 name : n
24873             });
24874             add.render(this.el);
24875             
24876             this.add( add );
24877         }, this);
24878         
24879     },
24880     /**
24881      * Mark fields in this form invalid in bulk.
24882      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24883      * @return {BasicForm} this
24884      */
24885     markInvalid : function(errors){
24886         if(errors instanceof Array){
24887             for(var i = 0, len = errors.length; i < len; i++){
24888                 var fieldError = errors[i];
24889                 var f = this.findField(fieldError.id);
24890                 if(f){
24891                     f.markInvalid(fieldError.msg);
24892                 }
24893             }
24894         }else{
24895             var field, id;
24896             for(id in errors){
24897                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24898                     field.markInvalid(errors[id]);
24899                 }
24900             }
24901         }
24902         Roo.each(this.childForms || [], function (f) {
24903             f.markInvalid(errors);
24904         });
24905         
24906         return this;
24907     },
24908
24909     /**
24910      * Set values for fields in this form in bulk.
24911      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24912      * @return {BasicForm} this
24913      */
24914     setValues : function(values){
24915         if(values instanceof Array){ // array of objects
24916             for(var i = 0, len = values.length; i < len; i++){
24917                 var v = values[i];
24918                 var f = this.findField(v.id);
24919                 if(f){
24920                     f.setValue(v.value);
24921                     if(this.trackResetOnLoad){
24922                         f.originalValue = f.getValue();
24923                     }
24924                 }
24925             }
24926         }else{ // object hash
24927             var field, id;
24928             for(id in values){
24929                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24930                     
24931                     if (field.setFromData && 
24932                         field.valueField && 
24933                         field.displayField &&
24934                         // combos' with local stores can 
24935                         // be queried via setValue()
24936                         // to set their value..
24937                         (field.store && !field.store.isLocal)
24938                         ) {
24939                         // it's a combo
24940                         var sd = { };
24941                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24942                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24943                         field.setFromData(sd);
24944                         
24945                     } else {
24946                         field.setValue(values[id]);
24947                     }
24948                     
24949                     
24950                     if(this.trackResetOnLoad){
24951                         field.originalValue = field.getValue();
24952                     }
24953                 }
24954             }
24955         }
24956         this.resetHasChanged();
24957         
24958         
24959         Roo.each(this.childForms || [], function (f) {
24960             f.setValues(values);
24961             f.resetHasChanged();
24962         });
24963                 
24964         return this;
24965     },
24966  
24967     /**
24968      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24969      * they are returned as an array.
24970      * @param {Boolean} asString
24971      * @return {Object}
24972      */
24973     getValues : function(asString){
24974         if (this.childForms) {
24975             // copy values from the child forms
24976             Roo.each(this.childForms, function (f) {
24977                 this.setValues(f.getValues());
24978             }, this);
24979         }
24980         
24981         // use formdata
24982         if (typeof(FormData) != 'undefined' && asString !== true) {
24983             // this relies on a 'recent' version of chrome apparently...
24984             try {
24985                 var fd = (new FormData(this.el.dom)).entries();
24986                 var ret = {};
24987                 var ent = fd.next();
24988                 while (!ent.done) {
24989                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
24990                     ent = fd.next();
24991                 };
24992                 return ret;
24993             } catch(e) {
24994                 
24995             }
24996             
24997         }
24998         
24999         
25000         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25001         if(asString === true){
25002             return fs;
25003         }
25004         return Roo.urlDecode(fs);
25005     },
25006     
25007     /**
25008      * Returns the fields in this form as an object with key/value pairs. 
25009      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25010      * @return {Object}
25011      */
25012     getFieldValues : function(with_hidden)
25013     {
25014         if (this.childForms) {
25015             // copy values from the child forms
25016             // should this call getFieldValues - probably not as we do not currently copy
25017             // hidden fields when we generate..
25018             Roo.each(this.childForms, function (f) {
25019                 this.setValues(f.getValues());
25020             }, this);
25021         }
25022         
25023         var ret = {};
25024         this.items.each(function(f){
25025             if (!f.getName()) {
25026                 return;
25027             }
25028             var v = f.getValue();
25029             if (f.inputType =='radio') {
25030                 if (typeof(ret[f.getName()]) == 'undefined') {
25031                     ret[f.getName()] = ''; // empty..
25032                 }
25033                 
25034                 if (!f.el.dom.checked) {
25035                     return;
25036                     
25037                 }
25038                 v = f.el.dom.value;
25039                 
25040             }
25041             
25042             // not sure if this supported any more..
25043             if ((typeof(v) == 'object') && f.getRawValue) {
25044                 v = f.getRawValue() ; // dates..
25045             }
25046             // combo boxes where name != hiddenName...
25047             if (f.name != f.getName()) {
25048                 ret[f.name] = f.getRawValue();
25049             }
25050             ret[f.getName()] = v;
25051         });
25052         
25053         return ret;
25054     },
25055
25056     /**
25057      * Clears all invalid messages in this form.
25058      * @return {BasicForm} this
25059      */
25060     clearInvalid : function(){
25061         this.items.each(function(f){
25062            f.clearInvalid();
25063         });
25064         
25065         Roo.each(this.childForms || [], function (f) {
25066             f.clearInvalid();
25067         });
25068         
25069         
25070         return this;
25071     },
25072
25073     /**
25074      * Resets this form.
25075      * @return {BasicForm} this
25076      */
25077     reset : function(){
25078         this.items.each(function(f){
25079             f.reset();
25080         });
25081         
25082         Roo.each(this.childForms || [], function (f) {
25083             f.reset();
25084         });
25085         this.resetHasChanged();
25086         
25087         return this;
25088     },
25089
25090     /**
25091      * Add Roo.form components to this form.
25092      * @param {Field} field1
25093      * @param {Field} field2 (optional)
25094      * @param {Field} etc (optional)
25095      * @return {BasicForm} this
25096      */
25097     add : function(){
25098         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25099         return this;
25100     },
25101
25102
25103     /**
25104      * Removes a field from the items collection (does NOT remove its markup).
25105      * @param {Field} field
25106      * @return {BasicForm} this
25107      */
25108     remove : function(field){
25109         this.items.remove(field);
25110         return this;
25111     },
25112
25113     /**
25114      * Looks at the fields in this form, checks them for an id attribute,
25115      * and calls applyTo on the existing dom element with that id.
25116      * @return {BasicForm} this
25117      */
25118     render : function(){
25119         this.items.each(function(f){
25120             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25121                 f.applyTo(f.id);
25122             }
25123         });
25124         return this;
25125     },
25126
25127     /**
25128      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25129      * @param {Object} values
25130      * @return {BasicForm} this
25131      */
25132     applyToFields : function(o){
25133         this.items.each(function(f){
25134            Roo.apply(f, o);
25135         });
25136         return this;
25137     },
25138
25139     /**
25140      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25141      * @param {Object} values
25142      * @return {BasicForm} this
25143      */
25144     applyIfToFields : function(o){
25145         this.items.each(function(f){
25146            Roo.applyIf(f, o);
25147         });
25148         return this;
25149     }
25150 });
25151
25152 // back compat
25153 Roo.BasicForm = Roo.form.BasicForm;
25154
25155 Roo.apply(Roo.form.BasicForm, {
25156     
25157     popover : {
25158         
25159         padding : 5,
25160         
25161         isApplied : false,
25162         
25163         isMasked : false,
25164         
25165         form : false,
25166         
25167         target : false,
25168         
25169         intervalID : false,
25170         
25171         maskEl : false,
25172         
25173         apply : function()
25174         {
25175             if(this.isApplied){
25176                 return;
25177             }
25178             
25179             this.maskEl = {
25180                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25181                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25182                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25183                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25184             };
25185             
25186             this.maskEl.top.enableDisplayMode("block");
25187             this.maskEl.left.enableDisplayMode("block");
25188             this.maskEl.bottom.enableDisplayMode("block");
25189             this.maskEl.right.enableDisplayMode("block");
25190             
25191             Roo.get(document.body).on('click', function(){
25192                 this.unmask();
25193             }, this);
25194             
25195             Roo.get(document.body).on('touchstart', function(){
25196                 this.unmask();
25197             }, this);
25198             
25199             this.isApplied = true
25200         },
25201         
25202         mask : function(form, target)
25203         {
25204             this.form = form;
25205             
25206             this.target = target;
25207             
25208             if(!this.form.errorMask || !target.el){
25209                 return;
25210             }
25211             
25212             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25213             
25214             var ot = this.target.el.calcOffsetsTo(scrollable);
25215             
25216             var scrollTo = ot[1] - this.form.maskOffset;
25217             
25218             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25219             
25220             scrollable.scrollTo('top', scrollTo);
25221             
25222             var el = this.target.wrap || this.target.el;
25223             
25224             var box = el.getBox();
25225             
25226             this.maskEl.top.setStyle('position', 'absolute');
25227             this.maskEl.top.setStyle('z-index', 10000);
25228             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25229             this.maskEl.top.setLeft(0);
25230             this.maskEl.top.setTop(0);
25231             this.maskEl.top.show();
25232             
25233             this.maskEl.left.setStyle('position', 'absolute');
25234             this.maskEl.left.setStyle('z-index', 10000);
25235             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25236             this.maskEl.left.setLeft(0);
25237             this.maskEl.left.setTop(box.y - this.padding);
25238             this.maskEl.left.show();
25239
25240             this.maskEl.bottom.setStyle('position', 'absolute');
25241             this.maskEl.bottom.setStyle('z-index', 10000);
25242             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25243             this.maskEl.bottom.setLeft(0);
25244             this.maskEl.bottom.setTop(box.bottom + this.padding);
25245             this.maskEl.bottom.show();
25246
25247             this.maskEl.right.setStyle('position', 'absolute');
25248             this.maskEl.right.setStyle('z-index', 10000);
25249             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25250             this.maskEl.right.setLeft(box.right + this.padding);
25251             this.maskEl.right.setTop(box.y - this.padding);
25252             this.maskEl.right.show();
25253
25254             this.intervalID = window.setInterval(function() {
25255                 Roo.form.BasicForm.popover.unmask();
25256             }, 10000);
25257
25258             window.onwheel = function(){ return false;};
25259             
25260             (function(){ this.isMasked = true; }).defer(500, this);
25261             
25262         },
25263         
25264         unmask : function()
25265         {
25266             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25267                 return;
25268             }
25269             
25270             this.maskEl.top.setStyle('position', 'absolute');
25271             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25272             this.maskEl.top.hide();
25273
25274             this.maskEl.left.setStyle('position', 'absolute');
25275             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25276             this.maskEl.left.hide();
25277
25278             this.maskEl.bottom.setStyle('position', 'absolute');
25279             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25280             this.maskEl.bottom.hide();
25281
25282             this.maskEl.right.setStyle('position', 'absolute');
25283             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25284             this.maskEl.right.hide();
25285             
25286             window.onwheel = function(){ return true;};
25287             
25288             if(this.intervalID){
25289                 window.clearInterval(this.intervalID);
25290                 this.intervalID = false;
25291             }
25292             
25293             this.isMasked = false;
25294             
25295         }
25296         
25297     }
25298     
25299 });/*
25300  * Based on:
25301  * Ext JS Library 1.1.1
25302  * Copyright(c) 2006-2007, Ext JS, LLC.
25303  *
25304  * Originally Released Under LGPL - original licence link has changed is not relivant.
25305  *
25306  * Fork - LGPL
25307  * <script type="text/javascript">
25308  */
25309
25310 /**
25311  * @class Roo.form.Form
25312  * @extends Roo.form.BasicForm
25313  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25314  * @constructor
25315  * @param {Object} config Configuration options
25316  */
25317 Roo.form.Form = function(config){
25318     var xitems =  [];
25319     if (config.items) {
25320         xitems = config.items;
25321         delete config.items;
25322     }
25323    
25324     
25325     Roo.form.Form.superclass.constructor.call(this, null, config);
25326     this.url = this.url || this.action;
25327     if(!this.root){
25328         this.root = new Roo.form.Layout(Roo.applyIf({
25329             id: Roo.id()
25330         }, config));
25331     }
25332     this.active = this.root;
25333     /**
25334      * Array of all the buttons that have been added to this form via {@link addButton}
25335      * @type Array
25336      */
25337     this.buttons = [];
25338     this.allItems = [];
25339     this.addEvents({
25340         /**
25341          * @event clientvalidation
25342          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25343          * @param {Form} this
25344          * @param {Boolean} valid true if the form has passed client-side validation
25345          */
25346         clientvalidation: true,
25347         /**
25348          * @event rendered
25349          * Fires when the form is rendered
25350          * @param {Roo.form.Form} form
25351          */
25352         rendered : true
25353     });
25354     
25355     if (this.progressUrl) {
25356             // push a hidden field onto the list of fields..
25357             this.addxtype( {
25358                     xns: Roo.form, 
25359                     xtype : 'Hidden', 
25360                     name : 'UPLOAD_IDENTIFIER' 
25361             });
25362         }
25363         
25364     
25365     Roo.each(xitems, this.addxtype, this);
25366     
25367 };
25368
25369 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25370     /**
25371      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25372      */
25373     /**
25374      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25375      */
25376     /**
25377      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25378      */
25379     buttonAlign:'center',
25380
25381     /**
25382      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25383      */
25384     minButtonWidth:75,
25385
25386     /**
25387      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25388      * This property cascades to child containers if not set.
25389      */
25390     labelAlign:'left',
25391
25392     /**
25393      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25394      * fires a looping event with that state. This is required to bind buttons to the valid
25395      * state using the config value formBind:true on the button.
25396      */
25397     monitorValid : false,
25398
25399     /**
25400      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25401      */
25402     monitorPoll : 200,
25403     
25404     /**
25405      * @cfg {String} progressUrl - Url to return progress data 
25406      */
25407     
25408     progressUrl : false,
25409     /**
25410      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25411      * sending a formdata with extra parameters - eg uploaded elements.
25412      */
25413     
25414     formData : false,
25415     
25416     /**
25417      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25418      * fields are added and the column is closed. If no fields are passed the column remains open
25419      * until end() is called.
25420      * @param {Object} config The config to pass to the column
25421      * @param {Field} field1 (optional)
25422      * @param {Field} field2 (optional)
25423      * @param {Field} etc (optional)
25424      * @return Column The column container object
25425      */
25426     column : function(c){
25427         var col = new Roo.form.Column(c);
25428         this.start(col);
25429         if(arguments.length > 1){ // duplicate code required because of Opera
25430             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25431             this.end();
25432         }
25433         return col;
25434     },
25435
25436     /**
25437      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25438      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25439      * until end() is called.
25440      * @param {Object} config The config to pass to the fieldset
25441      * @param {Field} field1 (optional)
25442      * @param {Field} field2 (optional)
25443      * @param {Field} etc (optional)
25444      * @return FieldSet The fieldset container object
25445      */
25446     fieldset : function(c){
25447         var fs = new Roo.form.FieldSet(c);
25448         this.start(fs);
25449         if(arguments.length > 1){ // duplicate code required because of Opera
25450             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25451             this.end();
25452         }
25453         return fs;
25454     },
25455
25456     /**
25457      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25458      * fields are added and the container is closed. If no fields are passed the container remains open
25459      * until end() is called.
25460      * @param {Object} config The config to pass to the Layout
25461      * @param {Field} field1 (optional)
25462      * @param {Field} field2 (optional)
25463      * @param {Field} etc (optional)
25464      * @return Layout The container object
25465      */
25466     container : function(c){
25467         var l = new Roo.form.Layout(c);
25468         this.start(l);
25469         if(arguments.length > 1){ // duplicate code required because of Opera
25470             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25471             this.end();
25472         }
25473         return l;
25474     },
25475
25476     /**
25477      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25478      * @param {Object} container A Roo.form.Layout or subclass of Layout
25479      * @return {Form} this
25480      */
25481     start : function(c){
25482         // cascade label info
25483         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25484         this.active.stack.push(c);
25485         c.ownerCt = this.active;
25486         this.active = c;
25487         return this;
25488     },
25489
25490     /**
25491      * Closes the current open container
25492      * @return {Form} this
25493      */
25494     end : function(){
25495         if(this.active == this.root){
25496             return this;
25497         }
25498         this.active = this.active.ownerCt;
25499         return this;
25500     },
25501
25502     /**
25503      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25504      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25505      * as the label of the field.
25506      * @param {Field} field1
25507      * @param {Field} field2 (optional)
25508      * @param {Field} etc. (optional)
25509      * @return {Form} this
25510      */
25511     add : function(){
25512         this.active.stack.push.apply(this.active.stack, arguments);
25513         this.allItems.push.apply(this.allItems,arguments);
25514         var r = [];
25515         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25516             if(a[i].isFormField){
25517                 r.push(a[i]);
25518             }
25519         }
25520         if(r.length > 0){
25521             Roo.form.Form.superclass.add.apply(this, r);
25522         }
25523         return this;
25524     },
25525     
25526
25527     
25528     
25529     
25530      /**
25531      * Find any element that has been added to a form, using it's ID or name
25532      * This can include framesets, columns etc. along with regular fields..
25533      * @param {String} id - id or name to find.
25534      
25535      * @return {Element} e - or false if nothing found.
25536      */
25537     findbyId : function(id)
25538     {
25539         var ret = false;
25540         if (!id) {
25541             return ret;
25542         }
25543         Roo.each(this.allItems, function(f){
25544             if (f.id == id || f.name == id ){
25545                 ret = f;
25546                 return false;
25547             }
25548         });
25549         return ret;
25550     },
25551
25552     
25553     
25554     /**
25555      * Render this form into the passed container. This should only be called once!
25556      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25557      * @return {Form} this
25558      */
25559     render : function(ct)
25560     {
25561         
25562         
25563         
25564         ct = Roo.get(ct);
25565         var o = this.autoCreate || {
25566             tag: 'form',
25567             method : this.method || 'POST',
25568             id : this.id || Roo.id()
25569         };
25570         this.initEl(ct.createChild(o));
25571
25572         this.root.render(this.el);
25573         
25574        
25575              
25576         this.items.each(function(f){
25577             f.render('x-form-el-'+f.id);
25578         });
25579
25580         if(this.buttons.length > 0){
25581             // tables are required to maintain order and for correct IE layout
25582             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25583                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25584                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25585             }}, null, true);
25586             var tr = tb.getElementsByTagName('tr')[0];
25587             for(var i = 0, len = this.buttons.length; i < len; i++) {
25588                 var b = this.buttons[i];
25589                 var td = document.createElement('td');
25590                 td.className = 'x-form-btn-td';
25591                 b.render(tr.appendChild(td));
25592             }
25593         }
25594         if(this.monitorValid){ // initialize after render
25595             this.startMonitoring();
25596         }
25597         this.fireEvent('rendered', this);
25598         return this;
25599     },
25600
25601     /**
25602      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25603      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25604      * object or a valid Roo.DomHelper element config
25605      * @param {Function} handler The function called when the button is clicked
25606      * @param {Object} scope (optional) The scope of the handler function
25607      * @return {Roo.Button}
25608      */
25609     addButton : function(config, handler, scope){
25610         var bc = {
25611             handler: handler,
25612             scope: scope,
25613             minWidth: this.minButtonWidth,
25614             hideParent:true
25615         };
25616         if(typeof config == "string"){
25617             bc.text = config;
25618         }else{
25619             Roo.apply(bc, config);
25620         }
25621         var btn = new Roo.Button(null, bc);
25622         this.buttons.push(btn);
25623         return btn;
25624     },
25625
25626      /**
25627      * Adds a series of form elements (using the xtype property as the factory method.
25628      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25629      * @param {Object} config 
25630      */
25631     
25632     addxtype : function()
25633     {
25634         var ar = Array.prototype.slice.call(arguments, 0);
25635         var ret = false;
25636         for(var i = 0; i < ar.length; i++) {
25637             if (!ar[i]) {
25638                 continue; // skip -- if this happends something invalid got sent, we 
25639                 // should ignore it, as basically that interface element will not show up
25640                 // and that should be pretty obvious!!
25641             }
25642             
25643             if (Roo.form[ar[i].xtype]) {
25644                 ar[i].form = this;
25645                 var fe = Roo.factory(ar[i], Roo.form);
25646                 if (!ret) {
25647                     ret = fe;
25648                 }
25649                 fe.form = this;
25650                 if (fe.store) {
25651                     fe.store.form = this;
25652                 }
25653                 if (fe.isLayout) {  
25654                          
25655                     this.start(fe);
25656                     this.allItems.push(fe);
25657                     if (fe.items && fe.addxtype) {
25658                         fe.addxtype.apply(fe, fe.items);
25659                         delete fe.items;
25660                     }
25661                      this.end();
25662                     continue;
25663                 }
25664                 
25665                 
25666                  
25667                 this.add(fe);
25668               //  console.log('adding ' + ar[i].xtype);
25669             }
25670             if (ar[i].xtype == 'Button') {  
25671                 //console.log('adding button');
25672                 //console.log(ar[i]);
25673                 this.addButton(ar[i]);
25674                 this.allItems.push(fe);
25675                 continue;
25676             }
25677             
25678             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25679                 alert('end is not supported on xtype any more, use items');
25680             //    this.end();
25681             //    //console.log('adding end');
25682             }
25683             
25684         }
25685         return ret;
25686     },
25687     
25688     /**
25689      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25690      * option "monitorValid"
25691      */
25692     startMonitoring : function(){
25693         if(!this.bound){
25694             this.bound = true;
25695             Roo.TaskMgr.start({
25696                 run : this.bindHandler,
25697                 interval : this.monitorPoll || 200,
25698                 scope: this
25699             });
25700         }
25701     },
25702
25703     /**
25704      * Stops monitoring of the valid state of this form
25705      */
25706     stopMonitoring : function(){
25707         this.bound = false;
25708     },
25709
25710     // private
25711     bindHandler : function(){
25712         if(!this.bound){
25713             return false; // stops binding
25714         }
25715         var valid = true;
25716         this.items.each(function(f){
25717             if(!f.isValid(true)){
25718                 valid = false;
25719                 return false;
25720             }
25721         });
25722         for(var i = 0, len = this.buttons.length; i < len; i++){
25723             var btn = this.buttons[i];
25724             if(btn.formBind === true && btn.disabled === valid){
25725                 btn.setDisabled(!valid);
25726             }
25727         }
25728         this.fireEvent('clientvalidation', this, valid);
25729     }
25730     
25731     
25732     
25733     
25734     
25735     
25736     
25737     
25738 });
25739
25740
25741 // back compat
25742 Roo.Form = Roo.form.Form;
25743 /*
25744  * Based on:
25745  * Ext JS Library 1.1.1
25746  * Copyright(c) 2006-2007, Ext JS, LLC.
25747  *
25748  * Originally Released Under LGPL - original licence link has changed is not relivant.
25749  *
25750  * Fork - LGPL
25751  * <script type="text/javascript">
25752  */
25753
25754 // as we use this in bootstrap.
25755 Roo.namespace('Roo.form');
25756  /**
25757  * @class Roo.form.Action
25758  * Internal Class used to handle form actions
25759  * @constructor
25760  * @param {Roo.form.BasicForm} el The form element or its id
25761  * @param {Object} config Configuration options
25762  */
25763
25764  
25765  
25766 // define the action interface
25767 Roo.form.Action = function(form, options){
25768     this.form = form;
25769     this.options = options || {};
25770 };
25771 /**
25772  * Client Validation Failed
25773  * @const 
25774  */
25775 Roo.form.Action.CLIENT_INVALID = 'client';
25776 /**
25777  * Server Validation Failed
25778  * @const 
25779  */
25780 Roo.form.Action.SERVER_INVALID = 'server';
25781  /**
25782  * Connect to Server Failed
25783  * @const 
25784  */
25785 Roo.form.Action.CONNECT_FAILURE = 'connect';
25786 /**
25787  * Reading Data from Server Failed
25788  * @const 
25789  */
25790 Roo.form.Action.LOAD_FAILURE = 'load';
25791
25792 Roo.form.Action.prototype = {
25793     type : 'default',
25794     failureType : undefined,
25795     response : undefined,
25796     result : undefined,
25797
25798     // interface method
25799     run : function(options){
25800
25801     },
25802
25803     // interface method
25804     success : function(response){
25805
25806     },
25807
25808     // interface method
25809     handleResponse : function(response){
25810
25811     },
25812
25813     // default connection failure
25814     failure : function(response){
25815         
25816         this.response = response;
25817         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25818         this.form.afterAction(this, false);
25819     },
25820
25821     processResponse : function(response){
25822         this.response = response;
25823         if(!response.responseText){
25824             return true;
25825         }
25826         this.result = this.handleResponse(response);
25827         return this.result;
25828     },
25829
25830     // utility functions used internally
25831     getUrl : function(appendParams){
25832         var url = this.options.url || this.form.url || this.form.el.dom.action;
25833         if(appendParams){
25834             var p = this.getParams();
25835             if(p){
25836                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25837             }
25838         }
25839         return url;
25840     },
25841
25842     getMethod : function(){
25843         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25844     },
25845
25846     getParams : function(){
25847         var bp = this.form.baseParams;
25848         var p = this.options.params;
25849         if(p){
25850             if(typeof p == "object"){
25851                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25852             }else if(typeof p == 'string' && bp){
25853                 p += '&' + Roo.urlEncode(bp);
25854             }
25855         }else if(bp){
25856             p = Roo.urlEncode(bp);
25857         }
25858         return p;
25859     },
25860
25861     createCallback : function(){
25862         return {
25863             success: this.success,
25864             failure: this.failure,
25865             scope: this,
25866             timeout: (this.form.timeout*1000),
25867             upload: this.form.fileUpload ? this.success : undefined
25868         };
25869     }
25870 };
25871
25872 Roo.form.Action.Submit = function(form, options){
25873     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25874 };
25875
25876 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25877     type : 'submit',
25878
25879     haveProgress : false,
25880     uploadComplete : false,
25881     
25882     // uploadProgress indicator.
25883     uploadProgress : function()
25884     {
25885         if (!this.form.progressUrl) {
25886             return;
25887         }
25888         
25889         if (!this.haveProgress) {
25890             Roo.MessageBox.progress("Uploading", "Uploading");
25891         }
25892         if (this.uploadComplete) {
25893            Roo.MessageBox.hide();
25894            return;
25895         }
25896         
25897         this.haveProgress = true;
25898    
25899         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25900         
25901         var c = new Roo.data.Connection();
25902         c.request({
25903             url : this.form.progressUrl,
25904             params: {
25905                 id : uid
25906             },
25907             method: 'GET',
25908             success : function(req){
25909                //console.log(data);
25910                 var rdata = false;
25911                 var edata;
25912                 try  {
25913                    rdata = Roo.decode(req.responseText)
25914                 } catch (e) {
25915                     Roo.log("Invalid data from server..");
25916                     Roo.log(edata);
25917                     return;
25918                 }
25919                 if (!rdata || !rdata.success) {
25920                     Roo.log(rdata);
25921                     Roo.MessageBox.alert(Roo.encode(rdata));
25922                     return;
25923                 }
25924                 var data = rdata.data;
25925                 
25926                 if (this.uploadComplete) {
25927                    Roo.MessageBox.hide();
25928                    return;
25929                 }
25930                    
25931                 if (data){
25932                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25933                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25934                     );
25935                 }
25936                 this.uploadProgress.defer(2000,this);
25937             },
25938        
25939             failure: function(data) {
25940                 Roo.log('progress url failed ');
25941                 Roo.log(data);
25942             },
25943             scope : this
25944         });
25945            
25946     },
25947     
25948     
25949     run : function()
25950     {
25951         // run get Values on the form, so it syncs any secondary forms.
25952         this.form.getValues();
25953         
25954         var o = this.options;
25955         var method = this.getMethod();
25956         var isPost = method == 'POST';
25957         if(o.clientValidation === false || this.form.isValid()){
25958             
25959             if (this.form.progressUrl) {
25960                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25961                     (new Date() * 1) + '' + Math.random());
25962                     
25963             } 
25964             
25965             
25966             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25967                 form:this.form.el.dom,
25968                 url:this.getUrl(!isPost),
25969                 method: method,
25970                 params:isPost ? this.getParams() : null,
25971                 isUpload: this.form.fileUpload,
25972                 formData : this.form.formData
25973             }));
25974             
25975             this.uploadProgress();
25976
25977         }else if (o.clientValidation !== false){ // client validation failed
25978             this.failureType = Roo.form.Action.CLIENT_INVALID;
25979             this.form.afterAction(this, false);
25980         }
25981     },
25982
25983     success : function(response)
25984     {
25985         this.uploadComplete= true;
25986         if (this.haveProgress) {
25987             Roo.MessageBox.hide();
25988         }
25989         
25990         
25991         var result = this.processResponse(response);
25992         if(result === true || result.success){
25993             this.form.afterAction(this, true);
25994             return;
25995         }
25996         if(result.errors){
25997             this.form.markInvalid(result.errors);
25998             this.failureType = Roo.form.Action.SERVER_INVALID;
25999         }
26000         this.form.afterAction(this, false);
26001     },
26002     failure : function(response)
26003     {
26004         this.uploadComplete= true;
26005         if (this.haveProgress) {
26006             Roo.MessageBox.hide();
26007         }
26008         
26009         this.response = response;
26010         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26011         this.form.afterAction(this, false);
26012     },
26013     
26014     handleResponse : function(response){
26015         if(this.form.errorReader){
26016             var rs = this.form.errorReader.read(response);
26017             var errors = [];
26018             if(rs.records){
26019                 for(var i = 0, len = rs.records.length; i < len; i++) {
26020                     var r = rs.records[i];
26021                     errors[i] = r.data;
26022                 }
26023             }
26024             if(errors.length < 1){
26025                 errors = null;
26026             }
26027             return {
26028                 success : rs.success,
26029                 errors : errors
26030             };
26031         }
26032         var ret = false;
26033         try {
26034             ret = Roo.decode(response.responseText);
26035         } catch (e) {
26036             ret = {
26037                 success: false,
26038                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26039                 errors : []
26040             };
26041         }
26042         return ret;
26043         
26044     }
26045 });
26046
26047
26048 Roo.form.Action.Load = function(form, options){
26049     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26050     this.reader = this.form.reader;
26051 };
26052
26053 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26054     type : 'load',
26055
26056     run : function(){
26057         
26058         Roo.Ajax.request(Roo.apply(
26059                 this.createCallback(), {
26060                     method:this.getMethod(),
26061                     url:this.getUrl(false),
26062                     params:this.getParams()
26063         }));
26064     },
26065
26066     success : function(response){
26067         
26068         var result = this.processResponse(response);
26069         if(result === true || !result.success || !result.data){
26070             this.failureType = Roo.form.Action.LOAD_FAILURE;
26071             this.form.afterAction(this, false);
26072             return;
26073         }
26074         this.form.clearInvalid();
26075         this.form.setValues(result.data);
26076         this.form.afterAction(this, true);
26077     },
26078
26079     handleResponse : function(response){
26080         if(this.form.reader){
26081             var rs = this.form.reader.read(response);
26082             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26083             return {
26084                 success : rs.success,
26085                 data : data
26086             };
26087         }
26088         return Roo.decode(response.responseText);
26089     }
26090 });
26091
26092 Roo.form.Action.ACTION_TYPES = {
26093     'load' : Roo.form.Action.Load,
26094     'submit' : Roo.form.Action.Submit
26095 };/*
26096  * Based on:
26097  * Ext JS Library 1.1.1
26098  * Copyright(c) 2006-2007, Ext JS, LLC.
26099  *
26100  * Originally Released Under LGPL - original licence link has changed is not relivant.
26101  *
26102  * Fork - LGPL
26103  * <script type="text/javascript">
26104  */
26105  
26106 /**
26107  * @class Roo.form.Layout
26108  * @extends Roo.Component
26109  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26110  * @constructor
26111  * @param {Object} config Configuration options
26112  */
26113 Roo.form.Layout = function(config){
26114     var xitems = [];
26115     if (config.items) {
26116         xitems = config.items;
26117         delete config.items;
26118     }
26119     Roo.form.Layout.superclass.constructor.call(this, config);
26120     this.stack = [];
26121     Roo.each(xitems, this.addxtype, this);
26122      
26123 };
26124
26125 Roo.extend(Roo.form.Layout, Roo.Component, {
26126     /**
26127      * @cfg {String/Object} autoCreate
26128      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26129      */
26130     /**
26131      * @cfg {String/Object/Function} style
26132      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26133      * a function which returns such a specification.
26134      */
26135     /**
26136      * @cfg {String} labelAlign
26137      * Valid values are "left," "top" and "right" (defaults to "left")
26138      */
26139     /**
26140      * @cfg {Number} labelWidth
26141      * Fixed width in pixels of all field labels (defaults to undefined)
26142      */
26143     /**
26144      * @cfg {Boolean} clear
26145      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26146      */
26147     clear : true,
26148     /**
26149      * @cfg {String} labelSeparator
26150      * The separator to use after field labels (defaults to ':')
26151      */
26152     labelSeparator : ':',
26153     /**
26154      * @cfg {Boolean} hideLabels
26155      * True to suppress the display of field labels in this layout (defaults to false)
26156      */
26157     hideLabels : false,
26158
26159     // private
26160     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26161     
26162     isLayout : true,
26163     
26164     // private
26165     onRender : function(ct, position){
26166         if(this.el){ // from markup
26167             this.el = Roo.get(this.el);
26168         }else {  // generate
26169             var cfg = this.getAutoCreate();
26170             this.el = ct.createChild(cfg, position);
26171         }
26172         if(this.style){
26173             this.el.applyStyles(this.style);
26174         }
26175         if(this.labelAlign){
26176             this.el.addClass('x-form-label-'+this.labelAlign);
26177         }
26178         if(this.hideLabels){
26179             this.labelStyle = "display:none";
26180             this.elementStyle = "padding-left:0;";
26181         }else{
26182             if(typeof this.labelWidth == 'number'){
26183                 this.labelStyle = "width:"+this.labelWidth+"px;";
26184                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26185             }
26186             if(this.labelAlign == 'top'){
26187                 this.labelStyle = "width:auto;";
26188                 this.elementStyle = "padding-left:0;";
26189             }
26190         }
26191         var stack = this.stack;
26192         var slen = stack.length;
26193         if(slen > 0){
26194             if(!this.fieldTpl){
26195                 var t = new Roo.Template(
26196                     '<div class="x-form-item {5}">',
26197                         '<label for="{0}" style="{2}">{1}{4}</label>',
26198                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26199                         '</div>',
26200                     '</div><div class="x-form-clear-left"></div>'
26201                 );
26202                 t.disableFormats = true;
26203                 t.compile();
26204                 Roo.form.Layout.prototype.fieldTpl = t;
26205             }
26206             for(var i = 0; i < slen; i++) {
26207                 if(stack[i].isFormField){
26208                     this.renderField(stack[i]);
26209                 }else{
26210                     this.renderComponent(stack[i]);
26211                 }
26212             }
26213         }
26214         if(this.clear){
26215             this.el.createChild({cls:'x-form-clear'});
26216         }
26217     },
26218
26219     // private
26220     renderField : function(f){
26221         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26222                f.id, //0
26223                f.fieldLabel, //1
26224                f.labelStyle||this.labelStyle||'', //2
26225                this.elementStyle||'', //3
26226                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26227                f.itemCls||this.itemCls||''  //5
26228        ], true).getPrevSibling());
26229     },
26230
26231     // private
26232     renderComponent : function(c){
26233         c.render(c.isLayout ? this.el : this.el.createChild());    
26234     },
26235     /**
26236      * Adds a object form elements (using the xtype property as the factory method.)
26237      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26238      * @param {Object} config 
26239      */
26240     addxtype : function(o)
26241     {
26242         // create the lement.
26243         o.form = this.form;
26244         var fe = Roo.factory(o, Roo.form);
26245         this.form.allItems.push(fe);
26246         this.stack.push(fe);
26247         
26248         if (fe.isFormField) {
26249             this.form.items.add(fe);
26250         }
26251          
26252         return fe;
26253     }
26254 });
26255
26256 /**
26257  * @class Roo.form.Column
26258  * @extends Roo.form.Layout
26259  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26260  * @constructor
26261  * @param {Object} config Configuration options
26262  */
26263 Roo.form.Column = function(config){
26264     Roo.form.Column.superclass.constructor.call(this, config);
26265 };
26266
26267 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26268     /**
26269      * @cfg {Number/String} width
26270      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26271      */
26272     /**
26273      * @cfg {String/Object} autoCreate
26274      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26275      */
26276
26277     // private
26278     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26279
26280     // private
26281     onRender : function(ct, position){
26282         Roo.form.Column.superclass.onRender.call(this, ct, position);
26283         if(this.width){
26284             this.el.setWidth(this.width);
26285         }
26286     }
26287 });
26288
26289
26290 /**
26291  * @class Roo.form.Row
26292  * @extends Roo.form.Layout
26293  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26294  * @constructor
26295  * @param {Object} config Configuration options
26296  */
26297
26298  
26299 Roo.form.Row = function(config){
26300     Roo.form.Row.superclass.constructor.call(this, config);
26301 };
26302  
26303 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26304       /**
26305      * @cfg {Number/String} width
26306      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26307      */
26308     /**
26309      * @cfg {Number/String} height
26310      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26311      */
26312     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26313     
26314     padWidth : 20,
26315     // private
26316     onRender : function(ct, position){
26317         //console.log('row render');
26318         if(!this.rowTpl){
26319             var t = new Roo.Template(
26320                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26321                     '<label for="{0}" style="{2}">{1}{4}</label>',
26322                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26323                     '</div>',
26324                 '</div>'
26325             );
26326             t.disableFormats = true;
26327             t.compile();
26328             Roo.form.Layout.prototype.rowTpl = t;
26329         }
26330         this.fieldTpl = this.rowTpl;
26331         
26332         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26333         var labelWidth = 100;
26334         
26335         if ((this.labelAlign != 'top')) {
26336             if (typeof this.labelWidth == 'number') {
26337                 labelWidth = this.labelWidth
26338             }
26339             this.padWidth =  20 + labelWidth;
26340             
26341         }
26342         
26343         Roo.form.Column.superclass.onRender.call(this, ct, position);
26344         if(this.width){
26345             this.el.setWidth(this.width);
26346         }
26347         if(this.height){
26348             this.el.setHeight(this.height);
26349         }
26350     },
26351     
26352     // private
26353     renderField : function(f){
26354         f.fieldEl = this.fieldTpl.append(this.el, [
26355                f.id, f.fieldLabel,
26356                f.labelStyle||this.labelStyle||'',
26357                this.elementStyle||'',
26358                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26359                f.itemCls||this.itemCls||'',
26360                f.width ? f.width + this.padWidth : 160 + this.padWidth
26361        ],true);
26362     }
26363 });
26364  
26365
26366 /**
26367  * @class Roo.form.FieldSet
26368  * @extends Roo.form.Layout
26369  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26370  * @constructor
26371  * @param {Object} config Configuration options
26372  */
26373 Roo.form.FieldSet = function(config){
26374     Roo.form.FieldSet.superclass.constructor.call(this, config);
26375 };
26376
26377 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26378     /**
26379      * @cfg {String} legend
26380      * The text to display as the legend for the FieldSet (defaults to '')
26381      */
26382     /**
26383      * @cfg {String/Object} autoCreate
26384      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26385      */
26386
26387     // private
26388     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26389
26390     // private
26391     onRender : function(ct, position){
26392         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26393         if(this.legend){
26394             this.setLegend(this.legend);
26395         }
26396     },
26397
26398     // private
26399     setLegend : function(text){
26400         if(this.rendered){
26401             this.el.child('legend').update(text);
26402         }
26403     }
26404 });/*
26405  * Based on:
26406  * Ext JS Library 1.1.1
26407  * Copyright(c) 2006-2007, Ext JS, LLC.
26408  *
26409  * Originally Released Under LGPL - original licence link has changed is not relivant.
26410  *
26411  * Fork - LGPL
26412  * <script type="text/javascript">
26413  */
26414 /**
26415  * @class Roo.form.VTypes
26416  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26417  * @singleton
26418  */
26419 Roo.form.VTypes = function(){
26420     // closure these in so they are only created once.
26421     var alpha = /^[a-zA-Z_]+$/;
26422     var alphanum = /^[a-zA-Z0-9_]+$/;
26423     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26424     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26425
26426     // All these messages and functions are configurable
26427     return {
26428         /**
26429          * The function used to validate email addresses
26430          * @param {String} value The email address
26431          */
26432         'email' : function(v){
26433             return email.test(v);
26434         },
26435         /**
26436          * The error text to display when the email validation function returns false
26437          * @type String
26438          */
26439         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26440         /**
26441          * The keystroke filter mask to be applied on email input
26442          * @type RegExp
26443          */
26444         'emailMask' : /[a-z0-9_\.\-@]/i,
26445
26446         /**
26447          * The function used to validate URLs
26448          * @param {String} value The URL
26449          */
26450         'url' : function(v){
26451             return url.test(v);
26452         },
26453         /**
26454          * The error text to display when the url validation function returns false
26455          * @type String
26456          */
26457         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26458         
26459         /**
26460          * The function used to validate alpha values
26461          * @param {String} value The value
26462          */
26463         'alpha' : function(v){
26464             return alpha.test(v);
26465         },
26466         /**
26467          * The error text to display when the alpha validation function returns false
26468          * @type String
26469          */
26470         'alphaText' : 'This field should only contain letters and _',
26471         /**
26472          * The keystroke filter mask to be applied on alpha input
26473          * @type RegExp
26474          */
26475         'alphaMask' : /[a-z_]/i,
26476
26477         /**
26478          * The function used to validate alphanumeric values
26479          * @param {String} value The value
26480          */
26481         'alphanum' : function(v){
26482             return alphanum.test(v);
26483         },
26484         /**
26485          * The error text to display when the alphanumeric validation function returns false
26486          * @type String
26487          */
26488         'alphanumText' : 'This field should only contain letters, numbers and _',
26489         /**
26490          * The keystroke filter mask to be applied on alphanumeric input
26491          * @type RegExp
26492          */
26493         'alphanumMask' : /[a-z0-9_]/i
26494     };
26495 }();//<script type="text/javascript">
26496
26497 /**
26498  * @class Roo.form.FCKeditor
26499  * @extends Roo.form.TextArea
26500  * Wrapper around the FCKEditor http://www.fckeditor.net
26501  * @constructor
26502  * Creates a new FCKeditor
26503  * @param {Object} config Configuration options
26504  */
26505 Roo.form.FCKeditor = function(config){
26506     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26507     this.addEvents({
26508          /**
26509          * @event editorinit
26510          * Fired when the editor is initialized - you can add extra handlers here..
26511          * @param {FCKeditor} this
26512          * @param {Object} the FCK object.
26513          */
26514         editorinit : true
26515     });
26516     
26517     
26518 };
26519 Roo.form.FCKeditor.editors = { };
26520 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26521 {
26522     //defaultAutoCreate : {
26523     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26524     //},
26525     // private
26526     /**
26527      * @cfg {Object} fck options - see fck manual for details.
26528      */
26529     fckconfig : false,
26530     
26531     /**
26532      * @cfg {Object} fck toolbar set (Basic or Default)
26533      */
26534     toolbarSet : 'Basic',
26535     /**
26536      * @cfg {Object} fck BasePath
26537      */ 
26538     basePath : '/fckeditor/',
26539     
26540     
26541     frame : false,
26542     
26543     value : '',
26544     
26545    
26546     onRender : function(ct, position)
26547     {
26548         if(!this.el){
26549             this.defaultAutoCreate = {
26550                 tag: "textarea",
26551                 style:"width:300px;height:60px;",
26552                 autocomplete: "new-password"
26553             };
26554         }
26555         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26556         /*
26557         if(this.grow){
26558             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26559             if(this.preventScrollbars){
26560                 this.el.setStyle("overflow", "hidden");
26561             }
26562             this.el.setHeight(this.growMin);
26563         }
26564         */
26565         //console.log('onrender' + this.getId() );
26566         Roo.form.FCKeditor.editors[this.getId()] = this;
26567          
26568
26569         this.replaceTextarea() ;
26570         
26571     },
26572     
26573     getEditor : function() {
26574         return this.fckEditor;
26575     },
26576     /**
26577      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26578      * @param {Mixed} value The value to set
26579      */
26580     
26581     
26582     setValue : function(value)
26583     {
26584         //console.log('setValue: ' + value);
26585         
26586         if(typeof(value) == 'undefined') { // not sure why this is happending...
26587             return;
26588         }
26589         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26590         
26591         //if(!this.el || !this.getEditor()) {
26592         //    this.value = value;
26593             //this.setValue.defer(100,this,[value]);    
26594         //    return;
26595         //} 
26596         
26597         if(!this.getEditor()) {
26598             return;
26599         }
26600         
26601         this.getEditor().SetData(value);
26602         
26603         //
26604
26605     },
26606
26607     /**
26608      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26609      * @return {Mixed} value The field value
26610      */
26611     getValue : function()
26612     {
26613         
26614         if (this.frame && this.frame.dom.style.display == 'none') {
26615             return Roo.form.FCKeditor.superclass.getValue.call(this);
26616         }
26617         
26618         if(!this.el || !this.getEditor()) {
26619            
26620            // this.getValue.defer(100,this); 
26621             return this.value;
26622         }
26623        
26624         
26625         var value=this.getEditor().GetData();
26626         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26627         return Roo.form.FCKeditor.superclass.getValue.call(this);
26628         
26629
26630     },
26631
26632     /**
26633      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26634      * @return {Mixed} value The field value
26635      */
26636     getRawValue : function()
26637     {
26638         if (this.frame && this.frame.dom.style.display == 'none') {
26639             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26640         }
26641         
26642         if(!this.el || !this.getEditor()) {
26643             //this.getRawValue.defer(100,this); 
26644             return this.value;
26645             return;
26646         }
26647         
26648         
26649         
26650         var value=this.getEditor().GetData();
26651         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26652         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26653          
26654     },
26655     
26656     setSize : function(w,h) {
26657         
26658         
26659         
26660         //if (this.frame && this.frame.dom.style.display == 'none') {
26661         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26662         //    return;
26663         //}
26664         //if(!this.el || !this.getEditor()) {
26665         //    this.setSize.defer(100,this, [w,h]); 
26666         //    return;
26667         //}
26668         
26669         
26670         
26671         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26672         
26673         this.frame.dom.setAttribute('width', w);
26674         this.frame.dom.setAttribute('height', h);
26675         this.frame.setSize(w,h);
26676         
26677     },
26678     
26679     toggleSourceEdit : function(value) {
26680         
26681       
26682          
26683         this.el.dom.style.display = value ? '' : 'none';
26684         this.frame.dom.style.display = value ?  'none' : '';
26685         
26686     },
26687     
26688     
26689     focus: function(tag)
26690     {
26691         if (this.frame.dom.style.display == 'none') {
26692             return Roo.form.FCKeditor.superclass.focus.call(this);
26693         }
26694         if(!this.el || !this.getEditor()) {
26695             this.focus.defer(100,this, [tag]); 
26696             return;
26697         }
26698         
26699         
26700         
26701         
26702         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26703         this.getEditor().Focus();
26704         if (tgs.length) {
26705             if (!this.getEditor().Selection.GetSelection()) {
26706                 this.focus.defer(100,this, [tag]); 
26707                 return;
26708             }
26709             
26710             
26711             var r = this.getEditor().EditorDocument.createRange();
26712             r.setStart(tgs[0],0);
26713             r.setEnd(tgs[0],0);
26714             this.getEditor().Selection.GetSelection().removeAllRanges();
26715             this.getEditor().Selection.GetSelection().addRange(r);
26716             this.getEditor().Focus();
26717         }
26718         
26719     },
26720     
26721     
26722     
26723     replaceTextarea : function()
26724     {
26725         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26726             return ;
26727         }
26728         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26729         //{
26730             // We must check the elements firstly using the Id and then the name.
26731         var oTextarea = document.getElementById( this.getId() );
26732         
26733         var colElementsByName = document.getElementsByName( this.getId() ) ;
26734          
26735         oTextarea.style.display = 'none' ;
26736
26737         if ( oTextarea.tabIndex ) {            
26738             this.TabIndex = oTextarea.tabIndex ;
26739         }
26740         
26741         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26742         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26743         this.frame = Roo.get(this.getId() + '___Frame')
26744     },
26745     
26746     _getConfigHtml : function()
26747     {
26748         var sConfig = '' ;
26749
26750         for ( var o in this.fckconfig ) {
26751             sConfig += sConfig.length > 0  ? '&amp;' : '';
26752             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26753         }
26754
26755         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26756     },
26757     
26758     
26759     _getIFrameHtml : function()
26760     {
26761         var sFile = 'fckeditor.html' ;
26762         /* no idea what this is about..
26763         try
26764         {
26765             if ( (/fcksource=true/i).test( window.top.location.search ) )
26766                 sFile = 'fckeditor.original.html' ;
26767         }
26768         catch (e) { 
26769         */
26770
26771         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26772         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26773         
26774         
26775         var html = '<iframe id="' + this.getId() +
26776             '___Frame" src="' + sLink +
26777             '" width="' + this.width +
26778             '" height="' + this.height + '"' +
26779             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26780             ' frameborder="0" scrolling="no"></iframe>' ;
26781
26782         return html ;
26783     },
26784     
26785     _insertHtmlBefore : function( html, element )
26786     {
26787         if ( element.insertAdjacentHTML )       {
26788             // IE
26789             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26790         } else { // Gecko
26791             var oRange = document.createRange() ;
26792             oRange.setStartBefore( element ) ;
26793             var oFragment = oRange.createContextualFragment( html );
26794             element.parentNode.insertBefore( oFragment, element ) ;
26795         }
26796     }
26797     
26798     
26799   
26800     
26801     
26802     
26803     
26804
26805 });
26806
26807 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26808
26809 function FCKeditor_OnComplete(editorInstance){
26810     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26811     f.fckEditor = editorInstance;
26812     //console.log("loaded");
26813     f.fireEvent('editorinit', f, editorInstance);
26814
26815   
26816
26817  
26818
26819
26820
26821
26822
26823
26824
26825
26826
26827
26828
26829
26830
26831
26832
26833 //<script type="text/javascript">
26834 /**
26835  * @class Roo.form.GridField
26836  * @extends Roo.form.Field
26837  * Embed a grid (or editable grid into a form)
26838  * STATUS ALPHA
26839  * 
26840  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26841  * it needs 
26842  * xgrid.store = Roo.data.Store
26843  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26844  * xgrid.store.reader = Roo.data.JsonReader 
26845  * 
26846  * 
26847  * @constructor
26848  * Creates a new GridField
26849  * @param {Object} config Configuration options
26850  */
26851 Roo.form.GridField = function(config){
26852     Roo.form.GridField.superclass.constructor.call(this, config);
26853      
26854 };
26855
26856 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26857     /**
26858      * @cfg {Number} width  - used to restrict width of grid..
26859      */
26860     width : 100,
26861     /**
26862      * @cfg {Number} height - used to restrict height of grid..
26863      */
26864     height : 50,
26865      /**
26866      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26867          * 
26868          *}
26869      */
26870     xgrid : false, 
26871     /**
26872      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26873      * {tag: "input", type: "checkbox", autocomplete: "off"})
26874      */
26875    // defaultAutoCreate : { tag: 'div' },
26876     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26877     /**
26878      * @cfg {String} addTitle Text to include for adding a title.
26879      */
26880     addTitle : false,
26881     //
26882     onResize : function(){
26883         Roo.form.Field.superclass.onResize.apply(this, arguments);
26884     },
26885
26886     initEvents : function(){
26887         // Roo.form.Checkbox.superclass.initEvents.call(this);
26888         // has no events...
26889        
26890     },
26891
26892
26893     getResizeEl : function(){
26894         return this.wrap;
26895     },
26896
26897     getPositionEl : function(){
26898         return this.wrap;
26899     },
26900
26901     // private
26902     onRender : function(ct, position){
26903         
26904         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26905         var style = this.style;
26906         delete this.style;
26907         
26908         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26909         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26910         this.viewEl = this.wrap.createChild({ tag: 'div' });
26911         if (style) {
26912             this.viewEl.applyStyles(style);
26913         }
26914         if (this.width) {
26915             this.viewEl.setWidth(this.width);
26916         }
26917         if (this.height) {
26918             this.viewEl.setHeight(this.height);
26919         }
26920         //if(this.inputValue !== undefined){
26921         //this.setValue(this.value);
26922         
26923         
26924         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26925         
26926         
26927         this.grid.render();
26928         this.grid.getDataSource().on('remove', this.refreshValue, this);
26929         this.grid.getDataSource().on('update', this.refreshValue, this);
26930         this.grid.on('afteredit', this.refreshValue, this);
26931  
26932     },
26933      
26934     
26935     /**
26936      * Sets the value of the item. 
26937      * @param {String} either an object  or a string..
26938      */
26939     setValue : function(v){
26940         //this.value = v;
26941         v = v || []; // empty set..
26942         // this does not seem smart - it really only affects memoryproxy grids..
26943         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26944             var ds = this.grid.getDataSource();
26945             // assumes a json reader..
26946             var data = {}
26947             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26948             ds.loadData( data);
26949         }
26950         // clear selection so it does not get stale.
26951         if (this.grid.sm) { 
26952             this.grid.sm.clearSelections();
26953         }
26954         
26955         Roo.form.GridField.superclass.setValue.call(this, v);
26956         this.refreshValue();
26957         // should load data in the grid really....
26958     },
26959     
26960     // private
26961     refreshValue: function() {
26962          var val = [];
26963         this.grid.getDataSource().each(function(r) {
26964             val.push(r.data);
26965         });
26966         this.el.dom.value = Roo.encode(val);
26967     }
26968     
26969      
26970     
26971     
26972 });/*
26973  * Based on:
26974  * Ext JS Library 1.1.1
26975  * Copyright(c) 2006-2007, Ext JS, LLC.
26976  *
26977  * Originally Released Under LGPL - original licence link has changed is not relivant.
26978  *
26979  * Fork - LGPL
26980  * <script type="text/javascript">
26981  */
26982 /**
26983  * @class Roo.form.DisplayField
26984  * @extends Roo.form.Field
26985  * A generic Field to display non-editable data.
26986  * @cfg {Boolean} closable (true|false) default false
26987  * @constructor
26988  * Creates a new Display Field item.
26989  * @param {Object} config Configuration options
26990  */
26991 Roo.form.DisplayField = function(config){
26992     Roo.form.DisplayField.superclass.constructor.call(this, config);
26993     
26994     this.addEvents({
26995         /**
26996          * @event close
26997          * Fires after the click the close btn
26998              * @param {Roo.form.DisplayField} this
26999              */
27000         close : true
27001     });
27002 };
27003
27004 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27005     inputType:      'hidden',
27006     allowBlank:     true,
27007     readOnly:         true,
27008     
27009  
27010     /**
27011      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27012      */
27013     focusClass : undefined,
27014     /**
27015      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27016      */
27017     fieldClass: 'x-form-field',
27018     
27019      /**
27020      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27021      */
27022     valueRenderer: undefined,
27023     
27024     width: 100,
27025     /**
27026      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27027      * {tag: "input", type: "checkbox", autocomplete: "off"})
27028      */
27029      
27030  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27031  
27032     closable : false,
27033     
27034     onResize : function(){
27035         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27036         
27037     },
27038
27039     initEvents : function(){
27040         // Roo.form.Checkbox.superclass.initEvents.call(this);
27041         // has no events...
27042         
27043         if(this.closable){
27044             this.closeEl.on('click', this.onClose, this);
27045         }
27046        
27047     },
27048
27049
27050     getResizeEl : function(){
27051         return this.wrap;
27052     },
27053
27054     getPositionEl : function(){
27055         return this.wrap;
27056     },
27057
27058     // private
27059     onRender : function(ct, position){
27060         
27061         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27062         //if(this.inputValue !== undefined){
27063         this.wrap = this.el.wrap();
27064         
27065         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27066         
27067         if(this.closable){
27068             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27069         }
27070         
27071         if (this.bodyStyle) {
27072             this.viewEl.applyStyles(this.bodyStyle);
27073         }
27074         //this.viewEl.setStyle('padding', '2px');
27075         
27076         this.setValue(this.value);
27077         
27078     },
27079 /*
27080     // private
27081     initValue : Roo.emptyFn,
27082
27083   */
27084
27085         // private
27086     onClick : function(){
27087         
27088     },
27089
27090     /**
27091      * Sets the checked state of the checkbox.
27092      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27093      */
27094     setValue : function(v){
27095         this.value = v;
27096         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27097         // this might be called before we have a dom element..
27098         if (!this.viewEl) {
27099             return;
27100         }
27101         this.viewEl.dom.innerHTML = html;
27102         Roo.form.DisplayField.superclass.setValue.call(this, v);
27103
27104     },
27105     
27106     onClose : function(e)
27107     {
27108         e.preventDefault();
27109         
27110         this.fireEvent('close', this);
27111     }
27112 });/*
27113  * 
27114  * Licence- LGPL
27115  * 
27116  */
27117
27118 /**
27119  * @class Roo.form.DayPicker
27120  * @extends Roo.form.Field
27121  * A Day picker show [M] [T] [W] ....
27122  * @constructor
27123  * Creates a new Day Picker
27124  * @param {Object} config Configuration options
27125  */
27126 Roo.form.DayPicker= function(config){
27127     Roo.form.DayPicker.superclass.constructor.call(this, config);
27128      
27129 };
27130
27131 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27132     /**
27133      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27134      */
27135     focusClass : undefined,
27136     /**
27137      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27138      */
27139     fieldClass: "x-form-field",
27140    
27141     /**
27142      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27143      * {tag: "input", type: "checkbox", autocomplete: "off"})
27144      */
27145     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27146     
27147    
27148     actionMode : 'viewEl', 
27149     //
27150     // private
27151  
27152     inputType : 'hidden',
27153     
27154      
27155     inputElement: false, // real input element?
27156     basedOn: false, // ????
27157     
27158     isFormField: true, // not sure where this is needed!!!!
27159
27160     onResize : function(){
27161         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27162         if(!this.boxLabel){
27163             this.el.alignTo(this.wrap, 'c-c');
27164         }
27165     },
27166
27167     initEvents : function(){
27168         Roo.form.Checkbox.superclass.initEvents.call(this);
27169         this.el.on("click", this.onClick,  this);
27170         this.el.on("change", this.onClick,  this);
27171     },
27172
27173
27174     getResizeEl : function(){
27175         return this.wrap;
27176     },
27177
27178     getPositionEl : function(){
27179         return this.wrap;
27180     },
27181
27182     
27183     // private
27184     onRender : function(ct, position){
27185         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27186        
27187         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27188         
27189         var r1 = '<table><tr>';
27190         var r2 = '<tr class="x-form-daypick-icons">';
27191         for (var i=0; i < 7; i++) {
27192             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27193             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27194         }
27195         
27196         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27197         viewEl.select('img').on('click', this.onClick, this);
27198         this.viewEl = viewEl;   
27199         
27200         
27201         // this will not work on Chrome!!!
27202         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27203         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27204         
27205         
27206           
27207
27208     },
27209
27210     // private
27211     initValue : Roo.emptyFn,
27212
27213     /**
27214      * Returns the checked state of the checkbox.
27215      * @return {Boolean} True if checked, else false
27216      */
27217     getValue : function(){
27218         return this.el.dom.value;
27219         
27220     },
27221
27222         // private
27223     onClick : function(e){ 
27224         //this.setChecked(!this.checked);
27225         Roo.get(e.target).toggleClass('x-menu-item-checked');
27226         this.refreshValue();
27227         //if(this.el.dom.checked != this.checked){
27228         //    this.setValue(this.el.dom.checked);
27229        // }
27230     },
27231     
27232     // private
27233     refreshValue : function()
27234     {
27235         var val = '';
27236         this.viewEl.select('img',true).each(function(e,i,n)  {
27237             val += e.is(".x-menu-item-checked") ? String(n) : '';
27238         });
27239         this.setValue(val, true);
27240     },
27241
27242     /**
27243      * Sets the checked state of the checkbox.
27244      * On is always based on a string comparison between inputValue and the param.
27245      * @param {Boolean/String} value - the value to set 
27246      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27247      */
27248     setValue : function(v,suppressEvent){
27249         if (!this.el.dom) {
27250             return;
27251         }
27252         var old = this.el.dom.value ;
27253         this.el.dom.value = v;
27254         if (suppressEvent) {
27255             return ;
27256         }
27257          
27258         // update display..
27259         this.viewEl.select('img',true).each(function(e,i,n)  {
27260             
27261             var on = e.is(".x-menu-item-checked");
27262             var newv = v.indexOf(String(n)) > -1;
27263             if (on != newv) {
27264                 e.toggleClass('x-menu-item-checked');
27265             }
27266             
27267         });
27268         
27269         
27270         this.fireEvent('change', this, v, old);
27271         
27272         
27273     },
27274    
27275     // handle setting of hidden value by some other method!!?!?
27276     setFromHidden: function()
27277     {
27278         if(!this.el){
27279             return;
27280         }
27281         //console.log("SET FROM HIDDEN");
27282         //alert('setFrom hidden');
27283         this.setValue(this.el.dom.value);
27284     },
27285     
27286     onDestroy : function()
27287     {
27288         if(this.viewEl){
27289             Roo.get(this.viewEl).remove();
27290         }
27291          
27292         Roo.form.DayPicker.superclass.onDestroy.call(this);
27293     }
27294
27295 });/*
27296  * RooJS Library 1.1.1
27297  * Copyright(c) 2008-2011  Alan Knowles
27298  *
27299  * License - LGPL
27300  */
27301  
27302
27303 /**
27304  * @class Roo.form.ComboCheck
27305  * @extends Roo.form.ComboBox
27306  * A combobox for multiple select items.
27307  *
27308  * FIXME - could do with a reset button..
27309  * 
27310  * @constructor
27311  * Create a new ComboCheck
27312  * @param {Object} config Configuration options
27313  */
27314 Roo.form.ComboCheck = function(config){
27315     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27316     // should verify some data...
27317     // like
27318     // hiddenName = required..
27319     // displayField = required
27320     // valudField == required
27321     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27322     var _t = this;
27323     Roo.each(req, function(e) {
27324         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27325             throw "Roo.form.ComboCheck : missing value for: " + e;
27326         }
27327     });
27328     
27329     
27330 };
27331
27332 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27333      
27334      
27335     editable : false,
27336      
27337     selectedClass: 'x-menu-item-checked', 
27338     
27339     // private
27340     onRender : function(ct, position){
27341         var _t = this;
27342         
27343         
27344         
27345         if(!this.tpl){
27346             var cls = 'x-combo-list';
27347
27348             
27349             this.tpl =  new Roo.Template({
27350                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27351                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27352                    '<span>{' + this.displayField + '}</span>' +
27353                     '</div>' 
27354                 
27355             });
27356         }
27357  
27358         
27359         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27360         this.view.singleSelect = false;
27361         this.view.multiSelect = true;
27362         this.view.toggleSelect = true;
27363         this.pageTb.add(new Roo.Toolbar.Fill(), {
27364             
27365             text: 'Done',
27366             handler: function()
27367             {
27368                 _t.collapse();
27369             }
27370         });
27371     },
27372     
27373     onViewOver : function(e, t){
27374         // do nothing...
27375         return;
27376         
27377     },
27378     
27379     onViewClick : function(doFocus,index){
27380         return;
27381         
27382     },
27383     select: function () {
27384         //Roo.log("SELECT CALLED");
27385     },
27386      
27387     selectByValue : function(xv, scrollIntoView){
27388         var ar = this.getValueArray();
27389         var sels = [];
27390         
27391         Roo.each(ar, function(v) {
27392             if(v === undefined || v === null){
27393                 return;
27394             }
27395             var r = this.findRecord(this.valueField, v);
27396             if(r){
27397                 sels.push(this.store.indexOf(r))
27398                 
27399             }
27400         },this);
27401         this.view.select(sels);
27402         return false;
27403     },
27404     
27405     
27406     
27407     onSelect : function(record, index){
27408        // Roo.log("onselect Called");
27409        // this is only called by the clear button now..
27410         this.view.clearSelections();
27411         this.setValue('[]');
27412         if (this.value != this.valueBefore) {
27413             this.fireEvent('change', this, this.value, this.valueBefore);
27414             this.valueBefore = this.value;
27415         }
27416     },
27417     getValueArray : function()
27418     {
27419         var ar = [] ;
27420         
27421         try {
27422             //Roo.log(this.value);
27423             if (typeof(this.value) == 'undefined') {
27424                 return [];
27425             }
27426             var ar = Roo.decode(this.value);
27427             return  ar instanceof Array ? ar : []; //?? valid?
27428             
27429         } catch(e) {
27430             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27431             return [];
27432         }
27433          
27434     },
27435     expand : function ()
27436     {
27437         
27438         Roo.form.ComboCheck.superclass.expand.call(this);
27439         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27440         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27441         
27442
27443     },
27444     
27445     collapse : function(){
27446         Roo.form.ComboCheck.superclass.collapse.call(this);
27447         var sl = this.view.getSelectedIndexes();
27448         var st = this.store;
27449         var nv = [];
27450         var tv = [];
27451         var r;
27452         Roo.each(sl, function(i) {
27453             r = st.getAt(i);
27454             nv.push(r.get(this.valueField));
27455         },this);
27456         this.setValue(Roo.encode(nv));
27457         if (this.value != this.valueBefore) {
27458
27459             this.fireEvent('change', this, this.value, this.valueBefore);
27460             this.valueBefore = this.value;
27461         }
27462         
27463     },
27464     
27465     setValue : function(v){
27466         // Roo.log(v);
27467         this.value = v;
27468         
27469         var vals = this.getValueArray();
27470         var tv = [];
27471         Roo.each(vals, function(k) {
27472             var r = this.findRecord(this.valueField, k);
27473             if(r){
27474                 tv.push(r.data[this.displayField]);
27475             }else if(this.valueNotFoundText !== undefined){
27476                 tv.push( this.valueNotFoundText );
27477             }
27478         },this);
27479        // Roo.log(tv);
27480         
27481         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27482         this.hiddenField.value = v;
27483         this.value = v;
27484     }
27485     
27486 });/*
27487  * Based on:
27488  * Ext JS Library 1.1.1
27489  * Copyright(c) 2006-2007, Ext JS, LLC.
27490  *
27491  * Originally Released Under LGPL - original licence link has changed is not relivant.
27492  *
27493  * Fork - LGPL
27494  * <script type="text/javascript">
27495  */
27496  
27497 /**
27498  * @class Roo.form.Signature
27499  * @extends Roo.form.Field
27500  * Signature field.  
27501  * @constructor
27502  * 
27503  * @param {Object} config Configuration options
27504  */
27505
27506 Roo.form.Signature = function(config){
27507     Roo.form.Signature.superclass.constructor.call(this, config);
27508     
27509     this.addEvents({// not in used??
27510          /**
27511          * @event confirm
27512          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27513              * @param {Roo.form.Signature} combo This combo box
27514              */
27515         'confirm' : true,
27516         /**
27517          * @event reset
27518          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27519              * @param {Roo.form.ComboBox} combo This combo box
27520              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27521              */
27522         'reset' : true
27523     });
27524 };
27525
27526 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27527     /**
27528      * @cfg {Object} labels Label to use when rendering a form.
27529      * defaults to 
27530      * labels : { 
27531      *      clear : "Clear",
27532      *      confirm : "Confirm"
27533      *  }
27534      */
27535     labels : { 
27536         clear : "Clear",
27537         confirm : "Confirm"
27538     },
27539     /**
27540      * @cfg {Number} width The signature panel width (defaults to 300)
27541      */
27542     width: 300,
27543     /**
27544      * @cfg {Number} height The signature panel height (defaults to 100)
27545      */
27546     height : 100,
27547     /**
27548      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27549      */
27550     allowBlank : false,
27551     
27552     //private
27553     // {Object} signPanel The signature SVG panel element (defaults to {})
27554     signPanel : {},
27555     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27556     isMouseDown : false,
27557     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27558     isConfirmed : false,
27559     // {String} signatureTmp SVG mapping string (defaults to empty string)
27560     signatureTmp : '',
27561     
27562     
27563     defaultAutoCreate : { // modified by initCompnoent..
27564         tag: "input",
27565         type:"hidden"
27566     },
27567
27568     // private
27569     onRender : function(ct, position){
27570         
27571         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27572         
27573         this.wrap = this.el.wrap({
27574             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27575         });
27576         
27577         this.createToolbar(this);
27578         this.signPanel = this.wrap.createChild({
27579                 tag: 'div',
27580                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27581             }, this.el
27582         );
27583             
27584         this.svgID = Roo.id();
27585         this.svgEl = this.signPanel.createChild({
27586               xmlns : 'http://www.w3.org/2000/svg',
27587               tag : 'svg',
27588               id : this.svgID + "-svg",
27589               width: this.width,
27590               height: this.height,
27591               viewBox: '0 0 '+this.width+' '+this.height,
27592               cn : [
27593                 {
27594                     tag: "rect",
27595                     id: this.svgID + "-svg-r",
27596                     width: this.width,
27597                     height: this.height,
27598                     fill: "#ffa"
27599                 },
27600                 {
27601                     tag: "line",
27602                     id: this.svgID + "-svg-l",
27603                     x1: "0", // start
27604                     y1: (this.height*0.8), // start set the line in 80% of height
27605                     x2: this.width, // end
27606                     y2: (this.height*0.8), // end set the line in 80% of height
27607                     'stroke': "#666",
27608                     'stroke-width': "1",
27609                     'stroke-dasharray': "3",
27610                     'shape-rendering': "crispEdges",
27611                     'pointer-events': "none"
27612                 },
27613                 {
27614                     tag: "path",
27615                     id: this.svgID + "-svg-p",
27616                     'stroke': "navy",
27617                     'stroke-width': "3",
27618                     'fill': "none",
27619                     'pointer-events': 'none'
27620                 }
27621               ]
27622         });
27623         this.createSVG();
27624         this.svgBox = this.svgEl.dom.getScreenCTM();
27625     },
27626     createSVG : function(){ 
27627         var svg = this.signPanel;
27628         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27629         var t = this;
27630
27631         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27632         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27633         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27634         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27635         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27636         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27637         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27638         
27639     },
27640     isTouchEvent : function(e){
27641         return e.type.match(/^touch/);
27642     },
27643     getCoords : function (e) {
27644         var pt    = this.svgEl.dom.createSVGPoint();
27645         pt.x = e.clientX; 
27646         pt.y = e.clientY;
27647         if (this.isTouchEvent(e)) {
27648             pt.x =  e.targetTouches[0].clientX;
27649             pt.y = e.targetTouches[0].clientY;
27650         }
27651         var a = this.svgEl.dom.getScreenCTM();
27652         var b = a.inverse();
27653         var mx = pt.matrixTransform(b);
27654         return mx.x + ',' + mx.y;
27655     },
27656     //mouse event headler 
27657     down : function (e) {
27658         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27659         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27660         
27661         this.isMouseDown = true;
27662         
27663         e.preventDefault();
27664     },
27665     move : function (e) {
27666         if (this.isMouseDown) {
27667             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27668             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27669         }
27670         
27671         e.preventDefault();
27672     },
27673     up : function (e) {
27674         this.isMouseDown = false;
27675         var sp = this.signatureTmp.split(' ');
27676         
27677         if(sp.length > 1){
27678             if(!sp[sp.length-2].match(/^L/)){
27679                 sp.pop();
27680                 sp.pop();
27681                 sp.push("");
27682                 this.signatureTmp = sp.join(" ");
27683             }
27684         }
27685         if(this.getValue() != this.signatureTmp){
27686             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27687             this.isConfirmed = false;
27688         }
27689         e.preventDefault();
27690     },
27691     
27692     /**
27693      * Protected method that will not generally be called directly. It
27694      * is called when the editor creates its toolbar. Override this method if you need to
27695      * add custom toolbar buttons.
27696      * @param {HtmlEditor} editor
27697      */
27698     createToolbar : function(editor){
27699          function btn(id, toggle, handler){
27700             var xid = fid + '-'+ id ;
27701             return {
27702                 id : xid,
27703                 cmd : id,
27704                 cls : 'x-btn-icon x-edit-'+id,
27705                 enableToggle:toggle !== false,
27706                 scope: editor, // was editor...
27707                 handler:handler||editor.relayBtnCmd,
27708                 clickEvent:'mousedown',
27709                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27710                 tabIndex:-1
27711             };
27712         }
27713         
27714         
27715         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27716         this.tb = tb;
27717         this.tb.add(
27718            {
27719                 cls : ' x-signature-btn x-signature-'+id,
27720                 scope: editor, // was editor...
27721                 handler: this.reset,
27722                 clickEvent:'mousedown',
27723                 text: this.labels.clear
27724             },
27725             {
27726                  xtype : 'Fill',
27727                  xns: Roo.Toolbar
27728             }, 
27729             {
27730                 cls : '  x-signature-btn x-signature-'+id,
27731                 scope: editor, // was editor...
27732                 handler: this.confirmHandler,
27733                 clickEvent:'mousedown',
27734                 text: this.labels.confirm
27735             }
27736         );
27737     
27738     },
27739     //public
27740     /**
27741      * when user is clicked confirm then show this image.....
27742      * 
27743      * @return {String} Image Data URI
27744      */
27745     getImageDataURI : function(){
27746         var svg = this.svgEl.dom.parentNode.innerHTML;
27747         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27748         return src; 
27749     },
27750     /**
27751      * 
27752      * @return {Boolean} this.isConfirmed
27753      */
27754     getConfirmed : function(){
27755         return this.isConfirmed;
27756     },
27757     /**
27758      * 
27759      * @return {Number} this.width
27760      */
27761     getWidth : function(){
27762         return this.width;
27763     },
27764     /**
27765      * 
27766      * @return {Number} this.height
27767      */
27768     getHeight : function(){
27769         return this.height;
27770     },
27771     // private
27772     getSignature : function(){
27773         return this.signatureTmp;
27774     },
27775     // private
27776     reset : function(){
27777         this.signatureTmp = '';
27778         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27779         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27780         this.isConfirmed = false;
27781         Roo.form.Signature.superclass.reset.call(this);
27782     },
27783     setSignature : function(s){
27784         this.signatureTmp = s;
27785         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27786         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27787         this.setValue(s);
27788         this.isConfirmed = false;
27789         Roo.form.Signature.superclass.reset.call(this);
27790     }, 
27791     test : function(){
27792 //        Roo.log(this.signPanel.dom.contentWindow.up())
27793     },
27794     //private
27795     setConfirmed : function(){
27796         
27797         
27798         
27799 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27800     },
27801     // private
27802     confirmHandler : function(){
27803         if(!this.getSignature()){
27804             return;
27805         }
27806         
27807         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27808         this.setValue(this.getSignature());
27809         this.isConfirmed = true;
27810         
27811         this.fireEvent('confirm', this);
27812     },
27813     // private
27814     // Subclasses should provide the validation implementation by overriding this
27815     validateValue : function(value){
27816         if(this.allowBlank){
27817             return true;
27818         }
27819         
27820         if(this.isConfirmed){
27821             return true;
27822         }
27823         return false;
27824     }
27825 });/*
27826  * Based on:
27827  * Ext JS Library 1.1.1
27828  * Copyright(c) 2006-2007, Ext JS, LLC.
27829  *
27830  * Originally Released Under LGPL - original licence link has changed is not relivant.
27831  *
27832  * Fork - LGPL
27833  * <script type="text/javascript">
27834  */
27835  
27836
27837 /**
27838  * @class Roo.form.ComboBox
27839  * @extends Roo.form.TriggerField
27840  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27841  * @constructor
27842  * Create a new ComboBox.
27843  * @param {Object} config Configuration options
27844  */
27845 Roo.form.Select = function(config){
27846     Roo.form.Select.superclass.constructor.call(this, config);
27847      
27848 };
27849
27850 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27851     /**
27852      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27853      */
27854     /**
27855      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27856      * rendering into an Roo.Editor, defaults to false)
27857      */
27858     /**
27859      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27860      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27861      */
27862     /**
27863      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27864      */
27865     /**
27866      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27867      * the dropdown list (defaults to undefined, with no header element)
27868      */
27869
27870      /**
27871      * @cfg {String/Roo.Template} tpl The template to use to render the output
27872      */
27873      
27874     // private
27875     defaultAutoCreate : {tag: "select"  },
27876     /**
27877      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27878      */
27879     listWidth: undefined,
27880     /**
27881      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27882      * mode = 'remote' or 'text' if mode = 'local')
27883      */
27884     displayField: undefined,
27885     /**
27886      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27887      * mode = 'remote' or 'value' if mode = 'local'). 
27888      * Note: use of a valueField requires the user make a selection
27889      * in order for a value to be mapped.
27890      */
27891     valueField: undefined,
27892     
27893     
27894     /**
27895      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27896      * field's data value (defaults to the underlying DOM element's name)
27897      */
27898     hiddenName: undefined,
27899     /**
27900      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27901      */
27902     listClass: '',
27903     /**
27904      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27905      */
27906     selectedClass: 'x-combo-selected',
27907     /**
27908      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27909      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27910      * which displays a downward arrow icon).
27911      */
27912     triggerClass : 'x-form-arrow-trigger',
27913     /**
27914      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27915      */
27916     shadow:'sides',
27917     /**
27918      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27919      * anchor positions (defaults to 'tl-bl')
27920      */
27921     listAlign: 'tl-bl?',
27922     /**
27923      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27924      */
27925     maxHeight: 300,
27926     /**
27927      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27928      * query specified by the allQuery config option (defaults to 'query')
27929      */
27930     triggerAction: 'query',
27931     /**
27932      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27933      * (defaults to 4, does not apply if editable = false)
27934      */
27935     minChars : 4,
27936     /**
27937      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27938      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27939      */
27940     typeAhead: false,
27941     /**
27942      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27943      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27944      */
27945     queryDelay: 500,
27946     /**
27947      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27948      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27949      */
27950     pageSize: 0,
27951     /**
27952      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27953      * when editable = true (defaults to false)
27954      */
27955     selectOnFocus:false,
27956     /**
27957      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27958      */
27959     queryParam: 'query',
27960     /**
27961      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27962      * when mode = 'remote' (defaults to 'Loading...')
27963      */
27964     loadingText: 'Loading...',
27965     /**
27966      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27967      */
27968     resizable: false,
27969     /**
27970      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27971      */
27972     handleHeight : 8,
27973     /**
27974      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27975      * traditional select (defaults to true)
27976      */
27977     editable: true,
27978     /**
27979      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27980      */
27981     allQuery: '',
27982     /**
27983      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27984      */
27985     mode: 'remote',
27986     /**
27987      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27988      * listWidth has a higher value)
27989      */
27990     minListWidth : 70,
27991     /**
27992      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27993      * allow the user to set arbitrary text into the field (defaults to false)
27994      */
27995     forceSelection:false,
27996     /**
27997      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27998      * if typeAhead = true (defaults to 250)
27999      */
28000     typeAheadDelay : 250,
28001     /**
28002      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28003      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28004      */
28005     valueNotFoundText : undefined,
28006     
28007     /**
28008      * @cfg {String} defaultValue The value displayed after loading the store.
28009      */
28010     defaultValue: '',
28011     
28012     /**
28013      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28014      */
28015     blockFocus : false,
28016     
28017     /**
28018      * @cfg {Boolean} disableClear Disable showing of clear button.
28019      */
28020     disableClear : false,
28021     /**
28022      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28023      */
28024     alwaysQuery : false,
28025     
28026     //private
28027     addicon : false,
28028     editicon: false,
28029     
28030     // element that contains real text value.. (when hidden is used..)
28031      
28032     // private
28033     onRender : function(ct, position){
28034         Roo.form.Field.prototype.onRender.call(this, ct, position);
28035         
28036         if(this.store){
28037             this.store.on('beforeload', this.onBeforeLoad, this);
28038             this.store.on('load', this.onLoad, this);
28039             this.store.on('loadexception', this.onLoadException, this);
28040             this.store.load({});
28041         }
28042         
28043         
28044         
28045     },
28046
28047     // private
28048     initEvents : function(){
28049         //Roo.form.ComboBox.superclass.initEvents.call(this);
28050  
28051     },
28052
28053     onDestroy : function(){
28054        
28055         if(this.store){
28056             this.store.un('beforeload', this.onBeforeLoad, this);
28057             this.store.un('load', this.onLoad, this);
28058             this.store.un('loadexception', this.onLoadException, this);
28059         }
28060         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28061     },
28062
28063     // private
28064     fireKey : function(e){
28065         if(e.isNavKeyPress() && !this.list.isVisible()){
28066             this.fireEvent("specialkey", this, e);
28067         }
28068     },
28069
28070     // private
28071     onResize: function(w, h){
28072         
28073         return; 
28074     
28075         
28076     },
28077
28078     /**
28079      * Allow or prevent the user from directly editing the field text.  If false is passed,
28080      * the user will only be able to select from the items defined in the dropdown list.  This method
28081      * is the runtime equivalent of setting the 'editable' config option at config time.
28082      * @param {Boolean} value True to allow the user to directly edit the field text
28083      */
28084     setEditable : function(value){
28085          
28086     },
28087
28088     // private
28089     onBeforeLoad : function(){
28090         
28091         Roo.log("Select before load");
28092         return;
28093     
28094         this.innerList.update(this.loadingText ?
28095                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28096         //this.restrictHeight();
28097         this.selectedIndex = -1;
28098     },
28099
28100     // private
28101     onLoad : function(){
28102
28103     
28104         var dom = this.el.dom;
28105         dom.innerHTML = '';
28106          var od = dom.ownerDocument;
28107          
28108         if (this.emptyText) {
28109             var op = od.createElement('option');
28110             op.setAttribute('value', '');
28111             op.innerHTML = String.format('{0}', this.emptyText);
28112             dom.appendChild(op);
28113         }
28114         if(this.store.getCount() > 0){
28115            
28116             var vf = this.valueField;
28117             var df = this.displayField;
28118             this.store.data.each(function(r) {
28119                 // which colmsn to use... testing - cdoe / title..
28120                 var op = od.createElement('option');
28121                 op.setAttribute('value', r.data[vf]);
28122                 op.innerHTML = String.format('{0}', r.data[df]);
28123                 dom.appendChild(op);
28124             });
28125             if (typeof(this.defaultValue != 'undefined')) {
28126                 this.setValue(this.defaultValue);
28127             }
28128             
28129              
28130         }else{
28131             //this.onEmptyResults();
28132         }
28133         //this.el.focus();
28134     },
28135     // private
28136     onLoadException : function()
28137     {
28138         dom.innerHTML = '';
28139             
28140         Roo.log("Select on load exception");
28141         return;
28142     
28143         this.collapse();
28144         Roo.log(this.store.reader.jsonData);
28145         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28146             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28147         }
28148         
28149         
28150     },
28151     // private
28152     onTypeAhead : function(){
28153          
28154     },
28155
28156     // private
28157     onSelect : function(record, index){
28158         Roo.log('on select?');
28159         return;
28160         if(this.fireEvent('beforeselect', this, record, index) !== false){
28161             this.setFromData(index > -1 ? record.data : false);
28162             this.collapse();
28163             this.fireEvent('select', this, record, index);
28164         }
28165     },
28166
28167     /**
28168      * Returns the currently selected field value or empty string if no value is set.
28169      * @return {String} value The selected value
28170      */
28171     getValue : function(){
28172         var dom = this.el.dom;
28173         this.value = dom.options[dom.selectedIndex].value;
28174         return this.value;
28175         
28176     },
28177
28178     /**
28179      * Clears any text/value currently set in the field
28180      */
28181     clearValue : function(){
28182         this.value = '';
28183         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28184         
28185     },
28186
28187     /**
28188      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28189      * will be displayed in the field.  If the value does not match the data value of an existing item,
28190      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28191      * Otherwise the field will be blank (although the value will still be set).
28192      * @param {String} value The value to match
28193      */
28194     setValue : function(v){
28195         var d = this.el.dom;
28196         for (var i =0; i < d.options.length;i++) {
28197             if (v == d.options[i].value) {
28198                 d.selectedIndex = i;
28199                 this.value = v;
28200                 return;
28201             }
28202         }
28203         this.clearValue();
28204     },
28205     /**
28206      * @property {Object} the last set data for the element
28207      */
28208     
28209     lastData : false,
28210     /**
28211      * Sets the value of the field based on a object which is related to the record format for the store.
28212      * @param {Object} value the value to set as. or false on reset?
28213      */
28214     setFromData : function(o){
28215         Roo.log('setfrom data?');
28216          
28217         
28218         
28219     },
28220     // private
28221     reset : function(){
28222         this.clearValue();
28223     },
28224     // private
28225     findRecord : function(prop, value){
28226         
28227         return false;
28228     
28229         var record;
28230         if(this.store.getCount() > 0){
28231             this.store.each(function(r){
28232                 if(r.data[prop] == value){
28233                     record = r;
28234                     return false;
28235                 }
28236                 return true;
28237             });
28238         }
28239         return record;
28240     },
28241     
28242     getName: function()
28243     {
28244         // returns hidden if it's set..
28245         if (!this.rendered) {return ''};
28246         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28247         
28248     },
28249      
28250
28251     
28252
28253     // private
28254     onEmptyResults : function(){
28255         Roo.log('empty results');
28256         //this.collapse();
28257     },
28258
28259     /**
28260      * Returns true if the dropdown list is expanded, else false.
28261      */
28262     isExpanded : function(){
28263         return false;
28264     },
28265
28266     /**
28267      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28268      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28269      * @param {String} value The data value of the item to select
28270      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28271      * selected item if it is not currently in view (defaults to true)
28272      * @return {Boolean} True if the value matched an item in the list, else false
28273      */
28274     selectByValue : function(v, scrollIntoView){
28275         Roo.log('select By Value');
28276         return false;
28277     
28278         if(v !== undefined && v !== null){
28279             var r = this.findRecord(this.valueField || this.displayField, v);
28280             if(r){
28281                 this.select(this.store.indexOf(r), scrollIntoView);
28282                 return true;
28283             }
28284         }
28285         return false;
28286     },
28287
28288     /**
28289      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28290      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28291      * @param {Number} index The zero-based index of the list item to select
28292      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28293      * selected item if it is not currently in view (defaults to true)
28294      */
28295     select : function(index, scrollIntoView){
28296         Roo.log('select ');
28297         return  ;
28298         
28299         this.selectedIndex = index;
28300         this.view.select(index);
28301         if(scrollIntoView !== false){
28302             var el = this.view.getNode(index);
28303             if(el){
28304                 this.innerList.scrollChildIntoView(el, false);
28305             }
28306         }
28307     },
28308
28309       
28310
28311     // private
28312     validateBlur : function(){
28313         
28314         return;
28315         
28316     },
28317
28318     // private
28319     initQuery : function(){
28320         this.doQuery(this.getRawValue());
28321     },
28322
28323     // private
28324     doForce : function(){
28325         if(this.el.dom.value.length > 0){
28326             this.el.dom.value =
28327                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28328              
28329         }
28330     },
28331
28332     /**
28333      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28334      * query allowing the query action to be canceled if needed.
28335      * @param {String} query The SQL query to execute
28336      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28337      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28338      * saved in the current store (defaults to false)
28339      */
28340     doQuery : function(q, forceAll){
28341         
28342         Roo.log('doQuery?');
28343         if(q === undefined || q === null){
28344             q = '';
28345         }
28346         var qe = {
28347             query: q,
28348             forceAll: forceAll,
28349             combo: this,
28350             cancel:false
28351         };
28352         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28353             return false;
28354         }
28355         q = qe.query;
28356         forceAll = qe.forceAll;
28357         if(forceAll === true || (q.length >= this.minChars)){
28358             if(this.lastQuery != q || this.alwaysQuery){
28359                 this.lastQuery = q;
28360                 if(this.mode == 'local'){
28361                     this.selectedIndex = -1;
28362                     if(forceAll){
28363                         this.store.clearFilter();
28364                     }else{
28365                         this.store.filter(this.displayField, q);
28366                     }
28367                     this.onLoad();
28368                 }else{
28369                     this.store.baseParams[this.queryParam] = q;
28370                     this.store.load({
28371                         params: this.getParams(q)
28372                     });
28373                     this.expand();
28374                 }
28375             }else{
28376                 this.selectedIndex = -1;
28377                 this.onLoad();   
28378             }
28379         }
28380     },
28381
28382     // private
28383     getParams : function(q){
28384         var p = {};
28385         //p[this.queryParam] = q;
28386         if(this.pageSize){
28387             p.start = 0;
28388             p.limit = this.pageSize;
28389         }
28390         return p;
28391     },
28392
28393     /**
28394      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28395      */
28396     collapse : function(){
28397         
28398     },
28399
28400     // private
28401     collapseIf : function(e){
28402         
28403     },
28404
28405     /**
28406      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28407      */
28408     expand : function(){
28409         
28410     } ,
28411
28412     // private
28413      
28414
28415     /** 
28416     * @cfg {Boolean} grow 
28417     * @hide 
28418     */
28419     /** 
28420     * @cfg {Number} growMin 
28421     * @hide 
28422     */
28423     /** 
28424     * @cfg {Number} growMax 
28425     * @hide 
28426     */
28427     /**
28428      * @hide
28429      * @method autoSize
28430      */
28431     
28432     setWidth : function()
28433     {
28434         
28435     },
28436     getResizeEl : function(){
28437         return this.el;
28438     }
28439 });//<script type="text/javasscript">
28440  
28441
28442 /**
28443  * @class Roo.DDView
28444  * A DnD enabled version of Roo.View.
28445  * @param {Element/String} container The Element in which to create the View.
28446  * @param {String} tpl The template string used to create the markup for each element of the View
28447  * @param {Object} config The configuration properties. These include all the config options of
28448  * {@link Roo.View} plus some specific to this class.<br>
28449  * <p>
28450  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28451  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28452  * <p>
28453  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28454 .x-view-drag-insert-above {
28455         border-top:1px dotted #3366cc;
28456 }
28457 .x-view-drag-insert-below {
28458         border-bottom:1px dotted #3366cc;
28459 }
28460 </code></pre>
28461  * 
28462  */
28463  
28464 Roo.DDView = function(container, tpl, config) {
28465     Roo.DDView.superclass.constructor.apply(this, arguments);
28466     this.getEl().setStyle("outline", "0px none");
28467     this.getEl().unselectable();
28468     if (this.dragGroup) {
28469         this.setDraggable(this.dragGroup.split(","));
28470     }
28471     if (this.dropGroup) {
28472         this.setDroppable(this.dropGroup.split(","));
28473     }
28474     if (this.deletable) {
28475         this.setDeletable();
28476     }
28477     this.isDirtyFlag = false;
28478         this.addEvents({
28479                 "drop" : true
28480         });
28481 };
28482
28483 Roo.extend(Roo.DDView, Roo.View, {
28484 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28485 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28486 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28487 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28488
28489         isFormField: true,
28490
28491         reset: Roo.emptyFn,
28492         
28493         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28494
28495         validate: function() {
28496                 return true;
28497         },
28498         
28499         destroy: function() {
28500                 this.purgeListeners();
28501                 this.getEl.removeAllListeners();
28502                 this.getEl().remove();
28503                 if (this.dragZone) {
28504                         if (this.dragZone.destroy) {
28505                                 this.dragZone.destroy();
28506                         }
28507                 }
28508                 if (this.dropZone) {
28509                         if (this.dropZone.destroy) {
28510                                 this.dropZone.destroy();
28511                         }
28512                 }
28513         },
28514
28515 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28516         getName: function() {
28517                 return this.name;
28518         },
28519
28520 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28521         setValue: function(v) {
28522                 if (!this.store) {
28523                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28524                 }
28525                 var data = {};
28526                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28527                 this.store.proxy = new Roo.data.MemoryProxy(data);
28528                 this.store.load();
28529         },
28530
28531 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28532         getValue: function() {
28533                 var result = '(';
28534                 this.store.each(function(rec) {
28535                         result += rec.id + ',';
28536                 });
28537                 return result.substr(0, result.length - 1) + ')';
28538         },
28539         
28540         getIds: function() {
28541                 var i = 0, result = new Array(this.store.getCount());
28542                 this.store.each(function(rec) {
28543                         result[i++] = rec.id;
28544                 });
28545                 return result;
28546         },
28547         
28548         isDirty: function() {
28549                 return this.isDirtyFlag;
28550         },
28551
28552 /**
28553  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28554  *      whole Element becomes the target, and this causes the drop gesture to append.
28555  */
28556     getTargetFromEvent : function(e) {
28557                 var target = e.getTarget();
28558                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28559                 target = target.parentNode;
28560                 }
28561                 if (!target) {
28562                         target = this.el.dom.lastChild || this.el.dom;
28563                 }
28564                 return target;
28565     },
28566
28567 /**
28568  *      Create the drag data which consists of an object which has the property "ddel" as
28569  *      the drag proxy element. 
28570  */
28571     getDragData : function(e) {
28572         var target = this.findItemFromChild(e.getTarget());
28573                 if(target) {
28574                         this.handleSelection(e);
28575                         var selNodes = this.getSelectedNodes();
28576             var dragData = {
28577                 source: this,
28578                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28579                 nodes: selNodes,
28580                 records: []
28581                         };
28582                         var selectedIndices = this.getSelectedIndexes();
28583                         for (var i = 0; i < selectedIndices.length; i++) {
28584                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28585                         }
28586                         if (selNodes.length == 1) {
28587                                 dragData.ddel = target.cloneNode(true); // the div element
28588                         } else {
28589                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28590                                 div.className = 'multi-proxy';
28591                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28592                                         div.appendChild(selNodes[i].cloneNode(true));
28593                                 }
28594                                 dragData.ddel = div;
28595                         }
28596             //console.log(dragData)
28597             //console.log(dragData.ddel.innerHTML)
28598                         return dragData;
28599                 }
28600         //console.log('nodragData')
28601                 return false;
28602     },
28603     
28604 /**     Specify to which ddGroup items in this DDView may be dragged. */
28605     setDraggable: function(ddGroup) {
28606         if (ddGroup instanceof Array) {
28607                 Roo.each(ddGroup, this.setDraggable, this);
28608                 return;
28609         }
28610         if (this.dragZone) {
28611                 this.dragZone.addToGroup(ddGroup);
28612         } else {
28613                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28614                                 containerScroll: true,
28615                                 ddGroup: ddGroup 
28616
28617                         });
28618 //                      Draggability implies selection. DragZone's mousedown selects the element.
28619                         if (!this.multiSelect) { this.singleSelect = true; }
28620
28621 //                      Wire the DragZone's handlers up to methods in *this*
28622                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28623                 }
28624     },
28625
28626 /**     Specify from which ddGroup this DDView accepts drops. */
28627     setDroppable: function(ddGroup) {
28628         if (ddGroup instanceof Array) {
28629                 Roo.each(ddGroup, this.setDroppable, this);
28630                 return;
28631         }
28632         if (this.dropZone) {
28633                 this.dropZone.addToGroup(ddGroup);
28634         } else {
28635                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28636                                 containerScroll: true,
28637                                 ddGroup: ddGroup
28638                         });
28639
28640 //                      Wire the DropZone's handlers up to methods in *this*
28641                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28642                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28643                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28644                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28645                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28646                 }
28647     },
28648
28649 /**     Decide whether to drop above or below a View node. */
28650     getDropPoint : function(e, n, dd){
28651         if (n == this.el.dom) { return "above"; }
28652                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28653                 var c = t + (b - t) / 2;
28654                 var y = Roo.lib.Event.getPageY(e);
28655                 if(y <= c) {
28656                         return "above";
28657                 }else{
28658                         return "below";
28659                 }
28660     },
28661
28662     onNodeEnter : function(n, dd, e, data){
28663                 return false;
28664     },
28665     
28666     onNodeOver : function(n, dd, e, data){
28667                 var pt = this.getDropPoint(e, n, dd);
28668                 // set the insert point style on the target node
28669                 var dragElClass = this.dropNotAllowed;
28670                 if (pt) {
28671                         var targetElClass;
28672                         if (pt == "above"){
28673                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28674                                 targetElClass = "x-view-drag-insert-above";
28675                         } else {
28676                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28677                                 targetElClass = "x-view-drag-insert-below";
28678                         }
28679                         if (this.lastInsertClass != targetElClass){
28680                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28681                                 this.lastInsertClass = targetElClass;
28682                         }
28683                 }
28684                 return dragElClass;
28685         },
28686
28687     onNodeOut : function(n, dd, e, data){
28688                 this.removeDropIndicators(n);
28689     },
28690
28691     onNodeDrop : function(n, dd, e, data){
28692         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28693                 return false;
28694         }
28695         var pt = this.getDropPoint(e, n, dd);
28696                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28697                 if (pt == "below") { insertAt++; }
28698                 for (var i = 0; i < data.records.length; i++) {
28699                         var r = data.records[i];
28700                         var dup = this.store.getById(r.id);
28701                         if (dup && (dd != this.dragZone)) {
28702                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28703                         } else {
28704                                 if (data.copy) {
28705                                         this.store.insert(insertAt++, r.copy());
28706                                 } else {
28707                                         data.source.isDirtyFlag = true;
28708                                         r.store.remove(r);
28709                                         this.store.insert(insertAt++, r);
28710                                 }
28711                                 this.isDirtyFlag = true;
28712                         }
28713                 }
28714                 this.dragZone.cachedTarget = null;
28715                 return true;
28716     },
28717
28718     removeDropIndicators : function(n){
28719                 if(n){
28720                         Roo.fly(n).removeClass([
28721                                 "x-view-drag-insert-above",
28722                                 "x-view-drag-insert-below"]);
28723                         this.lastInsertClass = "_noclass";
28724                 }
28725     },
28726
28727 /**
28728  *      Utility method. Add a delete option to the DDView's context menu.
28729  *      @param {String} imageUrl The URL of the "delete" icon image.
28730  */
28731         setDeletable: function(imageUrl) {
28732                 if (!this.singleSelect && !this.multiSelect) {
28733                         this.singleSelect = true;
28734                 }
28735                 var c = this.getContextMenu();
28736                 this.contextMenu.on("itemclick", function(item) {
28737                         switch (item.id) {
28738                                 case "delete":
28739                                         this.remove(this.getSelectedIndexes());
28740                                         break;
28741                         }
28742                 }, this);
28743                 this.contextMenu.add({
28744                         icon: imageUrl,
28745                         id: "delete",
28746                         text: 'Delete'
28747                 });
28748         },
28749         
28750 /**     Return the context menu for this DDView. */
28751         getContextMenu: function() {
28752                 if (!this.contextMenu) {
28753 //                      Create the View's context menu
28754                         this.contextMenu = new Roo.menu.Menu({
28755                                 id: this.id + "-contextmenu"
28756                         });
28757                         this.el.on("contextmenu", this.showContextMenu, this);
28758                 }
28759                 return this.contextMenu;
28760         },
28761         
28762         disableContextMenu: function() {
28763                 if (this.contextMenu) {
28764                         this.el.un("contextmenu", this.showContextMenu, this);
28765                 }
28766         },
28767
28768         showContextMenu: function(e, item) {
28769         item = this.findItemFromChild(e.getTarget());
28770                 if (item) {
28771                         e.stopEvent();
28772                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28773                         this.contextMenu.showAt(e.getXY());
28774             }
28775     },
28776
28777 /**
28778  *      Remove {@link Roo.data.Record}s at the specified indices.
28779  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28780  */
28781     remove: function(selectedIndices) {
28782                 selectedIndices = [].concat(selectedIndices);
28783                 for (var i = 0; i < selectedIndices.length; i++) {
28784                         var rec = this.store.getAt(selectedIndices[i]);
28785                         this.store.remove(rec);
28786                 }
28787     },
28788
28789 /**
28790  *      Double click fires the event, but also, if this is draggable, and there is only one other
28791  *      related DropZone, it transfers the selected node.
28792  */
28793     onDblClick : function(e){
28794         var item = this.findItemFromChild(e.getTarget());
28795         if(item){
28796             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28797                 return false;
28798             }
28799             if (this.dragGroup) {
28800                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28801                     while (targets.indexOf(this.dropZone) > -1) {
28802                             targets.remove(this.dropZone);
28803                                 }
28804                     if (targets.length == 1) {
28805                                         this.dragZone.cachedTarget = null;
28806                         var el = Roo.get(targets[0].getEl());
28807                         var box = el.getBox(true);
28808                         targets[0].onNodeDrop(el.dom, {
28809                                 target: el.dom,
28810                                 xy: [box.x, box.y + box.height - 1]
28811                         }, null, this.getDragData(e));
28812                     }
28813                 }
28814         }
28815     },
28816     
28817     handleSelection: function(e) {
28818                 this.dragZone.cachedTarget = null;
28819         var item = this.findItemFromChild(e.getTarget());
28820         if (!item) {
28821                 this.clearSelections(true);
28822                 return;
28823         }
28824                 if (item && (this.multiSelect || this.singleSelect)){
28825                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28826                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28827                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28828                                 this.unselect(item);
28829                         } else {
28830                                 this.select(item, this.multiSelect && e.ctrlKey);
28831                                 this.lastSelection = item;
28832                         }
28833                 }
28834     },
28835
28836     onItemClick : function(item, index, e){
28837                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28838                         return false;
28839                 }
28840                 return true;
28841     },
28842
28843     unselect : function(nodeInfo, suppressEvent){
28844                 var node = this.getNode(nodeInfo);
28845                 if(node && this.isSelected(node)){
28846                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28847                                 Roo.fly(node).removeClass(this.selectedClass);
28848                                 this.selections.remove(node);
28849                                 if(!suppressEvent){
28850                                         this.fireEvent("selectionchange", this, this.selections);
28851                                 }
28852                         }
28853                 }
28854     }
28855 });
28856 /*
28857  * Based on:
28858  * Ext JS Library 1.1.1
28859  * Copyright(c) 2006-2007, Ext JS, LLC.
28860  *
28861  * Originally Released Under LGPL - original licence link has changed is not relivant.
28862  *
28863  * Fork - LGPL
28864  * <script type="text/javascript">
28865  */
28866  
28867 /**
28868  * @class Roo.LayoutManager
28869  * @extends Roo.util.Observable
28870  * Base class for layout managers.
28871  */
28872 Roo.LayoutManager = function(container, config){
28873     Roo.LayoutManager.superclass.constructor.call(this);
28874     this.el = Roo.get(container);
28875     // ie scrollbar fix
28876     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28877         document.body.scroll = "no";
28878     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28879         this.el.position('relative');
28880     }
28881     this.id = this.el.id;
28882     this.el.addClass("x-layout-container");
28883     /** false to disable window resize monitoring @type Boolean */
28884     this.monitorWindowResize = true;
28885     this.regions = {};
28886     this.addEvents({
28887         /**
28888          * @event layout
28889          * Fires when a layout is performed. 
28890          * @param {Roo.LayoutManager} this
28891          */
28892         "layout" : true,
28893         /**
28894          * @event regionresized
28895          * Fires when the user resizes a region. 
28896          * @param {Roo.LayoutRegion} region The resized region
28897          * @param {Number} newSize The new size (width for east/west, height for north/south)
28898          */
28899         "regionresized" : true,
28900         /**
28901          * @event regioncollapsed
28902          * Fires when a region is collapsed. 
28903          * @param {Roo.LayoutRegion} region The collapsed region
28904          */
28905         "regioncollapsed" : true,
28906         /**
28907          * @event regionexpanded
28908          * Fires when a region is expanded.  
28909          * @param {Roo.LayoutRegion} region The expanded region
28910          */
28911         "regionexpanded" : true
28912     });
28913     this.updating = false;
28914     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28915 };
28916
28917 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28918     /**
28919      * Returns true if this layout is currently being updated
28920      * @return {Boolean}
28921      */
28922     isUpdating : function(){
28923         return this.updating; 
28924     },
28925     
28926     /**
28927      * Suspend the LayoutManager from doing auto-layouts while
28928      * making multiple add or remove calls
28929      */
28930     beginUpdate : function(){
28931         this.updating = true;    
28932     },
28933     
28934     /**
28935      * Restore auto-layouts and optionally disable the manager from performing a layout
28936      * @param {Boolean} noLayout true to disable a layout update 
28937      */
28938     endUpdate : function(noLayout){
28939         this.updating = false;
28940         if(!noLayout){
28941             this.layout();
28942         }    
28943     },
28944     
28945     layout: function(){
28946         
28947     },
28948     
28949     onRegionResized : function(region, newSize){
28950         this.fireEvent("regionresized", region, newSize);
28951         this.layout();
28952     },
28953     
28954     onRegionCollapsed : function(region){
28955         this.fireEvent("regioncollapsed", region);
28956     },
28957     
28958     onRegionExpanded : function(region){
28959         this.fireEvent("regionexpanded", region);
28960     },
28961         
28962     /**
28963      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28964      * performs box-model adjustments.
28965      * @return {Object} The size as an object {width: (the width), height: (the height)}
28966      */
28967     getViewSize : function(){
28968         var size;
28969         if(this.el.dom != document.body){
28970             size = this.el.getSize();
28971         }else{
28972             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28973         }
28974         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28975         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28976         return size;
28977     },
28978     
28979     /**
28980      * Returns the Element this layout is bound to.
28981      * @return {Roo.Element}
28982      */
28983     getEl : function(){
28984         return this.el;
28985     },
28986     
28987     /**
28988      * Returns the specified region.
28989      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28990      * @return {Roo.LayoutRegion}
28991      */
28992     getRegion : function(target){
28993         return this.regions[target.toLowerCase()];
28994     },
28995     
28996     onWindowResize : function(){
28997         if(this.monitorWindowResize){
28998             this.layout();
28999         }
29000     }
29001 });/*
29002  * Based on:
29003  * Ext JS Library 1.1.1
29004  * Copyright(c) 2006-2007, Ext JS, LLC.
29005  *
29006  * Originally Released Under LGPL - original licence link has changed is not relivant.
29007  *
29008  * Fork - LGPL
29009  * <script type="text/javascript">
29010  */
29011 /**
29012  * @class Roo.BorderLayout
29013  * @extends Roo.LayoutManager
29014  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29015  * please see: <br><br>
29016  * <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>
29017  * <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>
29018  * Example:
29019  <pre><code>
29020  var layout = new Roo.BorderLayout(document.body, {
29021     north: {
29022         initialSize: 25,
29023         titlebar: false
29024     },
29025     west: {
29026         split:true,
29027         initialSize: 200,
29028         minSize: 175,
29029         maxSize: 400,
29030         titlebar: true,
29031         collapsible: true
29032     },
29033     east: {
29034         split:true,
29035         initialSize: 202,
29036         minSize: 175,
29037         maxSize: 400,
29038         titlebar: true,
29039         collapsible: true
29040     },
29041     south: {
29042         split:true,
29043         initialSize: 100,
29044         minSize: 100,
29045         maxSize: 200,
29046         titlebar: true,
29047         collapsible: true
29048     },
29049     center: {
29050         titlebar: true,
29051         autoScroll:true,
29052         resizeTabs: true,
29053         minTabWidth: 50,
29054         preferredTabWidth: 150
29055     }
29056 });
29057
29058 // shorthand
29059 var CP = Roo.ContentPanel;
29060
29061 layout.beginUpdate();
29062 layout.add("north", new CP("north", "North"));
29063 layout.add("south", new CP("south", {title: "South", closable: true}));
29064 layout.add("west", new CP("west", {title: "West"}));
29065 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29066 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29067 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29068 layout.getRegion("center").showPanel("center1");
29069 layout.endUpdate();
29070 </code></pre>
29071
29072 <b>The container the layout is rendered into can be either the body element or any other element.
29073 If it is not the body element, the container needs to either be an absolute positioned element,
29074 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29075 the container size if it is not the body element.</b>
29076
29077 * @constructor
29078 * Create a new BorderLayout
29079 * @param {String/HTMLElement/Element} container The container this layout is bound to
29080 * @param {Object} config Configuration options
29081  */
29082 Roo.BorderLayout = function(container, config){
29083     config = config || {};
29084     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29085     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29086     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29087         var target = this.factory.validRegions[i];
29088         if(config[target]){
29089             this.addRegion(target, config[target]);
29090         }
29091     }
29092 };
29093
29094 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29095     /**
29096      * Creates and adds a new region if it doesn't already exist.
29097      * @param {String} target The target region key (north, south, east, west or center).
29098      * @param {Object} config The regions config object
29099      * @return {BorderLayoutRegion} The new region
29100      */
29101     addRegion : function(target, config){
29102         if(!this.regions[target]){
29103             var r = this.factory.create(target, this, config);
29104             this.bindRegion(target, r);
29105         }
29106         return this.regions[target];
29107     },
29108
29109     // private (kinda)
29110     bindRegion : function(name, r){
29111         this.regions[name] = r;
29112         r.on("visibilitychange", this.layout, this);
29113         r.on("paneladded", this.layout, this);
29114         r.on("panelremoved", this.layout, this);
29115         r.on("invalidated", this.layout, this);
29116         r.on("resized", this.onRegionResized, this);
29117         r.on("collapsed", this.onRegionCollapsed, this);
29118         r.on("expanded", this.onRegionExpanded, this);
29119     },
29120
29121     /**
29122      * Performs a layout update.
29123      */
29124     layout : function(){
29125         if(this.updating) {
29126             return;
29127         }
29128         var size = this.getViewSize();
29129         var w = size.width;
29130         var h = size.height;
29131         var centerW = w;
29132         var centerH = h;
29133         var centerY = 0;
29134         var centerX = 0;
29135         //var x = 0, y = 0;
29136
29137         var rs = this.regions;
29138         var north = rs["north"];
29139         var south = rs["south"]; 
29140         var west = rs["west"];
29141         var east = rs["east"];
29142         var center = rs["center"];
29143         //if(this.hideOnLayout){ // not supported anymore
29144             //c.el.setStyle("display", "none");
29145         //}
29146         if(north && north.isVisible()){
29147             var b = north.getBox();
29148             var m = north.getMargins();
29149             b.width = w - (m.left+m.right);
29150             b.x = m.left;
29151             b.y = m.top;
29152             centerY = b.height + b.y + m.bottom;
29153             centerH -= centerY;
29154             north.updateBox(this.safeBox(b));
29155         }
29156         if(south && south.isVisible()){
29157             var b = south.getBox();
29158             var m = south.getMargins();
29159             b.width = w - (m.left+m.right);
29160             b.x = m.left;
29161             var totalHeight = (b.height + m.top + m.bottom);
29162             b.y = h - totalHeight + m.top;
29163             centerH -= totalHeight;
29164             south.updateBox(this.safeBox(b));
29165         }
29166         if(west && west.isVisible()){
29167             var b = west.getBox();
29168             var m = west.getMargins();
29169             b.height = centerH - (m.top+m.bottom);
29170             b.x = m.left;
29171             b.y = centerY + m.top;
29172             var totalWidth = (b.width + m.left + m.right);
29173             centerX += totalWidth;
29174             centerW -= totalWidth;
29175             west.updateBox(this.safeBox(b));
29176         }
29177         if(east && east.isVisible()){
29178             var b = east.getBox();
29179             var m = east.getMargins();
29180             b.height = centerH - (m.top+m.bottom);
29181             var totalWidth = (b.width + m.left + m.right);
29182             b.x = w - totalWidth + m.left;
29183             b.y = centerY + m.top;
29184             centerW -= totalWidth;
29185             east.updateBox(this.safeBox(b));
29186         }
29187         if(center){
29188             var m = center.getMargins();
29189             var centerBox = {
29190                 x: centerX + m.left,
29191                 y: centerY + m.top,
29192                 width: centerW - (m.left+m.right),
29193                 height: centerH - (m.top+m.bottom)
29194             };
29195             //if(this.hideOnLayout){
29196                 //center.el.setStyle("display", "block");
29197             //}
29198             center.updateBox(this.safeBox(centerBox));
29199         }
29200         this.el.repaint();
29201         this.fireEvent("layout", this);
29202     },
29203
29204     // private
29205     safeBox : function(box){
29206         box.width = Math.max(0, box.width);
29207         box.height = Math.max(0, box.height);
29208         return box;
29209     },
29210
29211     /**
29212      * Adds a ContentPanel (or subclass) to this layout.
29213      * @param {String} target The target region key (north, south, east, west or center).
29214      * @param {Roo.ContentPanel} panel The panel to add
29215      * @return {Roo.ContentPanel} The added panel
29216      */
29217     add : function(target, panel){
29218          
29219         target = target.toLowerCase();
29220         return this.regions[target].add(panel);
29221     },
29222
29223     /**
29224      * Remove a ContentPanel (or subclass) to this layout.
29225      * @param {String} target The target region key (north, south, east, west or center).
29226      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29227      * @return {Roo.ContentPanel} The removed panel
29228      */
29229     remove : function(target, panel){
29230         target = target.toLowerCase();
29231         return this.regions[target].remove(panel);
29232     },
29233
29234     /**
29235      * Searches all regions for a panel with the specified id
29236      * @param {String} panelId
29237      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29238      */
29239     findPanel : function(panelId){
29240         var rs = this.regions;
29241         for(var target in rs){
29242             if(typeof rs[target] != "function"){
29243                 var p = rs[target].getPanel(panelId);
29244                 if(p){
29245                     return p;
29246                 }
29247             }
29248         }
29249         return null;
29250     },
29251
29252     /**
29253      * Searches all regions for a panel with the specified id and activates (shows) it.
29254      * @param {String/ContentPanel} panelId The panels id or the panel itself
29255      * @return {Roo.ContentPanel} The shown panel or null
29256      */
29257     showPanel : function(panelId) {
29258       var rs = this.regions;
29259       for(var target in rs){
29260          var r = rs[target];
29261          if(typeof r != "function"){
29262             if(r.hasPanel(panelId)){
29263                return r.showPanel(panelId);
29264             }
29265          }
29266       }
29267       return null;
29268    },
29269
29270    /**
29271      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29272      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29273      */
29274     restoreState : function(provider){
29275         if(!provider){
29276             provider = Roo.state.Manager;
29277         }
29278         var sm = new Roo.LayoutStateManager();
29279         sm.init(this, provider);
29280     },
29281
29282     /**
29283      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29284      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29285      * a valid ContentPanel config object.  Example:
29286      * <pre><code>
29287 // Create the main layout
29288 var layout = new Roo.BorderLayout('main-ct', {
29289     west: {
29290         split:true,
29291         minSize: 175,
29292         titlebar: true
29293     },
29294     center: {
29295         title:'Components'
29296     }
29297 }, 'main-ct');
29298
29299 // Create and add multiple ContentPanels at once via configs
29300 layout.batchAdd({
29301    west: {
29302        id: 'source-files',
29303        autoCreate:true,
29304        title:'Ext Source Files',
29305        autoScroll:true,
29306        fitToFrame:true
29307    },
29308    center : {
29309        el: cview,
29310        autoScroll:true,
29311        fitToFrame:true,
29312        toolbar: tb,
29313        resizeEl:'cbody'
29314    }
29315 });
29316 </code></pre>
29317      * @param {Object} regions An object containing ContentPanel configs by region name
29318      */
29319     batchAdd : function(regions){
29320         this.beginUpdate();
29321         for(var rname in regions){
29322             var lr = this.regions[rname];
29323             if(lr){
29324                 this.addTypedPanels(lr, regions[rname]);
29325             }
29326         }
29327         this.endUpdate();
29328     },
29329
29330     // private
29331     addTypedPanels : function(lr, ps){
29332         if(typeof ps == 'string'){
29333             lr.add(new Roo.ContentPanel(ps));
29334         }
29335         else if(ps instanceof Array){
29336             for(var i =0, len = ps.length; i < len; i++){
29337                 this.addTypedPanels(lr, ps[i]);
29338             }
29339         }
29340         else if(!ps.events){ // raw config?
29341             var el = ps.el;
29342             delete ps.el; // prevent conflict
29343             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29344         }
29345         else {  // panel object assumed!
29346             lr.add(ps);
29347         }
29348     },
29349     /**
29350      * Adds a xtype elements to the layout.
29351      * <pre><code>
29352
29353 layout.addxtype({
29354        xtype : 'ContentPanel',
29355        region: 'west',
29356        items: [ .... ]
29357    }
29358 );
29359
29360 layout.addxtype({
29361         xtype : 'NestedLayoutPanel',
29362         region: 'west',
29363         layout: {
29364            center: { },
29365            west: { }   
29366         },
29367         items : [ ... list of content panels or nested layout panels.. ]
29368    }
29369 );
29370 </code></pre>
29371      * @param {Object} cfg Xtype definition of item to add.
29372      */
29373     addxtype : function(cfg)
29374     {
29375         // basically accepts a pannel...
29376         // can accept a layout region..!?!?
29377         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29378         
29379         if (!cfg.xtype.match(/Panel$/)) {
29380             return false;
29381         }
29382         var ret = false;
29383         
29384         if (typeof(cfg.region) == 'undefined') {
29385             Roo.log("Failed to add Panel, region was not set");
29386             Roo.log(cfg);
29387             return false;
29388         }
29389         var region = cfg.region;
29390         delete cfg.region;
29391         
29392           
29393         var xitems = [];
29394         if (cfg.items) {
29395             xitems = cfg.items;
29396             delete cfg.items;
29397         }
29398         var nb = false;
29399         
29400         switch(cfg.xtype) 
29401         {
29402             case 'ContentPanel':  // ContentPanel (el, cfg)
29403             case 'ScrollPanel':  // ContentPanel (el, cfg)
29404             case 'ViewPanel': 
29405                 if(cfg.autoCreate) {
29406                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29407                 } else {
29408                     var el = this.el.createChild();
29409                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29410                 }
29411                 
29412                 this.add(region, ret);
29413                 break;
29414             
29415             
29416             case 'TreePanel': // our new panel!
29417                 cfg.el = this.el.createChild();
29418                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29419                 this.add(region, ret);
29420                 break;
29421             
29422             case 'NestedLayoutPanel': 
29423                 // create a new Layout (which is  a Border Layout...
29424                 var el = this.el.createChild();
29425                 var clayout = cfg.layout;
29426                 delete cfg.layout;
29427                 clayout.items   = clayout.items  || [];
29428                 // replace this exitems with the clayout ones..
29429                 xitems = clayout.items;
29430                  
29431                 
29432                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29433                     cfg.background = false;
29434                 }
29435                 var layout = new Roo.BorderLayout(el, clayout);
29436                 
29437                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29438                 //console.log('adding nested layout panel '  + cfg.toSource());
29439                 this.add(region, ret);
29440                 nb = {}; /// find first...
29441                 break;
29442                 
29443             case 'GridPanel': 
29444             
29445                 // needs grid and region
29446                 
29447                 //var el = this.getRegion(region).el.createChild();
29448                 var el = this.el.createChild();
29449                 // create the grid first...
29450                 
29451                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29452                 delete cfg.grid;
29453                 if (region == 'center' && this.active ) {
29454                     cfg.background = false;
29455                 }
29456                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29457                 
29458                 this.add(region, ret);
29459                 if (cfg.background) {
29460                     ret.on('activate', function(gp) {
29461                         if (!gp.grid.rendered) {
29462                             gp.grid.render();
29463                         }
29464                     });
29465                 } else {
29466                     grid.render();
29467                 }
29468                 break;
29469            
29470            
29471            
29472                 
29473                 
29474                 
29475             default:
29476                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29477                     
29478                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29479                     this.add(region, ret);
29480                 } else {
29481                 
29482                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29483                     return null;
29484                 }
29485                 
29486              // GridPanel (grid, cfg)
29487             
29488         }
29489         this.beginUpdate();
29490         // add children..
29491         var region = '';
29492         var abn = {};
29493         Roo.each(xitems, function(i)  {
29494             region = nb && i.region ? i.region : false;
29495             
29496             var add = ret.addxtype(i);
29497            
29498             if (region) {
29499                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29500                 if (!i.background) {
29501                     abn[region] = nb[region] ;
29502                 }
29503             }
29504             
29505         });
29506         this.endUpdate();
29507
29508         // make the last non-background panel active..
29509         //if (nb) { Roo.log(abn); }
29510         if (nb) {
29511             
29512             for(var r in abn) {
29513                 region = this.getRegion(r);
29514                 if (region) {
29515                     // tried using nb[r], but it does not work..
29516                      
29517                     region.showPanel(abn[r]);
29518                    
29519                 }
29520             }
29521         }
29522         return ret;
29523         
29524     }
29525 });
29526
29527 /**
29528  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29529  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29530  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29531  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29532  * <pre><code>
29533 // shorthand
29534 var CP = Roo.ContentPanel;
29535
29536 var layout = Roo.BorderLayout.create({
29537     north: {
29538         initialSize: 25,
29539         titlebar: false,
29540         panels: [new CP("north", "North")]
29541     },
29542     west: {
29543         split:true,
29544         initialSize: 200,
29545         minSize: 175,
29546         maxSize: 400,
29547         titlebar: true,
29548         collapsible: true,
29549         panels: [new CP("west", {title: "West"})]
29550     },
29551     east: {
29552         split:true,
29553         initialSize: 202,
29554         minSize: 175,
29555         maxSize: 400,
29556         titlebar: true,
29557         collapsible: true,
29558         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29559     },
29560     south: {
29561         split:true,
29562         initialSize: 100,
29563         minSize: 100,
29564         maxSize: 200,
29565         titlebar: true,
29566         collapsible: true,
29567         panels: [new CP("south", {title: "South", closable: true})]
29568     },
29569     center: {
29570         titlebar: true,
29571         autoScroll:true,
29572         resizeTabs: true,
29573         minTabWidth: 50,
29574         preferredTabWidth: 150,
29575         panels: [
29576             new CP("center1", {title: "Close Me", closable: true}),
29577             new CP("center2", {title: "Center Panel", closable: false})
29578         ]
29579     }
29580 }, document.body);
29581
29582 layout.getRegion("center").showPanel("center1");
29583 </code></pre>
29584  * @param config
29585  * @param targetEl
29586  */
29587 Roo.BorderLayout.create = function(config, targetEl){
29588     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29589     layout.beginUpdate();
29590     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29591     for(var j = 0, jlen = regions.length; j < jlen; j++){
29592         var lr = regions[j];
29593         if(layout.regions[lr] && config[lr].panels){
29594             var r = layout.regions[lr];
29595             var ps = config[lr].panels;
29596             layout.addTypedPanels(r, ps);
29597         }
29598     }
29599     layout.endUpdate();
29600     return layout;
29601 };
29602
29603 // private
29604 Roo.BorderLayout.RegionFactory = {
29605     // private
29606     validRegions : ["north","south","east","west","center"],
29607
29608     // private
29609     create : function(target, mgr, config){
29610         target = target.toLowerCase();
29611         if(config.lightweight || config.basic){
29612             return new Roo.BasicLayoutRegion(mgr, config, target);
29613         }
29614         switch(target){
29615             case "north":
29616                 return new Roo.NorthLayoutRegion(mgr, config);
29617             case "south":
29618                 return new Roo.SouthLayoutRegion(mgr, config);
29619             case "east":
29620                 return new Roo.EastLayoutRegion(mgr, config);
29621             case "west":
29622                 return new Roo.WestLayoutRegion(mgr, config);
29623             case "center":
29624                 return new Roo.CenterLayoutRegion(mgr, config);
29625         }
29626         throw 'Layout region "'+target+'" not supported.';
29627     }
29628 };/*
29629  * Based on:
29630  * Ext JS Library 1.1.1
29631  * Copyright(c) 2006-2007, Ext JS, LLC.
29632  *
29633  * Originally Released Under LGPL - original licence link has changed is not relivant.
29634  *
29635  * Fork - LGPL
29636  * <script type="text/javascript">
29637  */
29638  
29639 /**
29640  * @class Roo.BasicLayoutRegion
29641  * @extends Roo.util.Observable
29642  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29643  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29644  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29645  */
29646 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29647     this.mgr = mgr;
29648     this.position  = pos;
29649     this.events = {
29650         /**
29651          * @scope Roo.BasicLayoutRegion
29652          */
29653         
29654         /**
29655          * @event beforeremove
29656          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29657          * @param {Roo.LayoutRegion} this
29658          * @param {Roo.ContentPanel} panel The panel
29659          * @param {Object} e The cancel event object
29660          */
29661         "beforeremove" : true,
29662         /**
29663          * @event invalidated
29664          * Fires when the layout for this region is changed.
29665          * @param {Roo.LayoutRegion} this
29666          */
29667         "invalidated" : true,
29668         /**
29669          * @event visibilitychange
29670          * Fires when this region is shown or hidden 
29671          * @param {Roo.LayoutRegion} this
29672          * @param {Boolean} visibility true or false
29673          */
29674         "visibilitychange" : true,
29675         /**
29676          * @event paneladded
29677          * Fires when a panel is added. 
29678          * @param {Roo.LayoutRegion} this
29679          * @param {Roo.ContentPanel} panel The panel
29680          */
29681         "paneladded" : true,
29682         /**
29683          * @event panelremoved
29684          * Fires when a panel is removed. 
29685          * @param {Roo.LayoutRegion} this
29686          * @param {Roo.ContentPanel} panel The panel
29687          */
29688         "panelremoved" : true,
29689         /**
29690          * @event beforecollapse
29691          * Fires when this region before collapse.
29692          * @param {Roo.LayoutRegion} this
29693          */
29694         "beforecollapse" : true,
29695         /**
29696          * @event collapsed
29697          * Fires when this region is collapsed.
29698          * @param {Roo.LayoutRegion} this
29699          */
29700         "collapsed" : true,
29701         /**
29702          * @event expanded
29703          * Fires when this region is expanded.
29704          * @param {Roo.LayoutRegion} this
29705          */
29706         "expanded" : true,
29707         /**
29708          * @event slideshow
29709          * Fires when this region is slid into view.
29710          * @param {Roo.LayoutRegion} this
29711          */
29712         "slideshow" : true,
29713         /**
29714          * @event slidehide
29715          * Fires when this region slides out of view. 
29716          * @param {Roo.LayoutRegion} this
29717          */
29718         "slidehide" : true,
29719         /**
29720          * @event panelactivated
29721          * Fires when a panel is activated. 
29722          * @param {Roo.LayoutRegion} this
29723          * @param {Roo.ContentPanel} panel The activated panel
29724          */
29725         "panelactivated" : true,
29726         /**
29727          * @event resized
29728          * Fires when the user resizes this region. 
29729          * @param {Roo.LayoutRegion} this
29730          * @param {Number} newSize The new size (width for east/west, height for north/south)
29731          */
29732         "resized" : true
29733     };
29734     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29735     this.panels = new Roo.util.MixedCollection();
29736     this.panels.getKey = this.getPanelId.createDelegate(this);
29737     this.box = null;
29738     this.activePanel = null;
29739     // ensure listeners are added...
29740     
29741     if (config.listeners || config.events) {
29742         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29743             listeners : config.listeners || {},
29744             events : config.events || {}
29745         });
29746     }
29747     
29748     if(skipConfig !== true){
29749         this.applyConfig(config);
29750     }
29751 };
29752
29753 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29754     getPanelId : function(p){
29755         return p.getId();
29756     },
29757     
29758     applyConfig : function(config){
29759         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29760         this.config = config;
29761         
29762     },
29763     
29764     /**
29765      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29766      * the width, for horizontal (north, south) the height.
29767      * @param {Number} newSize The new width or height
29768      */
29769     resizeTo : function(newSize){
29770         var el = this.el ? this.el :
29771                  (this.activePanel ? this.activePanel.getEl() : null);
29772         if(el){
29773             switch(this.position){
29774                 case "east":
29775                 case "west":
29776                     el.setWidth(newSize);
29777                     this.fireEvent("resized", this, newSize);
29778                 break;
29779                 case "north":
29780                 case "south":
29781                     el.setHeight(newSize);
29782                     this.fireEvent("resized", this, newSize);
29783                 break;                
29784             }
29785         }
29786     },
29787     
29788     getBox : function(){
29789         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29790     },
29791     
29792     getMargins : function(){
29793         return this.margins;
29794     },
29795     
29796     updateBox : function(box){
29797         this.box = box;
29798         var el = this.activePanel.getEl();
29799         el.dom.style.left = box.x + "px";
29800         el.dom.style.top = box.y + "px";
29801         this.activePanel.setSize(box.width, box.height);
29802     },
29803     
29804     /**
29805      * Returns the container element for this region.
29806      * @return {Roo.Element}
29807      */
29808     getEl : function(){
29809         return this.activePanel;
29810     },
29811     
29812     /**
29813      * Returns true if this region is currently visible.
29814      * @return {Boolean}
29815      */
29816     isVisible : function(){
29817         return this.activePanel ? true : false;
29818     },
29819     
29820     setActivePanel : function(panel){
29821         panel = this.getPanel(panel);
29822         if(this.activePanel && this.activePanel != panel){
29823             this.activePanel.setActiveState(false);
29824             this.activePanel.getEl().setLeftTop(-10000,-10000);
29825         }
29826         this.activePanel = panel;
29827         panel.setActiveState(true);
29828         if(this.box){
29829             panel.setSize(this.box.width, this.box.height);
29830         }
29831         this.fireEvent("panelactivated", this, panel);
29832         this.fireEvent("invalidated");
29833     },
29834     
29835     /**
29836      * Show the specified panel.
29837      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29838      * @return {Roo.ContentPanel} The shown panel or null
29839      */
29840     showPanel : function(panel){
29841         if(panel = this.getPanel(panel)){
29842             this.setActivePanel(panel);
29843         }
29844         return panel;
29845     },
29846     
29847     /**
29848      * Get the active panel for this region.
29849      * @return {Roo.ContentPanel} The active panel or null
29850      */
29851     getActivePanel : function(){
29852         return this.activePanel;
29853     },
29854     
29855     /**
29856      * Add the passed ContentPanel(s)
29857      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29858      * @return {Roo.ContentPanel} The panel added (if only one was added)
29859      */
29860     add : function(panel){
29861         if(arguments.length > 1){
29862             for(var i = 0, len = arguments.length; i < len; i++) {
29863                 this.add(arguments[i]);
29864             }
29865             return null;
29866         }
29867         if(this.hasPanel(panel)){
29868             this.showPanel(panel);
29869             return panel;
29870         }
29871         var el = panel.getEl();
29872         if(el.dom.parentNode != this.mgr.el.dom){
29873             this.mgr.el.dom.appendChild(el.dom);
29874         }
29875         if(panel.setRegion){
29876             panel.setRegion(this);
29877         }
29878         this.panels.add(panel);
29879         el.setStyle("position", "absolute");
29880         if(!panel.background){
29881             this.setActivePanel(panel);
29882             if(this.config.initialSize && this.panels.getCount()==1){
29883                 this.resizeTo(this.config.initialSize);
29884             }
29885         }
29886         this.fireEvent("paneladded", this, panel);
29887         return panel;
29888     },
29889     
29890     /**
29891      * Returns true if the panel is in this region.
29892      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29893      * @return {Boolean}
29894      */
29895     hasPanel : function(panel){
29896         if(typeof panel == "object"){ // must be panel obj
29897             panel = panel.getId();
29898         }
29899         return this.getPanel(panel) ? true : false;
29900     },
29901     
29902     /**
29903      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29904      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29905      * @param {Boolean} preservePanel Overrides the config preservePanel option
29906      * @return {Roo.ContentPanel} The panel that was removed
29907      */
29908     remove : function(panel, preservePanel){
29909         panel = this.getPanel(panel);
29910         if(!panel){
29911             return null;
29912         }
29913         var e = {};
29914         this.fireEvent("beforeremove", this, panel, e);
29915         if(e.cancel === true){
29916             return null;
29917         }
29918         var panelId = panel.getId();
29919         this.panels.removeKey(panelId);
29920         return panel;
29921     },
29922     
29923     /**
29924      * Returns the panel specified or null if it's not in this region.
29925      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29926      * @return {Roo.ContentPanel}
29927      */
29928     getPanel : function(id){
29929         if(typeof id == "object"){ // must be panel obj
29930             return id;
29931         }
29932         return this.panels.get(id);
29933     },
29934     
29935     /**
29936      * Returns this regions position (north/south/east/west/center).
29937      * @return {String} 
29938      */
29939     getPosition: function(){
29940         return this.position;    
29941     }
29942 });/*
29943  * Based on:
29944  * Ext JS Library 1.1.1
29945  * Copyright(c) 2006-2007, Ext JS, LLC.
29946  *
29947  * Originally Released Under LGPL - original licence link has changed is not relivant.
29948  *
29949  * Fork - LGPL
29950  * <script type="text/javascript">
29951  */
29952  
29953 /**
29954  * @class Roo.LayoutRegion
29955  * @extends Roo.BasicLayoutRegion
29956  * This class represents a region in a layout manager.
29957  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29958  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29959  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29960  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29961  * @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})
29962  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29963  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29964  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29965  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29966  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29967  * @cfg {String}    title           The title for the region (overrides panel titles)
29968  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29969  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29970  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29971  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29972  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29973  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29974  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29975  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29976  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29977  * @cfg {Boolean}   showPin         True to show a pin button
29978  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29979  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29980  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29981  * @cfg {Number}    width           For East/West panels
29982  * @cfg {Number}    height          For North/South panels
29983  * @cfg {Boolean}   split           To show the splitter
29984  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29985  */
29986 Roo.LayoutRegion = function(mgr, config, pos){
29987     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29988     var dh = Roo.DomHelper;
29989     /** This region's container element 
29990     * @type Roo.Element */
29991     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29992     /** This region's title element 
29993     * @type Roo.Element */
29994
29995     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29996         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29997         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29998     ]}, true);
29999     this.titleEl.enableDisplayMode();
30000     /** This region's title text element 
30001     * @type HTMLElement */
30002     this.titleTextEl = this.titleEl.dom.firstChild;
30003     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30004     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30005     this.closeBtn.enableDisplayMode();
30006     this.closeBtn.on("click", this.closeClicked, this);
30007     this.closeBtn.hide();
30008
30009     this.createBody(config);
30010     this.visible = true;
30011     this.collapsed = false;
30012
30013     if(config.hideWhenEmpty){
30014         this.hide();
30015         this.on("paneladded", this.validateVisibility, this);
30016         this.on("panelremoved", this.validateVisibility, this);
30017     }
30018     this.applyConfig(config);
30019 };
30020
30021 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30022
30023     createBody : function(){
30024         /** This region's body element 
30025         * @type Roo.Element */
30026         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30027     },
30028
30029     applyConfig : function(c){
30030         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30031             var dh = Roo.DomHelper;
30032             if(c.titlebar !== false){
30033                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30034                 this.collapseBtn.on("click", this.collapse, this);
30035                 this.collapseBtn.enableDisplayMode();
30036
30037                 if(c.showPin === true || this.showPin){
30038                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30039                     this.stickBtn.enableDisplayMode();
30040                     this.stickBtn.on("click", this.expand, this);
30041                     this.stickBtn.hide();
30042                 }
30043             }
30044             /** This region's collapsed element
30045             * @type Roo.Element */
30046             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30047                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30048             ]}, true);
30049             if(c.floatable !== false){
30050                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30051                this.collapsedEl.on("click", this.collapseClick, this);
30052             }
30053
30054             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30055                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30056                    id: "message", unselectable: "on", style:{"float":"left"}});
30057                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30058              }
30059             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30060             this.expandBtn.on("click", this.expand, this);
30061         }
30062         if(this.collapseBtn){
30063             this.collapseBtn.setVisible(c.collapsible == true);
30064         }
30065         this.cmargins = c.cmargins || this.cmargins ||
30066                          (this.position == "west" || this.position == "east" ?
30067                              {top: 0, left: 2, right:2, bottom: 0} :
30068                              {top: 2, left: 0, right:0, bottom: 2});
30069         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30070         this.bottomTabs = c.tabPosition != "top";
30071         this.autoScroll = c.autoScroll || false;
30072         if(this.autoScroll){
30073             this.bodyEl.setStyle("overflow", "auto");
30074         }else{
30075             this.bodyEl.setStyle("overflow", "hidden");
30076         }
30077         //if(c.titlebar !== false){
30078             if((!c.titlebar && !c.title) || c.titlebar === false){
30079                 this.titleEl.hide();
30080             }else{
30081                 this.titleEl.show();
30082                 if(c.title){
30083                     this.titleTextEl.innerHTML = c.title;
30084                 }
30085             }
30086         //}
30087         this.duration = c.duration || .30;
30088         this.slideDuration = c.slideDuration || .45;
30089         this.config = c;
30090         if(c.collapsed){
30091             this.collapse(true);
30092         }
30093         if(c.hidden){
30094             this.hide();
30095         }
30096     },
30097     /**
30098      * Returns true if this region is currently visible.
30099      * @return {Boolean}
30100      */
30101     isVisible : function(){
30102         return this.visible;
30103     },
30104
30105     /**
30106      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30107      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30108      */
30109     setCollapsedTitle : function(title){
30110         title = title || "&#160;";
30111         if(this.collapsedTitleTextEl){
30112             this.collapsedTitleTextEl.innerHTML = title;
30113         }
30114     },
30115
30116     getBox : function(){
30117         var b;
30118         if(!this.collapsed){
30119             b = this.el.getBox(false, true);
30120         }else{
30121             b = this.collapsedEl.getBox(false, true);
30122         }
30123         return b;
30124     },
30125
30126     getMargins : function(){
30127         return this.collapsed ? this.cmargins : this.margins;
30128     },
30129
30130     highlight : function(){
30131         this.el.addClass("x-layout-panel-dragover");
30132     },
30133
30134     unhighlight : function(){
30135         this.el.removeClass("x-layout-panel-dragover");
30136     },
30137
30138     updateBox : function(box){
30139         this.box = box;
30140         if(!this.collapsed){
30141             this.el.dom.style.left = box.x + "px";
30142             this.el.dom.style.top = box.y + "px";
30143             this.updateBody(box.width, box.height);
30144         }else{
30145             this.collapsedEl.dom.style.left = box.x + "px";
30146             this.collapsedEl.dom.style.top = box.y + "px";
30147             this.collapsedEl.setSize(box.width, box.height);
30148         }
30149         if(this.tabs){
30150             this.tabs.autoSizeTabs();
30151         }
30152     },
30153
30154     updateBody : function(w, h){
30155         if(w !== null){
30156             this.el.setWidth(w);
30157             w -= this.el.getBorderWidth("rl");
30158             if(this.config.adjustments){
30159                 w += this.config.adjustments[0];
30160             }
30161         }
30162         if(h !== null){
30163             this.el.setHeight(h);
30164             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30165             h -= this.el.getBorderWidth("tb");
30166             if(this.config.adjustments){
30167                 h += this.config.adjustments[1];
30168             }
30169             this.bodyEl.setHeight(h);
30170             if(this.tabs){
30171                 h = this.tabs.syncHeight(h);
30172             }
30173         }
30174         if(this.panelSize){
30175             w = w !== null ? w : this.panelSize.width;
30176             h = h !== null ? h : this.panelSize.height;
30177         }
30178         if(this.activePanel){
30179             var el = this.activePanel.getEl();
30180             w = w !== null ? w : el.getWidth();
30181             h = h !== null ? h : el.getHeight();
30182             this.panelSize = {width: w, height: h};
30183             this.activePanel.setSize(w, h);
30184         }
30185         if(Roo.isIE && this.tabs){
30186             this.tabs.el.repaint();
30187         }
30188     },
30189
30190     /**
30191      * Returns the container element for this region.
30192      * @return {Roo.Element}
30193      */
30194     getEl : function(){
30195         return this.el;
30196     },
30197
30198     /**
30199      * Hides this region.
30200      */
30201     hide : function(){
30202         if(!this.collapsed){
30203             this.el.dom.style.left = "-2000px";
30204             this.el.hide();
30205         }else{
30206             this.collapsedEl.dom.style.left = "-2000px";
30207             this.collapsedEl.hide();
30208         }
30209         this.visible = false;
30210         this.fireEvent("visibilitychange", this, false);
30211     },
30212
30213     /**
30214      * Shows this region if it was previously hidden.
30215      */
30216     show : function(){
30217         if(!this.collapsed){
30218             this.el.show();
30219         }else{
30220             this.collapsedEl.show();
30221         }
30222         this.visible = true;
30223         this.fireEvent("visibilitychange", this, true);
30224     },
30225
30226     closeClicked : function(){
30227         if(this.activePanel){
30228             this.remove(this.activePanel);
30229         }
30230     },
30231
30232     collapseClick : function(e){
30233         if(this.isSlid){
30234            e.stopPropagation();
30235            this.slideIn();
30236         }else{
30237            e.stopPropagation();
30238            this.slideOut();
30239         }
30240     },
30241
30242     /**
30243      * Collapses this region.
30244      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30245      */
30246     collapse : function(skipAnim, skipCheck){
30247         if(this.collapsed) {
30248             return;
30249         }
30250         
30251         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30252             
30253             this.collapsed = true;
30254             if(this.split){
30255                 this.split.el.hide();
30256             }
30257             if(this.config.animate && skipAnim !== true){
30258                 this.fireEvent("invalidated", this);
30259                 this.animateCollapse();
30260             }else{
30261                 this.el.setLocation(-20000,-20000);
30262                 this.el.hide();
30263                 this.collapsedEl.show();
30264                 this.fireEvent("collapsed", this);
30265                 this.fireEvent("invalidated", this);
30266             }
30267         }
30268         
30269     },
30270
30271     animateCollapse : function(){
30272         // overridden
30273     },
30274
30275     /**
30276      * Expands this region if it was previously collapsed.
30277      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30278      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30279      */
30280     expand : function(e, skipAnim){
30281         if(e) {
30282             e.stopPropagation();
30283         }
30284         if(!this.collapsed || this.el.hasActiveFx()) {
30285             return;
30286         }
30287         if(this.isSlid){
30288             this.afterSlideIn();
30289             skipAnim = true;
30290         }
30291         this.collapsed = false;
30292         if(this.config.animate && skipAnim !== true){
30293             this.animateExpand();
30294         }else{
30295             this.el.show();
30296             if(this.split){
30297                 this.split.el.show();
30298             }
30299             this.collapsedEl.setLocation(-2000,-2000);
30300             this.collapsedEl.hide();
30301             this.fireEvent("invalidated", this);
30302             this.fireEvent("expanded", this);
30303         }
30304     },
30305
30306     animateExpand : function(){
30307         // overridden
30308     },
30309
30310     initTabs : function()
30311     {
30312         this.bodyEl.setStyle("overflow", "hidden");
30313         var ts = new Roo.TabPanel(
30314                 this.bodyEl.dom,
30315                 {
30316                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30317                     disableTooltips: this.config.disableTabTips,
30318                     toolbar : this.config.toolbar
30319                 }
30320         );
30321         if(this.config.hideTabs){
30322             ts.stripWrap.setDisplayed(false);
30323         }
30324         this.tabs = ts;
30325         ts.resizeTabs = this.config.resizeTabs === true;
30326         ts.minTabWidth = this.config.minTabWidth || 40;
30327         ts.maxTabWidth = this.config.maxTabWidth || 250;
30328         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30329         ts.monitorResize = false;
30330         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30331         ts.bodyEl.addClass('x-layout-tabs-body');
30332         this.panels.each(this.initPanelAsTab, this);
30333     },
30334
30335     initPanelAsTab : function(panel){
30336         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30337                     this.config.closeOnTab && panel.isClosable());
30338         if(panel.tabTip !== undefined){
30339             ti.setTooltip(panel.tabTip);
30340         }
30341         ti.on("activate", function(){
30342               this.setActivePanel(panel);
30343         }, this);
30344         if(this.config.closeOnTab){
30345             ti.on("beforeclose", function(t, e){
30346                 e.cancel = true;
30347                 this.remove(panel);
30348             }, this);
30349         }
30350         return ti;
30351     },
30352
30353     updatePanelTitle : function(panel, title){
30354         if(this.activePanel == panel){
30355             this.updateTitle(title);
30356         }
30357         if(this.tabs){
30358             var ti = this.tabs.getTab(panel.getEl().id);
30359             ti.setText(title);
30360             if(panel.tabTip !== undefined){
30361                 ti.setTooltip(panel.tabTip);
30362             }
30363         }
30364     },
30365
30366     updateTitle : function(title){
30367         if(this.titleTextEl && !this.config.title){
30368             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30369         }
30370     },
30371
30372     setActivePanel : function(panel){
30373         panel = this.getPanel(panel);
30374         if(this.activePanel && this.activePanel != panel){
30375             this.activePanel.setActiveState(false);
30376         }
30377         this.activePanel = panel;
30378         panel.setActiveState(true);
30379         if(this.panelSize){
30380             panel.setSize(this.panelSize.width, this.panelSize.height);
30381         }
30382         if(this.closeBtn){
30383             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30384         }
30385         this.updateTitle(panel.getTitle());
30386         if(this.tabs){
30387             this.fireEvent("invalidated", this);
30388         }
30389         this.fireEvent("panelactivated", this, panel);
30390     },
30391
30392     /**
30393      * Shows the specified panel.
30394      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30395      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30396      */
30397     showPanel : function(panel)
30398     {
30399         panel = this.getPanel(panel);
30400         if(panel){
30401             if(this.tabs){
30402                 var tab = this.tabs.getTab(panel.getEl().id);
30403                 if(tab.isHidden()){
30404                     this.tabs.unhideTab(tab.id);
30405                 }
30406                 tab.activate();
30407             }else{
30408                 this.setActivePanel(panel);
30409             }
30410         }
30411         return panel;
30412     },
30413
30414     /**
30415      * Get the active panel for this region.
30416      * @return {Roo.ContentPanel} The active panel or null
30417      */
30418     getActivePanel : function(){
30419         return this.activePanel;
30420     },
30421
30422     validateVisibility : function(){
30423         if(this.panels.getCount() < 1){
30424             this.updateTitle("&#160;");
30425             this.closeBtn.hide();
30426             this.hide();
30427         }else{
30428             if(!this.isVisible()){
30429                 this.show();
30430             }
30431         }
30432     },
30433
30434     /**
30435      * Adds the passed ContentPanel(s) to this region.
30436      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30437      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30438      */
30439     add : function(panel){
30440         if(arguments.length > 1){
30441             for(var i = 0, len = arguments.length; i < len; i++) {
30442                 this.add(arguments[i]);
30443             }
30444             return null;
30445         }
30446         if(this.hasPanel(panel)){
30447             this.showPanel(panel);
30448             return panel;
30449         }
30450         panel.setRegion(this);
30451         this.panels.add(panel);
30452         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30453             this.bodyEl.dom.appendChild(panel.getEl().dom);
30454             if(panel.background !== true){
30455                 this.setActivePanel(panel);
30456             }
30457             this.fireEvent("paneladded", this, panel);
30458             return panel;
30459         }
30460         if(!this.tabs){
30461             this.initTabs();
30462         }else{
30463             this.initPanelAsTab(panel);
30464         }
30465         if(panel.background !== true){
30466             this.tabs.activate(panel.getEl().id);
30467         }
30468         this.fireEvent("paneladded", this, panel);
30469         return panel;
30470     },
30471
30472     /**
30473      * Hides the tab for the specified panel.
30474      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30475      */
30476     hidePanel : function(panel){
30477         if(this.tabs && (panel = this.getPanel(panel))){
30478             this.tabs.hideTab(panel.getEl().id);
30479         }
30480     },
30481
30482     /**
30483      * Unhides the tab for a previously hidden panel.
30484      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30485      */
30486     unhidePanel : function(panel){
30487         if(this.tabs && (panel = this.getPanel(panel))){
30488             this.tabs.unhideTab(panel.getEl().id);
30489         }
30490     },
30491
30492     clearPanels : function(){
30493         while(this.panels.getCount() > 0){
30494              this.remove(this.panels.first());
30495         }
30496     },
30497
30498     /**
30499      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30500      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30501      * @param {Boolean} preservePanel Overrides the config preservePanel option
30502      * @return {Roo.ContentPanel} The panel that was removed
30503      */
30504     remove : function(panel, preservePanel){
30505         panel = this.getPanel(panel);
30506         if(!panel){
30507             return null;
30508         }
30509         var e = {};
30510         this.fireEvent("beforeremove", this, panel, e);
30511         if(e.cancel === true){
30512             return null;
30513         }
30514         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30515         var panelId = panel.getId();
30516         this.panels.removeKey(panelId);
30517         if(preservePanel){
30518             document.body.appendChild(panel.getEl().dom);
30519         }
30520         if(this.tabs){
30521             this.tabs.removeTab(panel.getEl().id);
30522         }else if (!preservePanel){
30523             this.bodyEl.dom.removeChild(panel.getEl().dom);
30524         }
30525         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30526             var p = this.panels.first();
30527             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30528             tempEl.appendChild(p.getEl().dom);
30529             this.bodyEl.update("");
30530             this.bodyEl.dom.appendChild(p.getEl().dom);
30531             tempEl = null;
30532             this.updateTitle(p.getTitle());
30533             this.tabs = null;
30534             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30535             this.setActivePanel(p);
30536         }
30537         panel.setRegion(null);
30538         if(this.activePanel == panel){
30539             this.activePanel = null;
30540         }
30541         if(this.config.autoDestroy !== false && preservePanel !== true){
30542             try{panel.destroy();}catch(e){}
30543         }
30544         this.fireEvent("panelremoved", this, panel);
30545         return panel;
30546     },
30547
30548     /**
30549      * Returns the TabPanel component used by this region
30550      * @return {Roo.TabPanel}
30551      */
30552     getTabs : function(){
30553         return this.tabs;
30554     },
30555
30556     createTool : function(parentEl, className){
30557         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30558             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30559         btn.addClassOnOver("x-layout-tools-button-over");
30560         return btn;
30561     }
30562 });/*
30563  * Based on:
30564  * Ext JS Library 1.1.1
30565  * Copyright(c) 2006-2007, Ext JS, LLC.
30566  *
30567  * Originally Released Under LGPL - original licence link has changed is not relivant.
30568  *
30569  * Fork - LGPL
30570  * <script type="text/javascript">
30571  */
30572  
30573
30574
30575 /**
30576  * @class Roo.SplitLayoutRegion
30577  * @extends Roo.LayoutRegion
30578  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30579  */
30580 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30581     this.cursor = cursor;
30582     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30583 };
30584
30585 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30586     splitTip : "Drag to resize.",
30587     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30588     useSplitTips : false,
30589
30590     applyConfig : function(config){
30591         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30592         if(config.split){
30593             if(!this.split){
30594                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30595                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30596                 /** The SplitBar for this region 
30597                 * @type Roo.SplitBar */
30598                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30599                 this.split.on("moved", this.onSplitMove, this);
30600                 this.split.useShim = config.useShim === true;
30601                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30602                 if(this.useSplitTips){
30603                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30604                 }
30605                 if(config.collapsible){
30606                     this.split.el.on("dblclick", this.collapse,  this);
30607                 }
30608             }
30609             if(typeof config.minSize != "undefined"){
30610                 this.split.minSize = config.minSize;
30611             }
30612             if(typeof config.maxSize != "undefined"){
30613                 this.split.maxSize = config.maxSize;
30614             }
30615             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30616                 this.hideSplitter();
30617             }
30618         }
30619     },
30620
30621     getHMaxSize : function(){
30622          var cmax = this.config.maxSize || 10000;
30623          var center = this.mgr.getRegion("center");
30624          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30625     },
30626
30627     getVMaxSize : function(){
30628          var cmax = this.config.maxSize || 10000;
30629          var center = this.mgr.getRegion("center");
30630          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30631     },
30632
30633     onSplitMove : function(split, newSize){
30634         this.fireEvent("resized", this, newSize);
30635     },
30636     
30637     /** 
30638      * Returns the {@link Roo.SplitBar} for this region.
30639      * @return {Roo.SplitBar}
30640      */
30641     getSplitBar : function(){
30642         return this.split;
30643     },
30644     
30645     hide : function(){
30646         this.hideSplitter();
30647         Roo.SplitLayoutRegion.superclass.hide.call(this);
30648     },
30649
30650     hideSplitter : function(){
30651         if(this.split){
30652             this.split.el.setLocation(-2000,-2000);
30653             this.split.el.hide();
30654         }
30655     },
30656
30657     show : function(){
30658         if(this.split){
30659             this.split.el.show();
30660         }
30661         Roo.SplitLayoutRegion.superclass.show.call(this);
30662     },
30663     
30664     beforeSlide: function(){
30665         if(Roo.isGecko){// firefox overflow auto bug workaround
30666             this.bodyEl.clip();
30667             if(this.tabs) {
30668                 this.tabs.bodyEl.clip();
30669             }
30670             if(this.activePanel){
30671                 this.activePanel.getEl().clip();
30672                 
30673                 if(this.activePanel.beforeSlide){
30674                     this.activePanel.beforeSlide();
30675                 }
30676             }
30677         }
30678     },
30679     
30680     afterSlide : function(){
30681         if(Roo.isGecko){// firefox overflow auto bug workaround
30682             this.bodyEl.unclip();
30683             if(this.tabs) {
30684                 this.tabs.bodyEl.unclip();
30685             }
30686             if(this.activePanel){
30687                 this.activePanel.getEl().unclip();
30688                 if(this.activePanel.afterSlide){
30689                     this.activePanel.afterSlide();
30690                 }
30691             }
30692         }
30693     },
30694
30695     initAutoHide : function(){
30696         if(this.autoHide !== false){
30697             if(!this.autoHideHd){
30698                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30699                 this.autoHideHd = {
30700                     "mouseout": function(e){
30701                         if(!e.within(this.el, true)){
30702                             st.delay(500);
30703                         }
30704                     },
30705                     "mouseover" : function(e){
30706                         st.cancel();
30707                     },
30708                     scope : this
30709                 };
30710             }
30711             this.el.on(this.autoHideHd);
30712         }
30713     },
30714
30715     clearAutoHide : function(){
30716         if(this.autoHide !== false){
30717             this.el.un("mouseout", this.autoHideHd.mouseout);
30718             this.el.un("mouseover", this.autoHideHd.mouseover);
30719         }
30720     },
30721
30722     clearMonitor : function(){
30723         Roo.get(document).un("click", this.slideInIf, this);
30724     },
30725
30726     // these names are backwards but not changed for compat
30727     slideOut : function(){
30728         if(this.isSlid || this.el.hasActiveFx()){
30729             return;
30730         }
30731         this.isSlid = true;
30732         if(this.collapseBtn){
30733             this.collapseBtn.hide();
30734         }
30735         this.closeBtnState = this.closeBtn.getStyle('display');
30736         this.closeBtn.hide();
30737         if(this.stickBtn){
30738             this.stickBtn.show();
30739         }
30740         this.el.show();
30741         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30742         this.beforeSlide();
30743         this.el.setStyle("z-index", 10001);
30744         this.el.slideIn(this.getSlideAnchor(), {
30745             callback: function(){
30746                 this.afterSlide();
30747                 this.initAutoHide();
30748                 Roo.get(document).on("click", this.slideInIf, this);
30749                 this.fireEvent("slideshow", this);
30750             },
30751             scope: this,
30752             block: true
30753         });
30754     },
30755
30756     afterSlideIn : function(){
30757         this.clearAutoHide();
30758         this.isSlid = false;
30759         this.clearMonitor();
30760         this.el.setStyle("z-index", "");
30761         if(this.collapseBtn){
30762             this.collapseBtn.show();
30763         }
30764         this.closeBtn.setStyle('display', this.closeBtnState);
30765         if(this.stickBtn){
30766             this.stickBtn.hide();
30767         }
30768         this.fireEvent("slidehide", this);
30769     },
30770
30771     slideIn : function(cb){
30772         if(!this.isSlid || this.el.hasActiveFx()){
30773             Roo.callback(cb);
30774             return;
30775         }
30776         this.isSlid = false;
30777         this.beforeSlide();
30778         this.el.slideOut(this.getSlideAnchor(), {
30779             callback: function(){
30780                 this.el.setLeftTop(-10000, -10000);
30781                 this.afterSlide();
30782                 this.afterSlideIn();
30783                 Roo.callback(cb);
30784             },
30785             scope: this,
30786             block: true
30787         });
30788     },
30789     
30790     slideInIf : function(e){
30791         if(!e.within(this.el)){
30792             this.slideIn();
30793         }
30794     },
30795
30796     animateCollapse : function(){
30797         this.beforeSlide();
30798         this.el.setStyle("z-index", 20000);
30799         var anchor = this.getSlideAnchor();
30800         this.el.slideOut(anchor, {
30801             callback : function(){
30802                 this.el.setStyle("z-index", "");
30803                 this.collapsedEl.slideIn(anchor, {duration:.3});
30804                 this.afterSlide();
30805                 this.el.setLocation(-10000,-10000);
30806                 this.el.hide();
30807                 this.fireEvent("collapsed", this);
30808             },
30809             scope: this,
30810             block: true
30811         });
30812     },
30813
30814     animateExpand : function(){
30815         this.beforeSlide();
30816         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30817         this.el.setStyle("z-index", 20000);
30818         this.collapsedEl.hide({
30819             duration:.1
30820         });
30821         this.el.slideIn(this.getSlideAnchor(), {
30822             callback : function(){
30823                 this.el.setStyle("z-index", "");
30824                 this.afterSlide();
30825                 if(this.split){
30826                     this.split.el.show();
30827                 }
30828                 this.fireEvent("invalidated", this);
30829                 this.fireEvent("expanded", this);
30830             },
30831             scope: this,
30832             block: true
30833         });
30834     },
30835
30836     anchors : {
30837         "west" : "left",
30838         "east" : "right",
30839         "north" : "top",
30840         "south" : "bottom"
30841     },
30842
30843     sanchors : {
30844         "west" : "l",
30845         "east" : "r",
30846         "north" : "t",
30847         "south" : "b"
30848     },
30849
30850     canchors : {
30851         "west" : "tl-tr",
30852         "east" : "tr-tl",
30853         "north" : "tl-bl",
30854         "south" : "bl-tl"
30855     },
30856
30857     getAnchor : function(){
30858         return this.anchors[this.position];
30859     },
30860
30861     getCollapseAnchor : function(){
30862         return this.canchors[this.position];
30863     },
30864
30865     getSlideAnchor : function(){
30866         return this.sanchors[this.position];
30867     },
30868
30869     getAlignAdj : function(){
30870         var cm = this.cmargins;
30871         switch(this.position){
30872             case "west":
30873                 return [0, 0];
30874             break;
30875             case "east":
30876                 return [0, 0];
30877             break;
30878             case "north":
30879                 return [0, 0];
30880             break;
30881             case "south":
30882                 return [0, 0];
30883             break;
30884         }
30885     },
30886
30887     getExpandAdj : function(){
30888         var c = this.collapsedEl, cm = this.cmargins;
30889         switch(this.position){
30890             case "west":
30891                 return [-(cm.right+c.getWidth()+cm.left), 0];
30892             break;
30893             case "east":
30894                 return [cm.right+c.getWidth()+cm.left, 0];
30895             break;
30896             case "north":
30897                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30898             break;
30899             case "south":
30900                 return [0, cm.top+cm.bottom+c.getHeight()];
30901             break;
30902         }
30903     }
30904 });/*
30905  * Based on:
30906  * Ext JS Library 1.1.1
30907  * Copyright(c) 2006-2007, Ext JS, LLC.
30908  *
30909  * Originally Released Under LGPL - original licence link has changed is not relivant.
30910  *
30911  * Fork - LGPL
30912  * <script type="text/javascript">
30913  */
30914 /*
30915  * These classes are private internal classes
30916  */
30917 Roo.CenterLayoutRegion = function(mgr, config){
30918     Roo.LayoutRegion.call(this, mgr, config, "center");
30919     this.visible = true;
30920     this.minWidth = config.minWidth || 20;
30921     this.minHeight = config.minHeight || 20;
30922 };
30923
30924 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30925     hide : function(){
30926         // center panel can't be hidden
30927     },
30928     
30929     show : function(){
30930         // center panel can't be hidden
30931     },
30932     
30933     getMinWidth: function(){
30934         return this.minWidth;
30935     },
30936     
30937     getMinHeight: function(){
30938         return this.minHeight;
30939     }
30940 });
30941
30942
30943 Roo.NorthLayoutRegion = function(mgr, config){
30944     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30945     if(this.split){
30946         this.split.placement = Roo.SplitBar.TOP;
30947         this.split.orientation = Roo.SplitBar.VERTICAL;
30948         this.split.el.addClass("x-layout-split-v");
30949     }
30950     var size = config.initialSize || config.height;
30951     if(typeof size != "undefined"){
30952         this.el.setHeight(size);
30953     }
30954 };
30955 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30956     orientation: Roo.SplitBar.VERTICAL,
30957     getBox : function(){
30958         if(this.collapsed){
30959             return this.collapsedEl.getBox();
30960         }
30961         var box = this.el.getBox();
30962         if(this.split){
30963             box.height += this.split.el.getHeight();
30964         }
30965         return box;
30966     },
30967     
30968     updateBox : function(box){
30969         if(this.split && !this.collapsed){
30970             box.height -= this.split.el.getHeight();
30971             this.split.el.setLeft(box.x);
30972             this.split.el.setTop(box.y+box.height);
30973             this.split.el.setWidth(box.width);
30974         }
30975         if(this.collapsed){
30976             this.updateBody(box.width, null);
30977         }
30978         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30979     }
30980 });
30981
30982 Roo.SouthLayoutRegion = function(mgr, config){
30983     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30984     if(this.split){
30985         this.split.placement = Roo.SplitBar.BOTTOM;
30986         this.split.orientation = Roo.SplitBar.VERTICAL;
30987         this.split.el.addClass("x-layout-split-v");
30988     }
30989     var size = config.initialSize || config.height;
30990     if(typeof size != "undefined"){
30991         this.el.setHeight(size);
30992     }
30993 };
30994 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30995     orientation: Roo.SplitBar.VERTICAL,
30996     getBox : function(){
30997         if(this.collapsed){
30998             return this.collapsedEl.getBox();
30999         }
31000         var box = this.el.getBox();
31001         if(this.split){
31002             var sh = this.split.el.getHeight();
31003             box.height += sh;
31004             box.y -= sh;
31005         }
31006         return box;
31007     },
31008     
31009     updateBox : function(box){
31010         if(this.split && !this.collapsed){
31011             var sh = this.split.el.getHeight();
31012             box.height -= sh;
31013             box.y += sh;
31014             this.split.el.setLeft(box.x);
31015             this.split.el.setTop(box.y-sh);
31016             this.split.el.setWidth(box.width);
31017         }
31018         if(this.collapsed){
31019             this.updateBody(box.width, null);
31020         }
31021         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31022     }
31023 });
31024
31025 Roo.EastLayoutRegion = function(mgr, config){
31026     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31027     if(this.split){
31028         this.split.placement = Roo.SplitBar.RIGHT;
31029         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31030         this.split.el.addClass("x-layout-split-h");
31031     }
31032     var size = config.initialSize || config.width;
31033     if(typeof size != "undefined"){
31034         this.el.setWidth(size);
31035     }
31036 };
31037 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31038     orientation: Roo.SplitBar.HORIZONTAL,
31039     getBox : function(){
31040         if(this.collapsed){
31041             return this.collapsedEl.getBox();
31042         }
31043         var box = this.el.getBox();
31044         if(this.split){
31045             var sw = this.split.el.getWidth();
31046             box.width += sw;
31047             box.x -= sw;
31048         }
31049         return box;
31050     },
31051
31052     updateBox : function(box){
31053         if(this.split && !this.collapsed){
31054             var sw = this.split.el.getWidth();
31055             box.width -= sw;
31056             this.split.el.setLeft(box.x);
31057             this.split.el.setTop(box.y);
31058             this.split.el.setHeight(box.height);
31059             box.x += sw;
31060         }
31061         if(this.collapsed){
31062             this.updateBody(null, box.height);
31063         }
31064         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31065     }
31066 });
31067
31068 Roo.WestLayoutRegion = function(mgr, config){
31069     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31070     if(this.split){
31071         this.split.placement = Roo.SplitBar.LEFT;
31072         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31073         this.split.el.addClass("x-layout-split-h");
31074     }
31075     var size = config.initialSize || config.width;
31076     if(typeof size != "undefined"){
31077         this.el.setWidth(size);
31078     }
31079 };
31080 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31081     orientation: Roo.SplitBar.HORIZONTAL,
31082     getBox : function(){
31083         if(this.collapsed){
31084             return this.collapsedEl.getBox();
31085         }
31086         var box = this.el.getBox();
31087         if(this.split){
31088             box.width += this.split.el.getWidth();
31089         }
31090         return box;
31091     },
31092     
31093     updateBox : function(box){
31094         if(this.split && !this.collapsed){
31095             var sw = this.split.el.getWidth();
31096             box.width -= sw;
31097             this.split.el.setLeft(box.x+box.width);
31098             this.split.el.setTop(box.y);
31099             this.split.el.setHeight(box.height);
31100         }
31101         if(this.collapsed){
31102             this.updateBody(null, box.height);
31103         }
31104         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31105     }
31106 });
31107 /*
31108  * Based on:
31109  * Ext JS Library 1.1.1
31110  * Copyright(c) 2006-2007, Ext JS, LLC.
31111  *
31112  * Originally Released Under LGPL - original licence link has changed is not relivant.
31113  *
31114  * Fork - LGPL
31115  * <script type="text/javascript">
31116  */
31117  
31118  
31119 /*
31120  * Private internal class for reading and applying state
31121  */
31122 Roo.LayoutStateManager = function(layout){
31123      // default empty state
31124      this.state = {
31125         north: {},
31126         south: {},
31127         east: {},
31128         west: {}       
31129     };
31130 };
31131
31132 Roo.LayoutStateManager.prototype = {
31133     init : function(layout, provider){
31134         this.provider = provider;
31135         var state = provider.get(layout.id+"-layout-state");
31136         if(state){
31137             var wasUpdating = layout.isUpdating();
31138             if(!wasUpdating){
31139                 layout.beginUpdate();
31140             }
31141             for(var key in state){
31142                 if(typeof state[key] != "function"){
31143                     var rstate = state[key];
31144                     var r = layout.getRegion(key);
31145                     if(r && rstate){
31146                         if(rstate.size){
31147                             r.resizeTo(rstate.size);
31148                         }
31149                         if(rstate.collapsed == true){
31150                             r.collapse(true);
31151                         }else{
31152                             r.expand(null, true);
31153                         }
31154                     }
31155                 }
31156             }
31157             if(!wasUpdating){
31158                 layout.endUpdate();
31159             }
31160             this.state = state; 
31161         }
31162         this.layout = layout;
31163         layout.on("regionresized", this.onRegionResized, this);
31164         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31165         layout.on("regionexpanded", this.onRegionExpanded, this);
31166     },
31167     
31168     storeState : function(){
31169         this.provider.set(this.layout.id+"-layout-state", this.state);
31170     },
31171     
31172     onRegionResized : function(region, newSize){
31173         this.state[region.getPosition()].size = newSize;
31174         this.storeState();
31175     },
31176     
31177     onRegionCollapsed : function(region){
31178         this.state[region.getPosition()].collapsed = true;
31179         this.storeState();
31180     },
31181     
31182     onRegionExpanded : function(region){
31183         this.state[region.getPosition()].collapsed = false;
31184         this.storeState();
31185     }
31186 };/*
31187  * Based on:
31188  * Ext JS Library 1.1.1
31189  * Copyright(c) 2006-2007, Ext JS, LLC.
31190  *
31191  * Originally Released Under LGPL - original licence link has changed is not relivant.
31192  *
31193  * Fork - LGPL
31194  * <script type="text/javascript">
31195  */
31196 /**
31197  * @class Roo.ContentPanel
31198  * @extends Roo.util.Observable
31199  * A basic ContentPanel element.
31200  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31201  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31202  * @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
31203  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31204  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31205  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31206  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31207  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31208  * @cfg {String} title          The title for this panel
31209  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31210  * @cfg {String} url            Calls {@link #setUrl} with this value
31211  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31212  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31213  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31214  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31215  * @cfg {String}    style  Extra style to add to the content panel 
31216
31217  * @constructor
31218  * Create a new ContentPanel.
31219  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31220  * @param {String/Object} config A string to set only the title or a config object
31221  * @param {String} content (optional) Set the HTML content for this panel
31222  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31223  */
31224 Roo.ContentPanel = function(el, config, content){
31225     
31226      
31227     /*
31228     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31229         config = el;
31230         el = Roo.id();
31231     }
31232     if (config && config.parentLayout) { 
31233         el = config.parentLayout.el.createChild(); 
31234     }
31235     */
31236     if(el.autoCreate){ // xtype is available if this is called from factory
31237         config = el;
31238         el = Roo.id();
31239     }
31240     this.el = Roo.get(el);
31241     if(!this.el && config && config.autoCreate){
31242         if(typeof config.autoCreate == "object"){
31243             if(!config.autoCreate.id){
31244                 config.autoCreate.id = config.id||el;
31245             }
31246             this.el = Roo.DomHelper.append(document.body,
31247                         config.autoCreate, true);
31248         }else{
31249             this.el = Roo.DomHelper.append(document.body,
31250                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31251         }
31252     }
31253     
31254     
31255     this.closable = false;
31256     this.loaded = false;
31257     this.active = false;
31258     if(typeof config == "string"){
31259         this.title = config;
31260     }else{
31261         Roo.apply(this, config);
31262     }
31263     
31264     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31265         this.wrapEl = this.el.wrap();
31266         this.toolbar.container = this.el.insertSibling(false, 'before');
31267         this.toolbar = new Roo.Toolbar(this.toolbar);
31268     }
31269     
31270     // xtype created footer. - not sure if will work as we normally have to render first..
31271     if (this.footer && !this.footer.el && this.footer.xtype) {
31272         if (!this.wrapEl) {
31273             this.wrapEl = this.el.wrap();
31274         }
31275     
31276         this.footer.container = this.wrapEl.createChild();
31277          
31278         this.footer = Roo.factory(this.footer, Roo);
31279         
31280     }
31281     
31282     if(this.resizeEl){
31283         this.resizeEl = Roo.get(this.resizeEl, true);
31284     }else{
31285         this.resizeEl = this.el;
31286     }
31287     // handle view.xtype
31288     
31289  
31290     
31291     
31292     this.addEvents({
31293         /**
31294          * @event activate
31295          * Fires when this panel is activated. 
31296          * @param {Roo.ContentPanel} this
31297          */
31298         "activate" : true,
31299         /**
31300          * @event deactivate
31301          * Fires when this panel is activated. 
31302          * @param {Roo.ContentPanel} this
31303          */
31304         "deactivate" : true,
31305
31306         /**
31307          * @event resize
31308          * Fires when this panel is resized if fitToFrame is true.
31309          * @param {Roo.ContentPanel} this
31310          * @param {Number} width The width after any component adjustments
31311          * @param {Number} height The height after any component adjustments
31312          */
31313         "resize" : true,
31314         
31315          /**
31316          * @event render
31317          * Fires when this tab is created
31318          * @param {Roo.ContentPanel} this
31319          */
31320         "render" : true
31321          
31322         
31323     });
31324     
31325
31326     
31327     
31328     if(this.autoScroll){
31329         this.resizeEl.setStyle("overflow", "auto");
31330     } else {
31331         // fix randome scrolling
31332         this.el.on('scroll', function() {
31333             Roo.log('fix random scolling');
31334             this.scrollTo('top',0); 
31335         });
31336     }
31337     content = content || this.content;
31338     if(content){
31339         this.setContent(content);
31340     }
31341     if(config && config.url){
31342         this.setUrl(this.url, this.params, this.loadOnce);
31343     }
31344     
31345     
31346     
31347     Roo.ContentPanel.superclass.constructor.call(this);
31348     
31349     if (this.view && typeof(this.view.xtype) != 'undefined') {
31350         this.view.el = this.el.appendChild(document.createElement("div"));
31351         this.view = Roo.factory(this.view); 
31352         this.view.render  &&  this.view.render(false, '');  
31353     }
31354     
31355     
31356     this.fireEvent('render', this);
31357 };
31358
31359 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31360     tabTip:'',
31361     setRegion : function(region){
31362         this.region = region;
31363         if(region){
31364            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31365         }else{
31366            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31367         } 
31368     },
31369     
31370     /**
31371      * Returns the toolbar for this Panel if one was configured. 
31372      * @return {Roo.Toolbar} 
31373      */
31374     getToolbar : function(){
31375         return this.toolbar;
31376     },
31377     
31378     setActiveState : function(active){
31379         this.active = active;
31380         if(!active){
31381             this.fireEvent("deactivate", this);
31382         }else{
31383             this.fireEvent("activate", this);
31384         }
31385     },
31386     /**
31387      * Updates this panel's element
31388      * @param {String} content The new content
31389      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31390     */
31391     setContent : function(content, loadScripts){
31392         this.el.update(content, loadScripts);
31393     },
31394
31395     ignoreResize : function(w, h){
31396         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31397             return true;
31398         }else{
31399             this.lastSize = {width: w, height: h};
31400             return false;
31401         }
31402     },
31403     /**
31404      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31405      * @return {Roo.UpdateManager} The UpdateManager
31406      */
31407     getUpdateManager : function(){
31408         return this.el.getUpdateManager();
31409     },
31410      /**
31411      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31412      * @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:
31413 <pre><code>
31414 panel.load({
31415     url: "your-url.php",
31416     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31417     callback: yourFunction,
31418     scope: yourObject, //(optional scope)
31419     discardUrl: false,
31420     nocache: false,
31421     text: "Loading...",
31422     timeout: 30,
31423     scripts: false
31424 });
31425 </code></pre>
31426      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31427      * 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.
31428      * @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}
31429      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31430      * @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.
31431      * @return {Roo.ContentPanel} this
31432      */
31433     load : function(){
31434         var um = this.el.getUpdateManager();
31435         um.update.apply(um, arguments);
31436         return this;
31437     },
31438
31439
31440     /**
31441      * 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.
31442      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31443      * @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)
31444      * @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)
31445      * @return {Roo.UpdateManager} The UpdateManager
31446      */
31447     setUrl : function(url, params, loadOnce){
31448         if(this.refreshDelegate){
31449             this.removeListener("activate", this.refreshDelegate);
31450         }
31451         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31452         this.on("activate", this.refreshDelegate);
31453         return this.el.getUpdateManager();
31454     },
31455     
31456     _handleRefresh : function(url, params, loadOnce){
31457         if(!loadOnce || !this.loaded){
31458             var updater = this.el.getUpdateManager();
31459             updater.update(url, params, this._setLoaded.createDelegate(this));
31460         }
31461     },
31462     
31463     _setLoaded : function(){
31464         this.loaded = true;
31465     }, 
31466     
31467     /**
31468      * Returns this panel's id
31469      * @return {String} 
31470      */
31471     getId : function(){
31472         return this.el.id;
31473     },
31474     
31475     /** 
31476      * Returns this panel's element - used by regiosn to add.
31477      * @return {Roo.Element} 
31478      */
31479     getEl : function(){
31480         return this.wrapEl || this.el;
31481     },
31482     
31483     adjustForComponents : function(width, height)
31484     {
31485         //Roo.log('adjustForComponents ');
31486         if(this.resizeEl != this.el){
31487             width -= this.el.getFrameWidth('lr');
31488             height -= this.el.getFrameWidth('tb');
31489         }
31490         if(this.toolbar){
31491             var te = this.toolbar.getEl();
31492             height -= te.getHeight();
31493             te.setWidth(width);
31494         }
31495         if(this.footer){
31496             var te = this.footer.getEl();
31497             //Roo.log("footer:" + te.getHeight());
31498             
31499             height -= te.getHeight();
31500             te.setWidth(width);
31501         }
31502         
31503         
31504         if(this.adjustments){
31505             width += this.adjustments[0];
31506             height += this.adjustments[1];
31507         }
31508         return {"width": width, "height": height};
31509     },
31510     
31511     setSize : function(width, height){
31512         if(this.fitToFrame && !this.ignoreResize(width, height)){
31513             if(this.fitContainer && this.resizeEl != this.el){
31514                 this.el.setSize(width, height);
31515             }
31516             var size = this.adjustForComponents(width, height);
31517             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31518             this.fireEvent('resize', this, size.width, size.height);
31519         }
31520     },
31521     
31522     /**
31523      * Returns this panel's title
31524      * @return {String} 
31525      */
31526     getTitle : function(){
31527         return this.title;
31528     },
31529     
31530     /**
31531      * Set this panel's title
31532      * @param {String} title
31533      */
31534     setTitle : function(title){
31535         this.title = title;
31536         if(this.region){
31537             this.region.updatePanelTitle(this, title);
31538         }
31539     },
31540     
31541     /**
31542      * Returns true is this panel was configured to be closable
31543      * @return {Boolean} 
31544      */
31545     isClosable : function(){
31546         return this.closable;
31547     },
31548     
31549     beforeSlide : function(){
31550         this.el.clip();
31551         this.resizeEl.clip();
31552     },
31553     
31554     afterSlide : function(){
31555         this.el.unclip();
31556         this.resizeEl.unclip();
31557     },
31558     
31559     /**
31560      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31561      *   Will fail silently if the {@link #setUrl} method has not been called.
31562      *   This does not activate the panel, just updates its content.
31563      */
31564     refresh : function(){
31565         if(this.refreshDelegate){
31566            this.loaded = false;
31567            this.refreshDelegate();
31568         }
31569     },
31570     
31571     /**
31572      * Destroys this panel
31573      */
31574     destroy : function(){
31575         this.el.removeAllListeners();
31576         var tempEl = document.createElement("span");
31577         tempEl.appendChild(this.el.dom);
31578         tempEl.innerHTML = "";
31579         this.el.remove();
31580         this.el = null;
31581     },
31582     
31583     /**
31584      * form - if the content panel contains a form - this is a reference to it.
31585      * @type {Roo.form.Form}
31586      */
31587     form : false,
31588     /**
31589      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31590      *    This contains a reference to it.
31591      * @type {Roo.View}
31592      */
31593     view : false,
31594     
31595       /**
31596      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31597      * <pre><code>
31598
31599 layout.addxtype({
31600        xtype : 'Form',
31601        items: [ .... ]
31602    }
31603 );
31604
31605 </code></pre>
31606      * @param {Object} cfg Xtype definition of item to add.
31607      */
31608     
31609     addxtype : function(cfg) {
31610         // add form..
31611         if (cfg.xtype.match(/^Form$/)) {
31612             
31613             var el;
31614             //if (this.footer) {
31615             //    el = this.footer.container.insertSibling(false, 'before');
31616             //} else {
31617                 el = this.el.createChild();
31618             //}
31619
31620             this.form = new  Roo.form.Form(cfg);
31621             
31622             
31623             if ( this.form.allItems.length) {
31624                 this.form.render(el.dom);
31625             }
31626             return this.form;
31627         }
31628         // should only have one of theses..
31629         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31630             // views.. should not be just added - used named prop 'view''
31631             
31632             cfg.el = this.el.appendChild(document.createElement("div"));
31633             // factory?
31634             
31635             var ret = new Roo.factory(cfg);
31636              
31637              ret.render && ret.render(false, ''); // render blank..
31638             this.view = ret;
31639             return ret;
31640         }
31641         return false;
31642     }
31643 });
31644
31645 /**
31646  * @class Roo.GridPanel
31647  * @extends Roo.ContentPanel
31648  * @constructor
31649  * Create a new GridPanel.
31650  * @param {Roo.grid.Grid} grid The grid for this panel
31651  * @param {String/Object} config A string to set only the panel's title, or a config object
31652  */
31653 Roo.GridPanel = function(grid, config){
31654     
31655   
31656     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31657         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31658         
31659     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31660     
31661     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31662     
31663     if(this.toolbar){
31664         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31665     }
31666     // xtype created footer. - not sure if will work as we normally have to render first..
31667     if (this.footer && !this.footer.el && this.footer.xtype) {
31668         
31669         this.footer.container = this.grid.getView().getFooterPanel(true);
31670         this.footer.dataSource = this.grid.dataSource;
31671         this.footer = Roo.factory(this.footer, Roo);
31672         
31673     }
31674     
31675     grid.monitorWindowResize = false; // turn off autosizing
31676     grid.autoHeight = false;
31677     grid.autoWidth = false;
31678     this.grid = grid;
31679     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31680 };
31681
31682 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31683     getId : function(){
31684         return this.grid.id;
31685     },
31686     
31687     /**
31688      * Returns the grid for this panel
31689      * @return {Roo.grid.Grid} 
31690      */
31691     getGrid : function(){
31692         return this.grid;    
31693     },
31694     
31695     setSize : function(width, height){
31696         if(!this.ignoreResize(width, height)){
31697             var grid = this.grid;
31698             var size = this.adjustForComponents(width, height);
31699             grid.getGridEl().setSize(size.width, size.height);
31700             grid.autoSize();
31701         }
31702     },
31703     
31704     beforeSlide : function(){
31705         this.grid.getView().scroller.clip();
31706     },
31707     
31708     afterSlide : function(){
31709         this.grid.getView().scroller.unclip();
31710     },
31711     
31712     destroy : function(){
31713         this.grid.destroy();
31714         delete this.grid;
31715         Roo.GridPanel.superclass.destroy.call(this); 
31716     }
31717 });
31718
31719
31720 /**
31721  * @class Roo.NestedLayoutPanel
31722  * @extends Roo.ContentPanel
31723  * @constructor
31724  * Create a new NestedLayoutPanel.
31725  * 
31726  * 
31727  * @param {Roo.BorderLayout} layout The layout for this panel
31728  * @param {String/Object} config A string to set only the title or a config object
31729  */
31730 Roo.NestedLayoutPanel = function(layout, config)
31731 {
31732     // construct with only one argument..
31733     /* FIXME - implement nicer consturctors
31734     if (layout.layout) {
31735         config = layout;
31736         layout = config.layout;
31737         delete config.layout;
31738     }
31739     if (layout.xtype && !layout.getEl) {
31740         // then layout needs constructing..
31741         layout = Roo.factory(layout, Roo);
31742     }
31743     */
31744     
31745     
31746     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31747     
31748     layout.monitorWindowResize = false; // turn off autosizing
31749     this.layout = layout;
31750     this.layout.getEl().addClass("x-layout-nested-layout");
31751     
31752     
31753     
31754     
31755 };
31756
31757 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31758
31759     setSize : function(width, height){
31760         if(!this.ignoreResize(width, height)){
31761             var size = this.adjustForComponents(width, height);
31762             var el = this.layout.getEl();
31763             el.setSize(size.width, size.height);
31764             var touch = el.dom.offsetWidth;
31765             this.layout.layout();
31766             // ie requires a double layout on the first pass
31767             if(Roo.isIE && !this.initialized){
31768                 this.initialized = true;
31769                 this.layout.layout();
31770             }
31771         }
31772     },
31773     
31774     // activate all subpanels if not currently active..
31775     
31776     setActiveState : function(active){
31777         this.active = active;
31778         if(!active){
31779             this.fireEvent("deactivate", this);
31780             return;
31781         }
31782         
31783         this.fireEvent("activate", this);
31784         // not sure if this should happen before or after..
31785         if (!this.layout) {
31786             return; // should not happen..
31787         }
31788         var reg = false;
31789         for (var r in this.layout.regions) {
31790             reg = this.layout.getRegion(r);
31791             if (reg.getActivePanel()) {
31792                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31793                 reg.setActivePanel(reg.getActivePanel());
31794                 continue;
31795             }
31796             if (!reg.panels.length) {
31797                 continue;
31798             }
31799             reg.showPanel(reg.getPanel(0));
31800         }
31801         
31802         
31803         
31804         
31805     },
31806     
31807     /**
31808      * Returns the nested BorderLayout for this panel
31809      * @return {Roo.BorderLayout} 
31810      */
31811     getLayout : function(){
31812         return this.layout;
31813     },
31814     
31815      /**
31816      * Adds a xtype elements to the layout of the nested panel
31817      * <pre><code>
31818
31819 panel.addxtype({
31820        xtype : 'ContentPanel',
31821        region: 'west',
31822        items: [ .... ]
31823    }
31824 );
31825
31826 panel.addxtype({
31827         xtype : 'NestedLayoutPanel',
31828         region: 'west',
31829         layout: {
31830            center: { },
31831            west: { }   
31832         },
31833         items : [ ... list of content panels or nested layout panels.. ]
31834    }
31835 );
31836 </code></pre>
31837      * @param {Object} cfg Xtype definition of item to add.
31838      */
31839     addxtype : function(cfg) {
31840         return this.layout.addxtype(cfg);
31841     
31842     }
31843 });
31844
31845 Roo.ScrollPanel = function(el, config, content){
31846     config = config || {};
31847     config.fitToFrame = true;
31848     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31849     
31850     this.el.dom.style.overflow = "hidden";
31851     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31852     this.el.removeClass("x-layout-inactive-content");
31853     this.el.on("mousewheel", this.onWheel, this);
31854
31855     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31856     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31857     up.unselectable(); down.unselectable();
31858     up.on("click", this.scrollUp, this);
31859     down.on("click", this.scrollDown, this);
31860     up.addClassOnOver("x-scroller-btn-over");
31861     down.addClassOnOver("x-scroller-btn-over");
31862     up.addClassOnClick("x-scroller-btn-click");
31863     down.addClassOnClick("x-scroller-btn-click");
31864     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31865
31866     this.resizeEl = this.el;
31867     this.el = wrap; this.up = up; this.down = down;
31868 };
31869
31870 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31871     increment : 100,
31872     wheelIncrement : 5,
31873     scrollUp : function(){
31874         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31875     },
31876
31877     scrollDown : function(){
31878         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31879     },
31880
31881     afterScroll : function(){
31882         var el = this.resizeEl;
31883         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31884         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31885         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31886     },
31887
31888     setSize : function(){
31889         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31890         this.afterScroll();
31891     },
31892
31893     onWheel : function(e){
31894         var d = e.getWheelDelta();
31895         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31896         this.afterScroll();
31897         e.stopEvent();
31898     },
31899
31900     setContent : function(content, loadScripts){
31901         this.resizeEl.update(content, loadScripts);
31902     }
31903
31904 });
31905
31906
31907
31908
31909
31910
31911
31912
31913
31914 /**
31915  * @class Roo.TreePanel
31916  * @extends Roo.ContentPanel
31917  * @constructor
31918  * Create a new TreePanel. - defaults to fit/scoll contents.
31919  * @param {String/Object} config A string to set only the panel's title, or a config object
31920  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31921  */
31922 Roo.TreePanel = function(config){
31923     var el = config.el;
31924     var tree = config.tree;
31925     delete config.tree; 
31926     delete config.el; // hopefull!
31927     
31928     // wrapper for IE7 strict & safari scroll issue
31929     
31930     var treeEl = el.createChild();
31931     config.resizeEl = treeEl;
31932     
31933     
31934     
31935     Roo.TreePanel.superclass.constructor.call(this, el, config);
31936  
31937  
31938     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31939     //console.log(tree);
31940     this.on('activate', function()
31941     {
31942         if (this.tree.rendered) {
31943             return;
31944         }
31945         //console.log('render tree');
31946         this.tree.render();
31947     });
31948     // this should not be needed.. - it's actually the 'el' that resizes?
31949     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31950     
31951     //this.on('resize',  function (cp, w, h) {
31952     //        this.tree.innerCt.setWidth(w);
31953     //        this.tree.innerCt.setHeight(h);
31954     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31955     //});
31956
31957         
31958     
31959 };
31960
31961 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31962     fitToFrame : true,
31963     autoScroll : true
31964 });
31965
31966
31967
31968
31969
31970
31971
31972
31973
31974
31975
31976 /*
31977  * Based on:
31978  * Ext JS Library 1.1.1
31979  * Copyright(c) 2006-2007, Ext JS, LLC.
31980  *
31981  * Originally Released Under LGPL - original licence link has changed is not relivant.
31982  *
31983  * Fork - LGPL
31984  * <script type="text/javascript">
31985  */
31986  
31987
31988 /**
31989  * @class Roo.ReaderLayout
31990  * @extends Roo.BorderLayout
31991  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31992  * center region containing two nested regions (a top one for a list view and one for item preview below),
31993  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31994  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31995  * expedites the setup of the overall layout and regions for this common application style.
31996  * Example:
31997  <pre><code>
31998 var reader = new Roo.ReaderLayout();
31999 var CP = Roo.ContentPanel;  // shortcut for adding
32000
32001 reader.beginUpdate();
32002 reader.add("north", new CP("north", "North"));
32003 reader.add("west", new CP("west", {title: "West"}));
32004 reader.add("east", new CP("east", {title: "East"}));
32005
32006 reader.regions.listView.add(new CP("listView", "List"));
32007 reader.regions.preview.add(new CP("preview", "Preview"));
32008 reader.endUpdate();
32009 </code></pre>
32010 * @constructor
32011 * Create a new ReaderLayout
32012 * @param {Object} config Configuration options
32013 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32014 * document.body if omitted)
32015 */
32016 Roo.ReaderLayout = function(config, renderTo){
32017     var c = config || {size:{}};
32018     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32019         north: c.north !== false ? Roo.apply({
32020             split:false,
32021             initialSize: 32,
32022             titlebar: false
32023         }, c.north) : false,
32024         west: c.west !== false ? Roo.apply({
32025             split:true,
32026             initialSize: 200,
32027             minSize: 175,
32028             maxSize: 400,
32029             titlebar: true,
32030             collapsible: true,
32031             animate: true,
32032             margins:{left:5,right:0,bottom:5,top:5},
32033             cmargins:{left:5,right:5,bottom:5,top:5}
32034         }, c.west) : false,
32035         east: c.east !== false ? Roo.apply({
32036             split:true,
32037             initialSize: 200,
32038             minSize: 175,
32039             maxSize: 400,
32040             titlebar: true,
32041             collapsible: true,
32042             animate: true,
32043             margins:{left:0,right:5,bottom:5,top:5},
32044             cmargins:{left:5,right:5,bottom:5,top:5}
32045         }, c.east) : false,
32046         center: Roo.apply({
32047             tabPosition: 'top',
32048             autoScroll:false,
32049             closeOnTab: true,
32050             titlebar:false,
32051             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32052         }, c.center)
32053     });
32054
32055     this.el.addClass('x-reader');
32056
32057     this.beginUpdate();
32058
32059     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32060         south: c.preview !== false ? Roo.apply({
32061             split:true,
32062             initialSize: 200,
32063             minSize: 100,
32064             autoScroll:true,
32065             collapsible:true,
32066             titlebar: true,
32067             cmargins:{top:5,left:0, right:0, bottom:0}
32068         }, c.preview) : false,
32069         center: Roo.apply({
32070             autoScroll:false,
32071             titlebar:false,
32072             minHeight:200
32073         }, c.listView)
32074     });
32075     this.add('center', new Roo.NestedLayoutPanel(inner,
32076             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32077
32078     this.endUpdate();
32079
32080     this.regions.preview = inner.getRegion('south');
32081     this.regions.listView = inner.getRegion('center');
32082 };
32083
32084 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32085  * Based on:
32086  * Ext JS Library 1.1.1
32087  * Copyright(c) 2006-2007, Ext JS, LLC.
32088  *
32089  * Originally Released Under LGPL - original licence link has changed is not relivant.
32090  *
32091  * Fork - LGPL
32092  * <script type="text/javascript">
32093  */
32094  
32095 /**
32096  * @class Roo.grid.Grid
32097  * @extends Roo.util.Observable
32098  * This class represents the primary interface of a component based grid control.
32099  * <br><br>Usage:<pre><code>
32100  var grid = new Roo.grid.Grid("my-container-id", {
32101      ds: myDataStore,
32102      cm: myColModel,
32103      selModel: mySelectionModel,
32104      autoSizeColumns: true,
32105      monitorWindowResize: false,
32106      trackMouseOver: true
32107  });
32108  // set any options
32109  grid.render();
32110  * </code></pre>
32111  * <b>Common Problems:</b><br/>
32112  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32113  * element will correct this<br/>
32114  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32115  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32116  * are unpredictable.<br/>
32117  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32118  * grid to calculate dimensions/offsets.<br/>
32119   * @constructor
32120  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32121  * The container MUST have some type of size defined for the grid to fill. The container will be
32122  * automatically set to position relative if it isn't already.
32123  * @param {Object} config A config object that sets properties on this grid.
32124  */
32125 Roo.grid.Grid = function(container, config){
32126         // initialize the container
32127         this.container = Roo.get(container);
32128         this.container.update("");
32129         this.container.setStyle("overflow", "hidden");
32130     this.container.addClass('x-grid-container');
32131
32132     this.id = this.container.id;
32133
32134     Roo.apply(this, config);
32135     // check and correct shorthanded configs
32136     if(this.ds){
32137         this.dataSource = this.ds;
32138         delete this.ds;
32139     }
32140     if(this.cm){
32141         this.colModel = this.cm;
32142         delete this.cm;
32143     }
32144     if(this.sm){
32145         this.selModel = this.sm;
32146         delete this.sm;
32147     }
32148
32149     if (this.selModel) {
32150         this.selModel = Roo.factory(this.selModel, Roo.grid);
32151         this.sm = this.selModel;
32152         this.sm.xmodule = this.xmodule || false;
32153     }
32154     if (typeof(this.colModel.config) == 'undefined') {
32155         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32156         this.cm = this.colModel;
32157         this.cm.xmodule = this.xmodule || false;
32158     }
32159     if (this.dataSource) {
32160         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32161         this.ds = this.dataSource;
32162         this.ds.xmodule = this.xmodule || false;
32163          
32164     }
32165     
32166     
32167     
32168     if(this.width){
32169         this.container.setWidth(this.width);
32170     }
32171
32172     if(this.height){
32173         this.container.setHeight(this.height);
32174     }
32175     /** @private */
32176         this.addEvents({
32177         // raw events
32178         /**
32179          * @event click
32180          * The raw click event for the entire grid.
32181          * @param {Roo.EventObject} e
32182          */
32183         "click" : true,
32184         /**
32185          * @event dblclick
32186          * The raw dblclick event for the entire grid.
32187          * @param {Roo.EventObject} e
32188          */
32189         "dblclick" : true,
32190         /**
32191          * @event contextmenu
32192          * The raw contextmenu event for the entire grid.
32193          * @param {Roo.EventObject} e
32194          */
32195         "contextmenu" : true,
32196         /**
32197          * @event mousedown
32198          * The raw mousedown event for the entire grid.
32199          * @param {Roo.EventObject} e
32200          */
32201         "mousedown" : true,
32202         /**
32203          * @event mouseup
32204          * The raw mouseup event for the entire grid.
32205          * @param {Roo.EventObject} e
32206          */
32207         "mouseup" : true,
32208         /**
32209          * @event mouseover
32210          * The raw mouseover event for the entire grid.
32211          * @param {Roo.EventObject} e
32212          */
32213         "mouseover" : true,
32214         /**
32215          * @event mouseout
32216          * The raw mouseout event for the entire grid.
32217          * @param {Roo.EventObject} e
32218          */
32219         "mouseout" : true,
32220         /**
32221          * @event keypress
32222          * The raw keypress event for the entire grid.
32223          * @param {Roo.EventObject} e
32224          */
32225         "keypress" : true,
32226         /**
32227          * @event keydown
32228          * The raw keydown event for the entire grid.
32229          * @param {Roo.EventObject} e
32230          */
32231         "keydown" : true,
32232
32233         // custom events
32234
32235         /**
32236          * @event cellclick
32237          * Fires when a cell is clicked
32238          * @param {Grid} this
32239          * @param {Number} rowIndex
32240          * @param {Number} columnIndex
32241          * @param {Roo.EventObject} e
32242          */
32243         "cellclick" : true,
32244         /**
32245          * @event celldblclick
32246          * Fires when a cell is double clicked
32247          * @param {Grid} this
32248          * @param {Number} rowIndex
32249          * @param {Number} columnIndex
32250          * @param {Roo.EventObject} e
32251          */
32252         "celldblclick" : true,
32253         /**
32254          * @event rowclick
32255          * Fires when a row is clicked
32256          * @param {Grid} this
32257          * @param {Number} rowIndex
32258          * @param {Roo.EventObject} e
32259          */
32260         "rowclick" : true,
32261         /**
32262          * @event rowdblclick
32263          * Fires when a row is double clicked
32264          * @param {Grid} this
32265          * @param {Number} rowIndex
32266          * @param {Roo.EventObject} e
32267          */
32268         "rowdblclick" : true,
32269         /**
32270          * @event headerclick
32271          * Fires when a header is clicked
32272          * @param {Grid} this
32273          * @param {Number} columnIndex
32274          * @param {Roo.EventObject} e
32275          */
32276         "headerclick" : true,
32277         /**
32278          * @event headerdblclick
32279          * Fires when a header cell is double clicked
32280          * @param {Grid} this
32281          * @param {Number} columnIndex
32282          * @param {Roo.EventObject} e
32283          */
32284         "headerdblclick" : true,
32285         /**
32286          * @event rowcontextmenu
32287          * Fires when a row is right clicked
32288          * @param {Grid} this
32289          * @param {Number} rowIndex
32290          * @param {Roo.EventObject} e
32291          */
32292         "rowcontextmenu" : true,
32293         /**
32294          * @event cellcontextmenu
32295          * Fires when a cell is right clicked
32296          * @param {Grid} this
32297          * @param {Number} rowIndex
32298          * @param {Number} cellIndex
32299          * @param {Roo.EventObject} e
32300          */
32301          "cellcontextmenu" : true,
32302         /**
32303          * @event headercontextmenu
32304          * Fires when a header is right clicked
32305          * @param {Grid} this
32306          * @param {Number} columnIndex
32307          * @param {Roo.EventObject} e
32308          */
32309         "headercontextmenu" : true,
32310         /**
32311          * @event bodyscroll
32312          * Fires when the body element is scrolled
32313          * @param {Number} scrollLeft
32314          * @param {Number} scrollTop
32315          */
32316         "bodyscroll" : true,
32317         /**
32318          * @event columnresize
32319          * Fires when the user resizes a column
32320          * @param {Number} columnIndex
32321          * @param {Number} newSize
32322          */
32323         "columnresize" : true,
32324         /**
32325          * @event columnmove
32326          * Fires when the user moves a column
32327          * @param {Number} oldIndex
32328          * @param {Number} newIndex
32329          */
32330         "columnmove" : true,
32331         /**
32332          * @event startdrag
32333          * Fires when row(s) start being dragged
32334          * @param {Grid} this
32335          * @param {Roo.GridDD} dd The drag drop object
32336          * @param {event} e The raw browser event
32337          */
32338         "startdrag" : true,
32339         /**
32340          * @event enddrag
32341          * Fires when a drag operation is complete
32342          * @param {Grid} this
32343          * @param {Roo.GridDD} dd The drag drop object
32344          * @param {event} e The raw browser event
32345          */
32346         "enddrag" : true,
32347         /**
32348          * @event dragdrop
32349          * Fires when dragged row(s) are dropped on a valid DD target
32350          * @param {Grid} this
32351          * @param {Roo.GridDD} dd The drag drop object
32352          * @param {String} targetId The target drag drop object
32353          * @param {event} e The raw browser event
32354          */
32355         "dragdrop" : true,
32356         /**
32357          * @event dragover
32358          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32359          * @param {Grid} this
32360          * @param {Roo.GridDD} dd The drag drop object
32361          * @param {String} targetId The target drag drop object
32362          * @param {event} e The raw browser event
32363          */
32364         "dragover" : true,
32365         /**
32366          * @event dragenter
32367          *  Fires when the dragged row(s) first cross another DD target while being dragged
32368          * @param {Grid} this
32369          * @param {Roo.GridDD} dd The drag drop object
32370          * @param {String} targetId The target drag drop object
32371          * @param {event} e The raw browser event
32372          */
32373         "dragenter" : true,
32374         /**
32375          * @event dragout
32376          * Fires when the dragged row(s) leave another DD target while being dragged
32377          * @param {Grid} this
32378          * @param {Roo.GridDD} dd The drag drop object
32379          * @param {String} targetId The target drag drop object
32380          * @param {event} e The raw browser event
32381          */
32382         "dragout" : true,
32383         /**
32384          * @event rowclass
32385          * Fires when a row is rendered, so you can change add a style to it.
32386          * @param {GridView} gridview   The grid view
32387          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32388          */
32389         'rowclass' : true,
32390
32391         /**
32392          * @event render
32393          * Fires when the grid is rendered
32394          * @param {Grid} grid
32395          */
32396         'render' : true
32397     });
32398
32399     Roo.grid.Grid.superclass.constructor.call(this);
32400 };
32401 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32402     
32403     /**
32404      * @cfg {String} ddGroup - drag drop group.
32405      */
32406       /**
32407      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
32408      */
32409
32410     /**
32411      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32412      */
32413     minColumnWidth : 25,
32414
32415     /**
32416      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32417      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32418      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32419      */
32420     autoSizeColumns : false,
32421
32422     /**
32423      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32424      */
32425     autoSizeHeaders : true,
32426
32427     /**
32428      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32429      */
32430     monitorWindowResize : true,
32431
32432     /**
32433      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32434      * rows measured to get a columns size. Default is 0 (all rows).
32435      */
32436     maxRowsToMeasure : 0,
32437
32438     /**
32439      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32440      */
32441     trackMouseOver : true,
32442
32443     /**
32444     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32445     */
32446       /**
32447     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
32448     */
32449     
32450     /**
32451     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32452     */
32453     enableDragDrop : false,
32454     
32455     /**
32456     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32457     */
32458     enableColumnMove : true,
32459     
32460     /**
32461     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32462     */
32463     enableColumnHide : true,
32464     
32465     /**
32466     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32467     */
32468     enableRowHeightSync : false,
32469     
32470     /**
32471     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32472     */
32473     stripeRows : true,
32474     
32475     /**
32476     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32477     */
32478     autoHeight : false,
32479
32480     /**
32481      * @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.
32482      */
32483     autoExpandColumn : false,
32484
32485     /**
32486     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32487     * Default is 50.
32488     */
32489     autoExpandMin : 50,
32490
32491     /**
32492     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32493     */
32494     autoExpandMax : 1000,
32495
32496     /**
32497     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32498     */
32499     view : null,
32500
32501     /**
32502     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32503     */
32504     loadMask : false,
32505     /**
32506     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32507     */
32508     dropTarget: false,
32509     
32510    
32511     
32512     // private
32513     rendered : false,
32514
32515     /**
32516     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32517     * of a fixed width. Default is false.
32518     */
32519     /**
32520     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32521     */
32522     
32523     
32524     /**
32525     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32526     * %0 is replaced with the number of selected rows.
32527     */
32528     ddText : "{0} selected row{1}",
32529     
32530     
32531     /**
32532      * Called once after all setup has been completed and the grid is ready to be rendered.
32533      * @return {Roo.grid.Grid} this
32534      */
32535     render : function()
32536     {
32537         var c = this.container;
32538         // try to detect autoHeight/width mode
32539         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32540             this.autoHeight = true;
32541         }
32542         var view = this.getView();
32543         view.init(this);
32544
32545         c.on("click", this.onClick, this);
32546         c.on("dblclick", this.onDblClick, this);
32547         c.on("contextmenu", this.onContextMenu, this);
32548         c.on("keydown", this.onKeyDown, this);
32549         if (Roo.isTouch) {
32550             c.on("touchstart", this.onTouchStart, this);
32551         }
32552
32553         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32554
32555         this.getSelectionModel().init(this);
32556
32557         view.render();
32558
32559         if(this.loadMask){
32560             this.loadMask = new Roo.LoadMask(this.container,
32561                     Roo.apply({store:this.dataSource}, this.loadMask));
32562         }
32563         
32564         
32565         if (this.toolbar && this.toolbar.xtype) {
32566             this.toolbar.container = this.getView().getHeaderPanel(true);
32567             this.toolbar = new Roo.Toolbar(this.toolbar);
32568         }
32569         if (this.footer && this.footer.xtype) {
32570             this.footer.dataSource = this.getDataSource();
32571             this.footer.container = this.getView().getFooterPanel(true);
32572             this.footer = Roo.factory(this.footer, Roo);
32573         }
32574         if (this.dropTarget && this.dropTarget.xtype) {
32575             delete this.dropTarget.xtype;
32576             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32577         }
32578         
32579         
32580         this.rendered = true;
32581         this.fireEvent('render', this);
32582         return this;
32583     },
32584
32585     /**
32586      * Reconfigures the grid to use a different Store and Column Model.
32587      * The View will be bound to the new objects and refreshed.
32588      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32589      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32590      */
32591     reconfigure : function(dataSource, colModel){
32592         if(this.loadMask){
32593             this.loadMask.destroy();
32594             this.loadMask = new Roo.LoadMask(this.container,
32595                     Roo.apply({store:dataSource}, this.loadMask));
32596         }
32597         this.view.bind(dataSource, colModel);
32598         this.dataSource = dataSource;
32599         this.colModel = colModel;
32600         this.view.refresh(true);
32601     },
32602     /**
32603      * addColumns
32604      * Add's a column, default at the end..
32605      
32606      * @param {int} position to add (default end)
32607      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
32608      */
32609     addColumns : function(pos, ar)
32610     {
32611         
32612         for (var i =0;i< ar.length;i++) {
32613             var cfg = ar[i];
32614             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32615             this.cm.lookup[cfg.id] = cfg;
32616         }
32617         
32618         
32619         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32620             pos = this.cm.config.length; //this.cm.config.push(cfg);
32621         } 
32622         pos = Math.max(0,pos);
32623         ar.unshift(0);
32624         ar.unshift(pos);
32625         this.cm.config.splice.apply(this.cm.config, ar);
32626         
32627         
32628         
32629         this.view.generateRules(this.cm);
32630         this.view.refresh(true);
32631         
32632     },
32633     
32634     
32635     
32636     
32637     // private
32638     onKeyDown : function(e){
32639         this.fireEvent("keydown", e);
32640     },
32641
32642     /**
32643      * Destroy this grid.
32644      * @param {Boolean} removeEl True to remove the element
32645      */
32646     destroy : function(removeEl, keepListeners){
32647         if(this.loadMask){
32648             this.loadMask.destroy();
32649         }
32650         var c = this.container;
32651         c.removeAllListeners();
32652         this.view.destroy();
32653         this.colModel.purgeListeners();
32654         if(!keepListeners){
32655             this.purgeListeners();
32656         }
32657         c.update("");
32658         if(removeEl === true){
32659             c.remove();
32660         }
32661     },
32662
32663     // private
32664     processEvent : function(name, e){
32665         // does this fire select???
32666         //Roo.log('grid:processEvent '  + name);
32667         
32668         if (name != 'touchstart' ) {
32669             this.fireEvent(name, e);    
32670         }
32671         
32672         var t = e.getTarget();
32673         var v = this.view;
32674         var header = v.findHeaderIndex(t);
32675         if(header !== false){
32676             var ename = name == 'touchstart' ? 'click' : name;
32677              
32678             this.fireEvent("header" + ename, this, header, e);
32679         }else{
32680             var row = v.findRowIndex(t);
32681             var cell = v.findCellIndex(t);
32682             if (name == 'touchstart') {
32683                 // first touch is always a click.
32684                 // hopefull this happens after selection is updated.?
32685                 name = false;
32686                 
32687                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32688                     var cs = this.selModel.getSelectedCell();
32689                     if (row == cs[0] && cell == cs[1]){
32690                         name = 'dblclick';
32691                     }
32692                 }
32693                 if (typeof(this.selModel.getSelections) != 'undefined') {
32694                     var cs = this.selModel.getSelections();
32695                     var ds = this.dataSource;
32696                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32697                         name = 'dblclick';
32698                     }
32699                 }
32700                 if (!name) {
32701                     return;
32702                 }
32703             }
32704             
32705             
32706             if(row !== false){
32707                 this.fireEvent("row" + name, this, row, e);
32708                 if(cell !== false){
32709                     this.fireEvent("cell" + name, this, row, cell, e);
32710                 }
32711             }
32712         }
32713     },
32714
32715     // private
32716     onClick : function(e){
32717         this.processEvent("click", e);
32718     },
32719    // private
32720     onTouchStart : function(e){
32721         this.processEvent("touchstart", e);
32722     },
32723
32724     // private
32725     onContextMenu : function(e, t){
32726         this.processEvent("contextmenu", e);
32727     },
32728
32729     // private
32730     onDblClick : function(e){
32731         this.processEvent("dblclick", e);
32732     },
32733
32734     // private
32735     walkCells : function(row, col, step, fn, scope){
32736         var cm = this.colModel, clen = cm.getColumnCount();
32737         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32738         if(step < 0){
32739             if(col < 0){
32740                 row--;
32741                 first = false;
32742             }
32743             while(row >= 0){
32744                 if(!first){
32745                     col = clen-1;
32746                 }
32747                 first = false;
32748                 while(col >= 0){
32749                     if(fn.call(scope || this, row, col, cm) === true){
32750                         return [row, col];
32751                     }
32752                     col--;
32753                 }
32754                 row--;
32755             }
32756         } else {
32757             if(col >= clen){
32758                 row++;
32759                 first = false;
32760             }
32761             while(row < rlen){
32762                 if(!first){
32763                     col = 0;
32764                 }
32765                 first = false;
32766                 while(col < clen){
32767                     if(fn.call(scope || this, row, col, cm) === true){
32768                         return [row, col];
32769                     }
32770                     col++;
32771                 }
32772                 row++;
32773             }
32774         }
32775         return null;
32776     },
32777
32778     // private
32779     getSelections : function(){
32780         return this.selModel.getSelections();
32781     },
32782
32783     /**
32784      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32785      * but if manual update is required this method will initiate it.
32786      */
32787     autoSize : function(){
32788         if(this.rendered){
32789             this.view.layout();
32790             if(this.view.adjustForScroll){
32791                 this.view.adjustForScroll();
32792             }
32793         }
32794     },
32795
32796     /**
32797      * Returns the grid's underlying element.
32798      * @return {Element} The element
32799      */
32800     getGridEl : function(){
32801         return this.container;
32802     },
32803
32804     // private for compatibility, overridden by editor grid
32805     stopEditing : function(){},
32806
32807     /**
32808      * Returns the grid's SelectionModel.
32809      * @return {SelectionModel}
32810      */
32811     getSelectionModel : function(){
32812         if(!this.selModel){
32813             this.selModel = new Roo.grid.RowSelectionModel();
32814         }
32815         return this.selModel;
32816     },
32817
32818     /**
32819      * Returns the grid's DataSource.
32820      * @return {DataSource}
32821      */
32822     getDataSource : function(){
32823         return this.dataSource;
32824     },
32825
32826     /**
32827      * Returns the grid's ColumnModel.
32828      * @return {ColumnModel}
32829      */
32830     getColumnModel : function(){
32831         return this.colModel;
32832     },
32833
32834     /**
32835      * Returns the grid's GridView object.
32836      * @return {GridView}
32837      */
32838     getView : function(){
32839         if(!this.view){
32840             this.view = new Roo.grid.GridView(this.viewConfig);
32841             this.relayEvents(this.view, [
32842                 "beforerowremoved", "beforerowsinserted",
32843                 "beforerefresh", "rowremoved",
32844                 "rowsinserted", "rowupdated" ,"refresh"
32845             ]);
32846         }
32847         return this.view;
32848     },
32849     /**
32850      * Called to get grid's drag proxy text, by default returns this.ddText.
32851      * Override this to put something different in the dragged text.
32852      * @return {String}
32853      */
32854     getDragDropText : function(){
32855         var count = this.selModel.getCount();
32856         return String.format(this.ddText, count, count == 1 ? '' : 's');
32857     }
32858 });
32859 /*
32860  * Based on:
32861  * Ext JS Library 1.1.1
32862  * Copyright(c) 2006-2007, Ext JS, LLC.
32863  *
32864  * Originally Released Under LGPL - original licence link has changed is not relivant.
32865  *
32866  * Fork - LGPL
32867  * <script type="text/javascript">
32868  */
32869  
32870 Roo.grid.AbstractGridView = function(){
32871         this.grid = null;
32872         
32873         this.events = {
32874             "beforerowremoved" : true,
32875             "beforerowsinserted" : true,
32876             "beforerefresh" : true,
32877             "rowremoved" : true,
32878             "rowsinserted" : true,
32879             "rowupdated" : true,
32880             "refresh" : true
32881         };
32882     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32883 };
32884
32885 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32886     rowClass : "x-grid-row",
32887     cellClass : "x-grid-cell",
32888     tdClass : "x-grid-td",
32889     hdClass : "x-grid-hd",
32890     splitClass : "x-grid-hd-split",
32891     
32892     init: function(grid){
32893         this.grid = grid;
32894                 var cid = this.grid.getGridEl().id;
32895         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32896         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32897         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32898         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32899         },
32900         
32901     getColumnRenderers : function(){
32902         var renderers = [];
32903         var cm = this.grid.colModel;
32904         var colCount = cm.getColumnCount();
32905         for(var i = 0; i < colCount; i++){
32906             renderers[i] = cm.getRenderer(i);
32907         }
32908         return renderers;
32909     },
32910     
32911     getColumnIds : function(){
32912         var ids = [];
32913         var cm = this.grid.colModel;
32914         var colCount = cm.getColumnCount();
32915         for(var i = 0; i < colCount; i++){
32916             ids[i] = cm.getColumnId(i);
32917         }
32918         return ids;
32919     },
32920     
32921     getDataIndexes : function(){
32922         if(!this.indexMap){
32923             this.indexMap = this.buildIndexMap();
32924         }
32925         return this.indexMap.colToData;
32926     },
32927     
32928     getColumnIndexByDataIndex : function(dataIndex){
32929         if(!this.indexMap){
32930             this.indexMap = this.buildIndexMap();
32931         }
32932         return this.indexMap.dataToCol[dataIndex];
32933     },
32934     
32935     /**
32936      * Set a css style for a column dynamically. 
32937      * @param {Number} colIndex The index of the column
32938      * @param {String} name The css property name
32939      * @param {String} value The css value
32940      */
32941     setCSSStyle : function(colIndex, name, value){
32942         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32943         Roo.util.CSS.updateRule(selector, name, value);
32944     },
32945     
32946     generateRules : function(cm){
32947         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32948         Roo.util.CSS.removeStyleSheet(rulesId);
32949         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32950             var cid = cm.getColumnId(i);
32951             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32952                          this.tdSelector, cid, " {\n}\n",
32953                          this.hdSelector, cid, " {\n}\n",
32954                          this.splitSelector, cid, " {\n}\n");
32955         }
32956         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32957     }
32958 });/*
32959  * Based on:
32960  * Ext JS Library 1.1.1
32961  * Copyright(c) 2006-2007, Ext JS, LLC.
32962  *
32963  * Originally Released Under LGPL - original licence link has changed is not relivant.
32964  *
32965  * Fork - LGPL
32966  * <script type="text/javascript">
32967  */
32968
32969 // private
32970 // This is a support class used internally by the Grid components
32971 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32972     this.grid = grid;
32973     this.view = grid.getView();
32974     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32975     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32976     if(hd2){
32977         this.setHandleElId(Roo.id(hd));
32978         this.setOuterHandleElId(Roo.id(hd2));
32979     }
32980     this.scroll = false;
32981 };
32982 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32983     maxDragWidth: 120,
32984     getDragData : function(e){
32985         var t = Roo.lib.Event.getTarget(e);
32986         var h = this.view.findHeaderCell(t);
32987         if(h){
32988             return {ddel: h.firstChild, header:h};
32989         }
32990         return false;
32991     },
32992
32993     onInitDrag : function(e){
32994         this.view.headersDisabled = true;
32995         var clone = this.dragData.ddel.cloneNode(true);
32996         clone.id = Roo.id();
32997         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32998         this.proxy.update(clone);
32999         return true;
33000     },
33001
33002     afterValidDrop : function(){
33003         var v = this.view;
33004         setTimeout(function(){
33005             v.headersDisabled = false;
33006         }, 50);
33007     },
33008
33009     afterInvalidDrop : function(){
33010         var v = this.view;
33011         setTimeout(function(){
33012             v.headersDisabled = false;
33013         }, 50);
33014     }
33015 });
33016 /*
33017  * Based on:
33018  * Ext JS Library 1.1.1
33019  * Copyright(c) 2006-2007, Ext JS, LLC.
33020  *
33021  * Originally Released Under LGPL - original licence link has changed is not relivant.
33022  *
33023  * Fork - LGPL
33024  * <script type="text/javascript">
33025  */
33026 // private
33027 // This is a support class used internally by the Grid components
33028 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33029     this.grid = grid;
33030     this.view = grid.getView();
33031     // split the proxies so they don't interfere with mouse events
33032     this.proxyTop = Roo.DomHelper.append(document.body, {
33033         cls:"col-move-top", html:"&#160;"
33034     }, true);
33035     this.proxyBottom = Roo.DomHelper.append(document.body, {
33036         cls:"col-move-bottom", html:"&#160;"
33037     }, true);
33038     this.proxyTop.hide = this.proxyBottom.hide = function(){
33039         this.setLeftTop(-100,-100);
33040         this.setStyle("visibility", "hidden");
33041     };
33042     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33043     // temporarily disabled
33044     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33045     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33046 };
33047 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33048     proxyOffsets : [-4, -9],
33049     fly: Roo.Element.fly,
33050
33051     getTargetFromEvent : function(e){
33052         var t = Roo.lib.Event.getTarget(e);
33053         var cindex = this.view.findCellIndex(t);
33054         if(cindex !== false){
33055             return this.view.getHeaderCell(cindex);
33056         }
33057         return null;
33058     },
33059
33060     nextVisible : function(h){
33061         var v = this.view, cm = this.grid.colModel;
33062         h = h.nextSibling;
33063         while(h){
33064             if(!cm.isHidden(v.getCellIndex(h))){
33065                 return h;
33066             }
33067             h = h.nextSibling;
33068         }
33069         return null;
33070     },
33071
33072     prevVisible : function(h){
33073         var v = this.view, cm = this.grid.colModel;
33074         h = h.prevSibling;
33075         while(h){
33076             if(!cm.isHidden(v.getCellIndex(h))){
33077                 return h;
33078             }
33079             h = h.prevSibling;
33080         }
33081         return null;
33082     },
33083
33084     positionIndicator : function(h, n, e){
33085         var x = Roo.lib.Event.getPageX(e);
33086         var r = Roo.lib.Dom.getRegion(n.firstChild);
33087         var px, pt, py = r.top + this.proxyOffsets[1];
33088         if((r.right - x) <= (r.right-r.left)/2){
33089             px = r.right+this.view.borderWidth;
33090             pt = "after";
33091         }else{
33092             px = r.left;
33093             pt = "before";
33094         }
33095         var oldIndex = this.view.getCellIndex(h);
33096         var newIndex = this.view.getCellIndex(n);
33097
33098         if(this.grid.colModel.isFixed(newIndex)){
33099             return false;
33100         }
33101
33102         var locked = this.grid.colModel.isLocked(newIndex);
33103
33104         if(pt == "after"){
33105             newIndex++;
33106         }
33107         if(oldIndex < newIndex){
33108             newIndex--;
33109         }
33110         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33111             return false;
33112         }
33113         px +=  this.proxyOffsets[0];
33114         this.proxyTop.setLeftTop(px, py);
33115         this.proxyTop.show();
33116         if(!this.bottomOffset){
33117             this.bottomOffset = this.view.mainHd.getHeight();
33118         }
33119         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33120         this.proxyBottom.show();
33121         return pt;
33122     },
33123
33124     onNodeEnter : function(n, dd, e, data){
33125         if(data.header != n){
33126             this.positionIndicator(data.header, n, e);
33127         }
33128     },
33129
33130     onNodeOver : function(n, dd, e, data){
33131         var result = false;
33132         if(data.header != n){
33133             result = this.positionIndicator(data.header, n, e);
33134         }
33135         if(!result){
33136             this.proxyTop.hide();
33137             this.proxyBottom.hide();
33138         }
33139         return result ? this.dropAllowed : this.dropNotAllowed;
33140     },
33141
33142     onNodeOut : function(n, dd, e, data){
33143         this.proxyTop.hide();
33144         this.proxyBottom.hide();
33145     },
33146
33147     onNodeDrop : function(n, dd, e, data){
33148         var h = data.header;
33149         if(h != n){
33150             var cm = this.grid.colModel;
33151             var x = Roo.lib.Event.getPageX(e);
33152             var r = Roo.lib.Dom.getRegion(n.firstChild);
33153             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33154             var oldIndex = this.view.getCellIndex(h);
33155             var newIndex = this.view.getCellIndex(n);
33156             var locked = cm.isLocked(newIndex);
33157             if(pt == "after"){
33158                 newIndex++;
33159             }
33160             if(oldIndex < newIndex){
33161                 newIndex--;
33162             }
33163             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33164                 return false;
33165             }
33166             cm.setLocked(oldIndex, locked, true);
33167             cm.moveColumn(oldIndex, newIndex);
33168             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33169             return true;
33170         }
33171         return false;
33172     }
33173 });
33174 /*
33175  * Based on:
33176  * Ext JS Library 1.1.1
33177  * Copyright(c) 2006-2007, Ext JS, LLC.
33178  *
33179  * Originally Released Under LGPL - original licence link has changed is not relivant.
33180  *
33181  * Fork - LGPL
33182  * <script type="text/javascript">
33183  */
33184   
33185 /**
33186  * @class Roo.grid.GridView
33187  * @extends Roo.util.Observable
33188  *
33189  * @constructor
33190  * @param {Object} config
33191  */
33192 Roo.grid.GridView = function(config){
33193     Roo.grid.GridView.superclass.constructor.call(this);
33194     this.el = null;
33195
33196     Roo.apply(this, config);
33197 };
33198
33199 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33200
33201     unselectable :  'unselectable="on"',
33202     unselectableCls :  'x-unselectable',
33203     
33204     
33205     rowClass : "x-grid-row",
33206
33207     cellClass : "x-grid-col",
33208
33209     tdClass : "x-grid-td",
33210
33211     hdClass : "x-grid-hd",
33212
33213     splitClass : "x-grid-split",
33214
33215     sortClasses : ["sort-asc", "sort-desc"],
33216
33217     enableMoveAnim : false,
33218
33219     hlColor: "C3DAF9",
33220
33221     dh : Roo.DomHelper,
33222
33223     fly : Roo.Element.fly,
33224
33225     css : Roo.util.CSS,
33226
33227     borderWidth: 1,
33228
33229     splitOffset: 3,
33230
33231     scrollIncrement : 22,
33232
33233     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33234
33235     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33236
33237     bind : function(ds, cm){
33238         if(this.ds){
33239             this.ds.un("load", this.onLoad, this);
33240             this.ds.un("datachanged", this.onDataChange, this);
33241             this.ds.un("add", this.onAdd, this);
33242             this.ds.un("remove", this.onRemove, this);
33243             this.ds.un("update", this.onUpdate, this);
33244             this.ds.un("clear", this.onClear, this);
33245         }
33246         if(ds){
33247             ds.on("load", this.onLoad, this);
33248             ds.on("datachanged", this.onDataChange, this);
33249             ds.on("add", this.onAdd, this);
33250             ds.on("remove", this.onRemove, this);
33251             ds.on("update", this.onUpdate, this);
33252             ds.on("clear", this.onClear, this);
33253         }
33254         this.ds = ds;
33255
33256         if(this.cm){
33257             this.cm.un("widthchange", this.onColWidthChange, this);
33258             this.cm.un("headerchange", this.onHeaderChange, this);
33259             this.cm.un("hiddenchange", this.onHiddenChange, this);
33260             this.cm.un("columnmoved", this.onColumnMove, this);
33261             this.cm.un("columnlockchange", this.onColumnLock, this);
33262         }
33263         if(cm){
33264             this.generateRules(cm);
33265             cm.on("widthchange", this.onColWidthChange, this);
33266             cm.on("headerchange", this.onHeaderChange, this);
33267             cm.on("hiddenchange", this.onHiddenChange, this);
33268             cm.on("columnmoved", this.onColumnMove, this);
33269             cm.on("columnlockchange", this.onColumnLock, this);
33270         }
33271         this.cm = cm;
33272     },
33273
33274     init: function(grid){
33275         Roo.grid.GridView.superclass.init.call(this, grid);
33276
33277         this.bind(grid.dataSource, grid.colModel);
33278
33279         grid.on("headerclick", this.handleHeaderClick, this);
33280
33281         if(grid.trackMouseOver){
33282             grid.on("mouseover", this.onRowOver, this);
33283             grid.on("mouseout", this.onRowOut, this);
33284         }
33285         grid.cancelTextSelection = function(){};
33286         this.gridId = grid.id;
33287
33288         var tpls = this.templates || {};
33289
33290         if(!tpls.master){
33291             tpls.master = new Roo.Template(
33292                '<div class="x-grid" hidefocus="true">',
33293                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33294                   '<div class="x-grid-topbar"></div>',
33295                   '<div class="x-grid-scroller"><div></div></div>',
33296                   '<div class="x-grid-locked">',
33297                       '<div class="x-grid-header">{lockedHeader}</div>',
33298                       '<div class="x-grid-body">{lockedBody}</div>',
33299                   "</div>",
33300                   '<div class="x-grid-viewport">',
33301                       '<div class="x-grid-header">{header}</div>',
33302                       '<div class="x-grid-body">{body}</div>',
33303                   "</div>",
33304                   '<div class="x-grid-bottombar"></div>',
33305                  
33306                   '<div class="x-grid-resize-proxy">&#160;</div>',
33307                "</div>"
33308             );
33309             tpls.master.disableformats = true;
33310         }
33311
33312         if(!tpls.header){
33313             tpls.header = new Roo.Template(
33314                '<table border="0" cellspacing="0" cellpadding="0">',
33315                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33316                "</table>{splits}"
33317             );
33318             tpls.header.disableformats = true;
33319         }
33320         tpls.header.compile();
33321
33322         if(!tpls.hcell){
33323             tpls.hcell = new Roo.Template(
33324                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33325                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33326                 "</div></td>"
33327              );
33328              tpls.hcell.disableFormats = true;
33329         }
33330         tpls.hcell.compile();
33331
33332         if(!tpls.hsplit){
33333             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33334                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33335             tpls.hsplit.disableFormats = true;
33336         }
33337         tpls.hsplit.compile();
33338
33339         if(!tpls.body){
33340             tpls.body = new Roo.Template(
33341                '<table border="0" cellspacing="0" cellpadding="0">',
33342                "<tbody>{rows}</tbody>",
33343                "</table>"
33344             );
33345             tpls.body.disableFormats = true;
33346         }
33347         tpls.body.compile();
33348
33349         if(!tpls.row){
33350             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33351             tpls.row.disableFormats = true;
33352         }
33353         tpls.row.compile();
33354
33355         if(!tpls.cell){
33356             tpls.cell = new Roo.Template(
33357                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33358                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33359                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33360                 "</td>"
33361             );
33362             tpls.cell.disableFormats = true;
33363         }
33364         tpls.cell.compile();
33365
33366         this.templates = tpls;
33367     },
33368
33369     // remap these for backwards compat
33370     onColWidthChange : function(){
33371         this.updateColumns.apply(this, arguments);
33372     },
33373     onHeaderChange : function(){
33374         this.updateHeaders.apply(this, arguments);
33375     }, 
33376     onHiddenChange : function(){
33377         this.handleHiddenChange.apply(this, arguments);
33378     },
33379     onColumnMove : function(){
33380         this.handleColumnMove.apply(this, arguments);
33381     },
33382     onColumnLock : function(){
33383         this.handleLockChange.apply(this, arguments);
33384     },
33385
33386     onDataChange : function(){
33387         this.refresh();
33388         this.updateHeaderSortState();
33389     },
33390
33391     onClear : function(){
33392         this.refresh();
33393     },
33394
33395     onUpdate : function(ds, record){
33396         this.refreshRow(record);
33397     },
33398
33399     refreshRow : function(record){
33400         var ds = this.ds, index;
33401         if(typeof record == 'number'){
33402             index = record;
33403             record = ds.getAt(index);
33404         }else{
33405             index = ds.indexOf(record);
33406         }
33407         this.insertRows(ds, index, index, true);
33408         this.onRemove(ds, record, index+1, true);
33409         this.syncRowHeights(index, index);
33410         this.layout();
33411         this.fireEvent("rowupdated", this, index, record);
33412     },
33413
33414     onAdd : function(ds, records, index){
33415         this.insertRows(ds, index, index + (records.length-1));
33416     },
33417
33418     onRemove : function(ds, record, index, isUpdate){
33419         if(isUpdate !== true){
33420             this.fireEvent("beforerowremoved", this, index, record);
33421         }
33422         var bt = this.getBodyTable(), lt = this.getLockedTable();
33423         if(bt.rows[index]){
33424             bt.firstChild.removeChild(bt.rows[index]);
33425         }
33426         if(lt.rows[index]){
33427             lt.firstChild.removeChild(lt.rows[index]);
33428         }
33429         if(isUpdate !== true){
33430             this.stripeRows(index);
33431             this.syncRowHeights(index, index);
33432             this.layout();
33433             this.fireEvent("rowremoved", this, index, record);
33434         }
33435     },
33436
33437     onLoad : function(){
33438         this.scrollToTop();
33439     },
33440
33441     /**
33442      * Scrolls the grid to the top
33443      */
33444     scrollToTop : function(){
33445         if(this.scroller){
33446             this.scroller.dom.scrollTop = 0;
33447             this.syncScroll();
33448         }
33449     },
33450
33451     /**
33452      * Gets a panel in the header of the grid that can be used for toolbars etc.
33453      * After modifying the contents of this panel a call to grid.autoSize() may be
33454      * required to register any changes in size.
33455      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33456      * @return Roo.Element
33457      */
33458     getHeaderPanel : function(doShow){
33459         if(doShow){
33460             this.headerPanel.show();
33461         }
33462         return this.headerPanel;
33463     },
33464
33465     /**
33466      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33467      * After modifying the contents of this panel a call to grid.autoSize() may be
33468      * required to register any changes in size.
33469      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33470      * @return Roo.Element
33471      */
33472     getFooterPanel : function(doShow){
33473         if(doShow){
33474             this.footerPanel.show();
33475         }
33476         return this.footerPanel;
33477     },
33478
33479     initElements : function(){
33480         var E = Roo.Element;
33481         var el = this.grid.getGridEl().dom.firstChild;
33482         var cs = el.childNodes;
33483
33484         this.el = new E(el);
33485         
33486          this.focusEl = new E(el.firstChild);
33487         this.focusEl.swallowEvent("click", true);
33488         
33489         this.headerPanel = new E(cs[1]);
33490         this.headerPanel.enableDisplayMode("block");
33491
33492         this.scroller = new E(cs[2]);
33493         this.scrollSizer = new E(this.scroller.dom.firstChild);
33494
33495         this.lockedWrap = new E(cs[3]);
33496         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33497         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33498
33499         this.mainWrap = new E(cs[4]);
33500         this.mainHd = new E(this.mainWrap.dom.firstChild);
33501         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33502
33503         this.footerPanel = new E(cs[5]);
33504         this.footerPanel.enableDisplayMode("block");
33505
33506         this.resizeProxy = new E(cs[6]);
33507
33508         this.headerSelector = String.format(
33509            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33510            this.lockedHd.id, this.mainHd.id
33511         );
33512
33513         this.splitterSelector = String.format(
33514            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33515            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33516         );
33517     },
33518     idToCssName : function(s)
33519     {
33520         return s.replace(/[^a-z0-9]+/ig, '-');
33521     },
33522
33523     getHeaderCell : function(index){
33524         return Roo.DomQuery.select(this.headerSelector)[index];
33525     },
33526
33527     getHeaderCellMeasure : function(index){
33528         return this.getHeaderCell(index).firstChild;
33529     },
33530
33531     getHeaderCellText : function(index){
33532         return this.getHeaderCell(index).firstChild.firstChild;
33533     },
33534
33535     getLockedTable : function(){
33536         return this.lockedBody.dom.firstChild;
33537     },
33538
33539     getBodyTable : function(){
33540         return this.mainBody.dom.firstChild;
33541     },
33542
33543     getLockedRow : function(index){
33544         return this.getLockedTable().rows[index];
33545     },
33546
33547     getRow : function(index){
33548         return this.getBodyTable().rows[index];
33549     },
33550
33551     getRowComposite : function(index){
33552         if(!this.rowEl){
33553             this.rowEl = new Roo.CompositeElementLite();
33554         }
33555         var els = [], lrow, mrow;
33556         if(lrow = this.getLockedRow(index)){
33557             els.push(lrow);
33558         }
33559         if(mrow = this.getRow(index)){
33560             els.push(mrow);
33561         }
33562         this.rowEl.elements = els;
33563         return this.rowEl;
33564     },
33565     /**
33566      * Gets the 'td' of the cell
33567      * 
33568      * @param {Integer} rowIndex row to select
33569      * @param {Integer} colIndex column to select
33570      * 
33571      * @return {Object} 
33572      */
33573     getCell : function(rowIndex, colIndex){
33574         var locked = this.cm.getLockedCount();
33575         var source;
33576         if(colIndex < locked){
33577             source = this.lockedBody.dom.firstChild;
33578         }else{
33579             source = this.mainBody.dom.firstChild;
33580             colIndex -= locked;
33581         }
33582         return source.rows[rowIndex].childNodes[colIndex];
33583     },
33584
33585     getCellText : function(rowIndex, colIndex){
33586         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33587     },
33588
33589     getCellBox : function(cell){
33590         var b = this.fly(cell).getBox();
33591         if(Roo.isOpera){ // opera fails to report the Y
33592             b.y = cell.offsetTop + this.mainBody.getY();
33593         }
33594         return b;
33595     },
33596
33597     getCellIndex : function(cell){
33598         var id = String(cell.className).match(this.cellRE);
33599         if(id){
33600             return parseInt(id[1], 10);
33601         }
33602         return 0;
33603     },
33604
33605     findHeaderIndex : function(n){
33606         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33607         return r ? this.getCellIndex(r) : false;
33608     },
33609
33610     findHeaderCell : function(n){
33611         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33612         return r ? r : false;
33613     },
33614
33615     findRowIndex : function(n){
33616         if(!n){
33617             return false;
33618         }
33619         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33620         return r ? r.rowIndex : false;
33621     },
33622
33623     findCellIndex : function(node){
33624         var stop = this.el.dom;
33625         while(node && node != stop){
33626             if(this.findRE.test(node.className)){
33627                 return this.getCellIndex(node);
33628             }
33629             node = node.parentNode;
33630         }
33631         return false;
33632     },
33633
33634     getColumnId : function(index){
33635         return this.cm.getColumnId(index);
33636     },
33637
33638     getSplitters : function()
33639     {
33640         if(this.splitterSelector){
33641            return Roo.DomQuery.select(this.splitterSelector);
33642         }else{
33643             return null;
33644       }
33645     },
33646
33647     getSplitter : function(index){
33648         return this.getSplitters()[index];
33649     },
33650
33651     onRowOver : function(e, t){
33652         var row;
33653         if((row = this.findRowIndex(t)) !== false){
33654             this.getRowComposite(row).addClass("x-grid-row-over");
33655         }
33656     },
33657
33658     onRowOut : function(e, t){
33659         var row;
33660         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33661             this.getRowComposite(row).removeClass("x-grid-row-over");
33662         }
33663     },
33664
33665     renderHeaders : function(){
33666         var cm = this.cm;
33667         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33668         var cb = [], lb = [], sb = [], lsb = [], p = {};
33669         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33670             p.cellId = "x-grid-hd-0-" + i;
33671             p.splitId = "x-grid-csplit-0-" + i;
33672             p.id = cm.getColumnId(i);
33673             p.value = cm.getColumnHeader(i) || "";
33674             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33675             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33676             if(!cm.isLocked(i)){
33677                 cb[cb.length] = ct.apply(p);
33678                 sb[sb.length] = st.apply(p);
33679             }else{
33680                 lb[lb.length] = ct.apply(p);
33681                 lsb[lsb.length] = st.apply(p);
33682             }
33683         }
33684         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33685                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33686     },
33687
33688     updateHeaders : function(){
33689         var html = this.renderHeaders();
33690         this.lockedHd.update(html[0]);
33691         this.mainHd.update(html[1]);
33692     },
33693
33694     /**
33695      * Focuses the specified row.
33696      * @param {Number} row The row index
33697      */
33698     focusRow : function(row)
33699     {
33700         //Roo.log('GridView.focusRow');
33701         var x = this.scroller.dom.scrollLeft;
33702         this.focusCell(row, 0, false);
33703         this.scroller.dom.scrollLeft = x;
33704     },
33705
33706     /**
33707      * Focuses the specified cell.
33708      * @param {Number} row The row index
33709      * @param {Number} col The column index
33710      * @param {Boolean} hscroll false to disable horizontal scrolling
33711      */
33712     focusCell : function(row, col, hscroll)
33713     {
33714         //Roo.log('GridView.focusCell');
33715         var el = this.ensureVisible(row, col, hscroll);
33716         this.focusEl.alignTo(el, "tl-tl");
33717         if(Roo.isGecko){
33718             this.focusEl.focus();
33719         }else{
33720             this.focusEl.focus.defer(1, this.focusEl);
33721         }
33722     },
33723
33724     /**
33725      * Scrolls the specified cell into view
33726      * @param {Number} row The row index
33727      * @param {Number} col The column index
33728      * @param {Boolean} hscroll false to disable horizontal scrolling
33729      */
33730     ensureVisible : function(row, col, hscroll)
33731     {
33732         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33733         //return null; //disable for testing.
33734         if(typeof row != "number"){
33735             row = row.rowIndex;
33736         }
33737         if(row < 0 && row >= this.ds.getCount()){
33738             return  null;
33739         }
33740         col = (col !== undefined ? col : 0);
33741         var cm = this.grid.colModel;
33742         while(cm.isHidden(col)){
33743             col++;
33744         }
33745
33746         var el = this.getCell(row, col);
33747         if(!el){
33748             return null;
33749         }
33750         var c = this.scroller.dom;
33751
33752         var ctop = parseInt(el.offsetTop, 10);
33753         var cleft = parseInt(el.offsetLeft, 10);
33754         var cbot = ctop + el.offsetHeight;
33755         var cright = cleft + el.offsetWidth;
33756         
33757         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33758         var stop = parseInt(c.scrollTop, 10);
33759         var sleft = parseInt(c.scrollLeft, 10);
33760         var sbot = stop + ch;
33761         var sright = sleft + c.clientWidth;
33762         /*
33763         Roo.log('GridView.ensureVisible:' +
33764                 ' ctop:' + ctop +
33765                 ' c.clientHeight:' + c.clientHeight +
33766                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33767                 ' stop:' + stop +
33768                 ' cbot:' + cbot +
33769                 ' sbot:' + sbot +
33770                 ' ch:' + ch  
33771                 );
33772         */
33773         if(ctop < stop){
33774             c.scrollTop = ctop;
33775             //Roo.log("set scrolltop to ctop DISABLE?");
33776         }else if(cbot > sbot){
33777             //Roo.log("set scrolltop to cbot-ch");
33778             c.scrollTop = cbot-ch;
33779         }
33780         
33781         if(hscroll !== false){
33782             if(cleft < sleft){
33783                 c.scrollLeft = cleft;
33784             }else if(cright > sright){
33785                 c.scrollLeft = cright-c.clientWidth;
33786             }
33787         }
33788          
33789         return el;
33790     },
33791
33792     updateColumns : function(){
33793         this.grid.stopEditing();
33794         var cm = this.grid.colModel, colIds = this.getColumnIds();
33795         //var totalWidth = cm.getTotalWidth();
33796         var pos = 0;
33797         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33798             //if(cm.isHidden(i)) continue;
33799             var w = cm.getColumnWidth(i);
33800             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33801             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33802         }
33803         this.updateSplitters();
33804     },
33805
33806     generateRules : function(cm){
33807         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33808         Roo.util.CSS.removeStyleSheet(rulesId);
33809         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33810             var cid = cm.getColumnId(i);
33811             var align = '';
33812             if(cm.config[i].align){
33813                 align = 'text-align:'+cm.config[i].align+';';
33814             }
33815             var hidden = '';
33816             if(cm.isHidden(i)){
33817                 hidden = 'display:none;';
33818             }
33819             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33820             ruleBuf.push(
33821                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33822                     this.hdSelector, cid, " {\n", align, width, "}\n",
33823                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33824                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33825         }
33826         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33827     },
33828
33829     updateSplitters : function(){
33830         var cm = this.cm, s = this.getSplitters();
33831         if(s){ // splitters not created yet
33832             var pos = 0, locked = true;
33833             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33834                 if(cm.isHidden(i)) {
33835                     continue;
33836                 }
33837                 var w = cm.getColumnWidth(i); // make sure it's a number
33838                 if(!cm.isLocked(i) && locked){
33839                     pos = 0;
33840                     locked = false;
33841                 }
33842                 pos += w;
33843                 s[i].style.left = (pos-this.splitOffset) + "px";
33844             }
33845         }
33846     },
33847
33848     handleHiddenChange : function(colModel, colIndex, hidden){
33849         if(hidden){
33850             this.hideColumn(colIndex);
33851         }else{
33852             this.unhideColumn(colIndex);
33853         }
33854     },
33855
33856     hideColumn : function(colIndex){
33857         var cid = this.getColumnId(colIndex);
33858         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33859         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33860         if(Roo.isSafari){
33861             this.updateHeaders();
33862         }
33863         this.updateSplitters();
33864         this.layout();
33865     },
33866
33867     unhideColumn : function(colIndex){
33868         var cid = this.getColumnId(colIndex);
33869         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33870         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33871
33872         if(Roo.isSafari){
33873             this.updateHeaders();
33874         }
33875         this.updateSplitters();
33876         this.layout();
33877     },
33878
33879     insertRows : function(dm, firstRow, lastRow, isUpdate){
33880         if(firstRow == 0 && lastRow == dm.getCount()-1){
33881             this.refresh();
33882         }else{
33883             if(!isUpdate){
33884                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33885             }
33886             var s = this.getScrollState();
33887             var markup = this.renderRows(firstRow, lastRow);
33888             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33889             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33890             this.restoreScroll(s);
33891             if(!isUpdate){
33892                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33893                 this.syncRowHeights(firstRow, lastRow);
33894                 this.stripeRows(firstRow);
33895                 this.layout();
33896             }
33897         }
33898     },
33899
33900     bufferRows : function(markup, target, index){
33901         var before = null, trows = target.rows, tbody = target.tBodies[0];
33902         if(index < trows.length){
33903             before = trows[index];
33904         }
33905         var b = document.createElement("div");
33906         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33907         var rows = b.firstChild.rows;
33908         for(var i = 0, len = rows.length; i < len; i++){
33909             if(before){
33910                 tbody.insertBefore(rows[0], before);
33911             }else{
33912                 tbody.appendChild(rows[0]);
33913             }
33914         }
33915         b.innerHTML = "";
33916         b = null;
33917     },
33918
33919     deleteRows : function(dm, firstRow, lastRow){
33920         if(dm.getRowCount()<1){
33921             this.fireEvent("beforerefresh", this);
33922             this.mainBody.update("");
33923             this.lockedBody.update("");
33924             this.fireEvent("refresh", this);
33925         }else{
33926             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33927             var bt = this.getBodyTable();
33928             var tbody = bt.firstChild;
33929             var rows = bt.rows;
33930             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33931                 tbody.removeChild(rows[firstRow]);
33932             }
33933             this.stripeRows(firstRow);
33934             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33935         }
33936     },
33937
33938     updateRows : function(dataSource, firstRow, lastRow){
33939         var s = this.getScrollState();
33940         this.refresh();
33941         this.restoreScroll(s);
33942     },
33943
33944     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33945         if(!noRefresh){
33946            this.refresh();
33947         }
33948         this.updateHeaderSortState();
33949     },
33950
33951     getScrollState : function(){
33952         
33953         var sb = this.scroller.dom;
33954         return {left: sb.scrollLeft, top: sb.scrollTop};
33955     },
33956
33957     stripeRows : function(startRow){
33958         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33959             return;
33960         }
33961         startRow = startRow || 0;
33962         var rows = this.getBodyTable().rows;
33963         var lrows = this.getLockedTable().rows;
33964         var cls = ' x-grid-row-alt ';
33965         for(var i = startRow, len = rows.length; i < len; i++){
33966             var row = rows[i], lrow = lrows[i];
33967             var isAlt = ((i+1) % 2 == 0);
33968             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33969             if(isAlt == hasAlt){
33970                 continue;
33971             }
33972             if(isAlt){
33973                 row.className += " x-grid-row-alt";
33974             }else{
33975                 row.className = row.className.replace("x-grid-row-alt", "");
33976             }
33977             if(lrow){
33978                 lrow.className = row.className;
33979             }
33980         }
33981     },
33982
33983     restoreScroll : function(state){
33984         //Roo.log('GridView.restoreScroll');
33985         var sb = this.scroller.dom;
33986         sb.scrollLeft = state.left;
33987         sb.scrollTop = state.top;
33988         this.syncScroll();
33989     },
33990
33991     syncScroll : function(){
33992         //Roo.log('GridView.syncScroll');
33993         var sb = this.scroller.dom;
33994         var sh = this.mainHd.dom;
33995         var bs = this.mainBody.dom;
33996         var lv = this.lockedBody.dom;
33997         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33998         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33999     },
34000
34001     handleScroll : function(e){
34002         this.syncScroll();
34003         var sb = this.scroller.dom;
34004         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34005         e.stopEvent();
34006     },
34007
34008     handleWheel : function(e){
34009         var d = e.getWheelDelta();
34010         this.scroller.dom.scrollTop -= d*22;
34011         // set this here to prevent jumpy scrolling on large tables
34012         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34013         e.stopEvent();
34014     },
34015
34016     renderRows : function(startRow, endRow){
34017         // pull in all the crap needed to render rows
34018         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34019         var colCount = cm.getColumnCount();
34020
34021         if(ds.getCount() < 1){
34022             return ["", ""];
34023         }
34024
34025         // build a map for all the columns
34026         var cs = [];
34027         for(var i = 0; i < colCount; i++){
34028             var name = cm.getDataIndex(i);
34029             cs[i] = {
34030                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34031                 renderer : cm.getRenderer(i),
34032                 id : cm.getColumnId(i),
34033                 locked : cm.isLocked(i),
34034                 has_editor : cm.isCellEditable(i)
34035             };
34036         }
34037
34038         startRow = startRow || 0;
34039         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34040
34041         // records to render
34042         var rs = ds.getRange(startRow, endRow);
34043
34044         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34045     },
34046
34047     // As much as I hate to duplicate code, this was branched because FireFox really hates
34048     // [].join("") on strings. The performance difference was substantial enough to
34049     // branch this function
34050     doRender : Roo.isGecko ?
34051             function(cs, rs, ds, startRow, colCount, stripe){
34052                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34053                 // buffers
34054                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34055                 
34056                 var hasListener = this.grid.hasListener('rowclass');
34057                 var rowcfg = {};
34058                 for(var j = 0, len = rs.length; j < len; j++){
34059                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34060                     for(var i = 0; i < colCount; i++){
34061                         c = cs[i];
34062                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34063                         p.id = c.id;
34064                         p.css = p.attr = "";
34065                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34066                         if(p.value == undefined || p.value === "") {
34067                             p.value = "&#160;";
34068                         }
34069                         if(c.has_editor){
34070                             p.css += ' x-grid-editable-cell';
34071                         }
34072                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34073                             p.css +=  ' x-grid-dirty-cell';
34074                         }
34075                         var markup = ct.apply(p);
34076                         if(!c.locked){
34077                             cb+= markup;
34078                         }else{
34079                             lcb+= markup;
34080                         }
34081                     }
34082                     var alt = [];
34083                     if(stripe && ((rowIndex+1) % 2 == 0)){
34084                         alt.push("x-grid-row-alt")
34085                     }
34086                     if(r.dirty){
34087                         alt.push(  " x-grid-dirty-row");
34088                     }
34089                     rp.cells = lcb;
34090                     if(this.getRowClass){
34091                         alt.push(this.getRowClass(r, rowIndex));
34092                     }
34093                     if (hasListener) {
34094                         rowcfg = {
34095                              
34096                             record: r,
34097                             rowIndex : rowIndex,
34098                             rowClass : ''
34099                         };
34100                         this.grid.fireEvent('rowclass', this, rowcfg);
34101                         alt.push(rowcfg.rowClass);
34102                     }
34103                     rp.alt = alt.join(" ");
34104                     lbuf+= rt.apply(rp);
34105                     rp.cells = cb;
34106                     buf+=  rt.apply(rp);
34107                 }
34108                 return [lbuf, buf];
34109             } :
34110             function(cs, rs, ds, startRow, colCount, stripe){
34111                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34112                 // buffers
34113                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34114                 var hasListener = this.grid.hasListener('rowclass');
34115  
34116                 var rowcfg = {};
34117                 for(var j = 0, len = rs.length; j < len; j++){
34118                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34119                     for(var i = 0; i < colCount; i++){
34120                         c = cs[i];
34121                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34122                         p.id = c.id;
34123                         p.css = p.attr = "";
34124                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34125                         if(p.value == undefined || p.value === "") {
34126                             p.value = "&#160;";
34127                         }
34128                         //Roo.log(c);
34129                          if(c.has_editor){
34130                             p.css += ' x-grid-editable-cell';
34131                         }
34132                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34133                             p.css += ' x-grid-dirty-cell' 
34134                         }
34135                         
34136                         var markup = ct.apply(p);
34137                         if(!c.locked){
34138                             cb[cb.length] = markup;
34139                         }else{
34140                             lcb[lcb.length] = markup;
34141                         }
34142                     }
34143                     var alt = [];
34144                     if(stripe && ((rowIndex+1) % 2 == 0)){
34145                         alt.push( "x-grid-row-alt");
34146                     }
34147                     if(r.dirty){
34148                         alt.push(" x-grid-dirty-row");
34149                     }
34150                     rp.cells = lcb;
34151                     if(this.getRowClass){
34152                         alt.push( this.getRowClass(r, rowIndex));
34153                     }
34154                     if (hasListener) {
34155                         rowcfg = {
34156                              
34157                             record: r,
34158                             rowIndex : rowIndex,
34159                             rowClass : ''
34160                         };
34161                         this.grid.fireEvent('rowclass', this, rowcfg);
34162                         alt.push(rowcfg.rowClass);
34163                     }
34164                     
34165                     rp.alt = alt.join(" ");
34166                     rp.cells = lcb.join("");
34167                     lbuf[lbuf.length] = rt.apply(rp);
34168                     rp.cells = cb.join("");
34169                     buf[buf.length] =  rt.apply(rp);
34170                 }
34171                 return [lbuf.join(""), buf.join("")];
34172             },
34173
34174     renderBody : function(){
34175         var markup = this.renderRows();
34176         var bt = this.templates.body;
34177         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34178     },
34179
34180     /**
34181      * Refreshes the grid
34182      * @param {Boolean} headersToo
34183      */
34184     refresh : function(headersToo){
34185         this.fireEvent("beforerefresh", this);
34186         this.grid.stopEditing();
34187         var result = this.renderBody();
34188         this.lockedBody.update(result[0]);
34189         this.mainBody.update(result[1]);
34190         if(headersToo === true){
34191             this.updateHeaders();
34192             this.updateColumns();
34193             this.updateSplitters();
34194             this.updateHeaderSortState();
34195         }
34196         this.syncRowHeights();
34197         this.layout();
34198         this.fireEvent("refresh", this);
34199     },
34200
34201     handleColumnMove : function(cm, oldIndex, newIndex){
34202         this.indexMap = null;
34203         var s = this.getScrollState();
34204         this.refresh(true);
34205         this.restoreScroll(s);
34206         this.afterMove(newIndex);
34207     },
34208
34209     afterMove : function(colIndex){
34210         if(this.enableMoveAnim && Roo.enableFx){
34211             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34212         }
34213         // if multisort - fix sortOrder, and reload..
34214         if (this.grid.dataSource.multiSort) {
34215             // the we can call sort again..
34216             var dm = this.grid.dataSource;
34217             var cm = this.grid.colModel;
34218             var so = [];
34219             for(var i = 0; i < cm.config.length; i++ ) {
34220                 
34221                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34222                     continue; // dont' bother, it's not in sort list or being set.
34223                 }
34224                 
34225                 so.push(cm.config[i].dataIndex);
34226             };
34227             dm.sortOrder = so;
34228             dm.load(dm.lastOptions);
34229             
34230             
34231         }
34232         
34233     },
34234
34235     updateCell : function(dm, rowIndex, dataIndex){
34236         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34237         if(typeof colIndex == "undefined"){ // not present in grid
34238             return;
34239         }
34240         var cm = this.grid.colModel;
34241         var cell = this.getCell(rowIndex, colIndex);
34242         var cellText = this.getCellText(rowIndex, colIndex);
34243
34244         var p = {
34245             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34246             id : cm.getColumnId(colIndex),
34247             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34248         };
34249         var renderer = cm.getRenderer(colIndex);
34250         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34251         if(typeof val == "undefined" || val === "") {
34252             val = "&#160;";
34253         }
34254         cellText.innerHTML = val;
34255         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34256         this.syncRowHeights(rowIndex, rowIndex);
34257     },
34258
34259     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34260         var maxWidth = 0;
34261         if(this.grid.autoSizeHeaders){
34262             var h = this.getHeaderCellMeasure(colIndex);
34263             maxWidth = Math.max(maxWidth, h.scrollWidth);
34264         }
34265         var tb, index;
34266         if(this.cm.isLocked(colIndex)){
34267             tb = this.getLockedTable();
34268             index = colIndex;
34269         }else{
34270             tb = this.getBodyTable();
34271             index = colIndex - this.cm.getLockedCount();
34272         }
34273         if(tb && tb.rows){
34274             var rows = tb.rows;
34275             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34276             for(var i = 0; i < stopIndex; i++){
34277                 var cell = rows[i].childNodes[index].firstChild;
34278                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34279             }
34280         }
34281         return maxWidth + /*margin for error in IE*/ 5;
34282     },
34283     /**
34284      * Autofit a column to its content.
34285      * @param {Number} colIndex
34286      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34287      */
34288      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34289          if(this.cm.isHidden(colIndex)){
34290              return; // can't calc a hidden column
34291          }
34292         if(forceMinSize){
34293             var cid = this.cm.getColumnId(colIndex);
34294             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34295            if(this.grid.autoSizeHeaders){
34296                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34297            }
34298         }
34299         var newWidth = this.calcColumnWidth(colIndex);
34300         this.cm.setColumnWidth(colIndex,
34301             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34302         if(!suppressEvent){
34303             this.grid.fireEvent("columnresize", colIndex, newWidth);
34304         }
34305     },
34306
34307     /**
34308      * Autofits all columns to their content and then expands to fit any extra space in the grid
34309      */
34310      autoSizeColumns : function(){
34311         var cm = this.grid.colModel;
34312         var colCount = cm.getColumnCount();
34313         for(var i = 0; i < colCount; i++){
34314             this.autoSizeColumn(i, true, true);
34315         }
34316         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34317             this.fitColumns();
34318         }else{
34319             this.updateColumns();
34320             this.layout();
34321         }
34322     },
34323
34324     /**
34325      * Autofits all columns to the grid's width proportionate with their current size
34326      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34327      */
34328     fitColumns : function(reserveScrollSpace){
34329         var cm = this.grid.colModel;
34330         var colCount = cm.getColumnCount();
34331         var cols = [];
34332         var width = 0;
34333         var i, w;
34334         for (i = 0; i < colCount; i++){
34335             if(!cm.isHidden(i) && !cm.isFixed(i)){
34336                 w = cm.getColumnWidth(i);
34337                 cols.push(i);
34338                 cols.push(w);
34339                 width += w;
34340             }
34341         }
34342         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34343         if(reserveScrollSpace){
34344             avail -= 17;
34345         }
34346         var frac = (avail - cm.getTotalWidth())/width;
34347         while (cols.length){
34348             w = cols.pop();
34349             i = cols.pop();
34350             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34351         }
34352         this.updateColumns();
34353         this.layout();
34354     },
34355
34356     onRowSelect : function(rowIndex){
34357         var row = this.getRowComposite(rowIndex);
34358         row.addClass("x-grid-row-selected");
34359     },
34360
34361     onRowDeselect : function(rowIndex){
34362         var row = this.getRowComposite(rowIndex);
34363         row.removeClass("x-grid-row-selected");
34364     },
34365
34366     onCellSelect : function(row, col){
34367         var cell = this.getCell(row, col);
34368         if(cell){
34369             Roo.fly(cell).addClass("x-grid-cell-selected");
34370         }
34371     },
34372
34373     onCellDeselect : function(row, col){
34374         var cell = this.getCell(row, col);
34375         if(cell){
34376             Roo.fly(cell).removeClass("x-grid-cell-selected");
34377         }
34378     },
34379
34380     updateHeaderSortState : function(){
34381         
34382         // sort state can be single { field: xxx, direction : yyy}
34383         // or   { xxx=>ASC , yyy : DESC ..... }
34384         
34385         var mstate = {};
34386         if (!this.ds.multiSort) { 
34387             var state = this.ds.getSortState();
34388             if(!state){
34389                 return;
34390             }
34391             mstate[state.field] = state.direction;
34392             // FIXME... - this is not used here.. but might be elsewhere..
34393             this.sortState = state;
34394             
34395         } else {
34396             mstate = this.ds.sortToggle;
34397         }
34398         //remove existing sort classes..
34399         
34400         var sc = this.sortClasses;
34401         var hds = this.el.select(this.headerSelector).removeClass(sc);
34402         
34403         for(var f in mstate) {
34404         
34405             var sortColumn = this.cm.findColumnIndex(f);
34406             
34407             if(sortColumn != -1){
34408                 var sortDir = mstate[f];        
34409                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34410             }
34411         }
34412         
34413          
34414         
34415     },
34416
34417
34418     handleHeaderClick : function(g, index,e){
34419         
34420         Roo.log("header click");
34421         
34422         if (Roo.isTouch) {
34423             // touch events on header are handled by context
34424             this.handleHdCtx(g,index,e);
34425             return;
34426         }
34427         
34428         
34429         if(this.headersDisabled){
34430             return;
34431         }
34432         var dm = g.dataSource, cm = g.colModel;
34433         if(!cm.isSortable(index)){
34434             return;
34435         }
34436         g.stopEditing();
34437         
34438         if (dm.multiSort) {
34439             // update the sortOrder
34440             var so = [];
34441             for(var i = 0; i < cm.config.length; i++ ) {
34442                 
34443                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34444                     continue; // dont' bother, it's not in sort list or being set.
34445                 }
34446                 
34447                 so.push(cm.config[i].dataIndex);
34448             };
34449             dm.sortOrder = so;
34450         }
34451         
34452         
34453         dm.sort(cm.getDataIndex(index));
34454     },
34455
34456
34457     destroy : function(){
34458         if(this.colMenu){
34459             this.colMenu.removeAll();
34460             Roo.menu.MenuMgr.unregister(this.colMenu);
34461             this.colMenu.getEl().remove();
34462             delete this.colMenu;
34463         }
34464         if(this.hmenu){
34465             this.hmenu.removeAll();
34466             Roo.menu.MenuMgr.unregister(this.hmenu);
34467             this.hmenu.getEl().remove();
34468             delete this.hmenu;
34469         }
34470         if(this.grid.enableColumnMove){
34471             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34472             if(dds){
34473                 for(var dd in dds){
34474                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34475                         var elid = dds[dd].dragElId;
34476                         dds[dd].unreg();
34477                         Roo.get(elid).remove();
34478                     } else if(dds[dd].config.isTarget){
34479                         dds[dd].proxyTop.remove();
34480                         dds[dd].proxyBottom.remove();
34481                         dds[dd].unreg();
34482                     }
34483                     if(Roo.dd.DDM.locationCache[dd]){
34484                         delete Roo.dd.DDM.locationCache[dd];
34485                     }
34486                 }
34487                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34488             }
34489         }
34490         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34491         this.bind(null, null);
34492         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34493     },
34494
34495     handleLockChange : function(){
34496         this.refresh(true);
34497     },
34498
34499     onDenyColumnLock : function(){
34500
34501     },
34502
34503     onDenyColumnHide : function(){
34504
34505     },
34506
34507     handleHdMenuClick : function(item){
34508         var index = this.hdCtxIndex;
34509         var cm = this.cm, ds = this.ds;
34510         switch(item.id){
34511             case "asc":
34512                 ds.sort(cm.getDataIndex(index), "ASC");
34513                 break;
34514             case "desc":
34515                 ds.sort(cm.getDataIndex(index), "DESC");
34516                 break;
34517             case "lock":
34518                 var lc = cm.getLockedCount();
34519                 if(cm.getColumnCount(true) <= lc+1){
34520                     this.onDenyColumnLock();
34521                     return;
34522                 }
34523                 if(lc != index){
34524                     cm.setLocked(index, true, true);
34525                     cm.moveColumn(index, lc);
34526                     this.grid.fireEvent("columnmove", index, lc);
34527                 }else{
34528                     cm.setLocked(index, true);
34529                 }
34530             break;
34531             case "unlock":
34532                 var lc = cm.getLockedCount();
34533                 if((lc-1) != index){
34534                     cm.setLocked(index, false, true);
34535                     cm.moveColumn(index, lc-1);
34536                     this.grid.fireEvent("columnmove", index, lc-1);
34537                 }else{
34538                     cm.setLocked(index, false);
34539                 }
34540             break;
34541             case 'wider': // used to expand cols on touch..
34542             case 'narrow':
34543                 var cw = cm.getColumnWidth(index);
34544                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34545                 cw = Math.max(0, cw);
34546                 cw = Math.min(cw,4000);
34547                 cm.setColumnWidth(index, cw);
34548                 break;
34549                 
34550             default:
34551                 index = cm.getIndexById(item.id.substr(4));
34552                 if(index != -1){
34553                     if(item.checked && cm.getColumnCount(true) <= 1){
34554                         this.onDenyColumnHide();
34555                         return false;
34556                     }
34557                     cm.setHidden(index, item.checked);
34558                 }
34559         }
34560         return true;
34561     },
34562
34563     beforeColMenuShow : function(){
34564         var cm = this.cm,  colCount = cm.getColumnCount();
34565         this.colMenu.removeAll();
34566         for(var i = 0; i < colCount; i++){
34567             this.colMenu.add(new Roo.menu.CheckItem({
34568                 id: "col-"+cm.getColumnId(i),
34569                 text: cm.getColumnHeader(i),
34570                 checked: !cm.isHidden(i),
34571                 hideOnClick:false
34572             }));
34573         }
34574     },
34575
34576     handleHdCtx : function(g, index, e){
34577         e.stopEvent();
34578         var hd = this.getHeaderCell(index);
34579         this.hdCtxIndex = index;
34580         var ms = this.hmenu.items, cm = this.cm;
34581         ms.get("asc").setDisabled(!cm.isSortable(index));
34582         ms.get("desc").setDisabled(!cm.isSortable(index));
34583         if(this.grid.enableColLock !== false){
34584             ms.get("lock").setDisabled(cm.isLocked(index));
34585             ms.get("unlock").setDisabled(!cm.isLocked(index));
34586         }
34587         this.hmenu.show(hd, "tl-bl");
34588     },
34589
34590     handleHdOver : function(e){
34591         var hd = this.findHeaderCell(e.getTarget());
34592         if(hd && !this.headersDisabled){
34593             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34594                this.fly(hd).addClass("x-grid-hd-over");
34595             }
34596         }
34597     },
34598
34599     handleHdOut : function(e){
34600         var hd = this.findHeaderCell(e.getTarget());
34601         if(hd){
34602             this.fly(hd).removeClass("x-grid-hd-over");
34603         }
34604     },
34605
34606     handleSplitDblClick : function(e, t){
34607         var i = this.getCellIndex(t);
34608         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34609             this.autoSizeColumn(i, true);
34610             this.layout();
34611         }
34612     },
34613
34614     render : function(){
34615
34616         var cm = this.cm;
34617         var colCount = cm.getColumnCount();
34618
34619         if(this.grid.monitorWindowResize === true){
34620             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34621         }
34622         var header = this.renderHeaders();
34623         var body = this.templates.body.apply({rows:""});
34624         var html = this.templates.master.apply({
34625             lockedBody: body,
34626             body: body,
34627             lockedHeader: header[0],
34628             header: header[1]
34629         });
34630
34631         //this.updateColumns();
34632
34633         this.grid.getGridEl().dom.innerHTML = html;
34634
34635         this.initElements();
34636         
34637         // a kludge to fix the random scolling effect in webkit
34638         this.el.on("scroll", function() {
34639             this.el.dom.scrollTop=0; // hopefully not recursive..
34640         },this);
34641
34642         this.scroller.on("scroll", this.handleScroll, this);
34643         this.lockedBody.on("mousewheel", this.handleWheel, this);
34644         this.mainBody.on("mousewheel", this.handleWheel, this);
34645
34646         this.mainHd.on("mouseover", this.handleHdOver, this);
34647         this.mainHd.on("mouseout", this.handleHdOut, this);
34648         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34649                 {delegate: "."+this.splitClass});
34650
34651         this.lockedHd.on("mouseover", this.handleHdOver, this);
34652         this.lockedHd.on("mouseout", this.handleHdOut, this);
34653         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34654                 {delegate: "."+this.splitClass});
34655
34656         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34657             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34658         }
34659
34660         this.updateSplitters();
34661
34662         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34663             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34664             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34665         }
34666
34667         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34668             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34669             this.hmenu.add(
34670                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34671                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34672             );
34673             if(this.grid.enableColLock !== false){
34674                 this.hmenu.add('-',
34675                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34676                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34677                 );
34678             }
34679             if (Roo.isTouch) {
34680                  this.hmenu.add('-',
34681                     {id:"wider", text: this.columnsWiderText},
34682                     {id:"narrow", text: this.columnsNarrowText }
34683                 );
34684                 
34685                  
34686             }
34687             
34688             if(this.grid.enableColumnHide !== false){
34689
34690                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34691                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34692                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34693
34694                 this.hmenu.add('-',
34695                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34696                 );
34697             }
34698             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34699
34700             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34701         }
34702
34703         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34704             this.dd = new Roo.grid.GridDragZone(this.grid, {
34705                 ddGroup : this.grid.ddGroup || 'GridDD'
34706             });
34707             
34708         }
34709
34710         /*
34711         for(var i = 0; i < colCount; i++){
34712             if(cm.isHidden(i)){
34713                 this.hideColumn(i);
34714             }
34715             if(cm.config[i].align){
34716                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34717                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34718             }
34719         }*/
34720         
34721         this.updateHeaderSortState();
34722
34723         this.beforeInitialResize();
34724         this.layout(true);
34725
34726         // two part rendering gives faster view to the user
34727         this.renderPhase2.defer(1, this);
34728     },
34729
34730     renderPhase2 : function(){
34731         // render the rows now
34732         this.refresh();
34733         if(this.grid.autoSizeColumns){
34734             this.autoSizeColumns();
34735         }
34736     },
34737
34738     beforeInitialResize : function(){
34739
34740     },
34741
34742     onColumnSplitterMoved : function(i, w){
34743         this.userResized = true;
34744         var cm = this.grid.colModel;
34745         cm.setColumnWidth(i, w, true);
34746         var cid = cm.getColumnId(i);
34747         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34748         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34749         this.updateSplitters();
34750         this.layout();
34751         this.grid.fireEvent("columnresize", i, w);
34752     },
34753
34754     syncRowHeights : function(startIndex, endIndex){
34755         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34756             startIndex = startIndex || 0;
34757             var mrows = this.getBodyTable().rows;
34758             var lrows = this.getLockedTable().rows;
34759             var len = mrows.length-1;
34760             endIndex = Math.min(endIndex || len, len);
34761             for(var i = startIndex; i <= endIndex; i++){
34762                 var m = mrows[i], l = lrows[i];
34763                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34764                 m.style.height = l.style.height = h + "px";
34765             }
34766         }
34767     },
34768
34769     layout : function(initialRender, is2ndPass)
34770     {
34771         var g = this.grid;
34772         var auto = g.autoHeight;
34773         var scrollOffset = 16;
34774         var c = g.getGridEl(), cm = this.cm,
34775                 expandCol = g.autoExpandColumn,
34776                 gv = this;
34777         //c.beginMeasure();
34778
34779         if(!c.dom.offsetWidth){ // display:none?
34780             if(initialRender){
34781                 this.lockedWrap.show();
34782                 this.mainWrap.show();
34783             }
34784             return;
34785         }
34786
34787         var hasLock = this.cm.isLocked(0);
34788
34789         var tbh = this.headerPanel.getHeight();
34790         var bbh = this.footerPanel.getHeight();
34791
34792         if(auto){
34793             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34794             var newHeight = ch + c.getBorderWidth("tb");
34795             if(g.maxHeight){
34796                 newHeight = Math.min(g.maxHeight, newHeight);
34797             }
34798             c.setHeight(newHeight);
34799         }
34800
34801         if(g.autoWidth){
34802             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34803         }
34804
34805         var s = this.scroller;
34806
34807         var csize = c.getSize(true);
34808
34809         this.el.setSize(csize.width, csize.height);
34810
34811         this.headerPanel.setWidth(csize.width);
34812         this.footerPanel.setWidth(csize.width);
34813
34814         var hdHeight = this.mainHd.getHeight();
34815         var vw = csize.width;
34816         var vh = csize.height - (tbh + bbh);
34817
34818         s.setSize(vw, vh);
34819
34820         var bt = this.getBodyTable();
34821         
34822         if(cm.getLockedCount() == cm.config.length){
34823             bt = this.getLockedTable();
34824         }
34825         
34826         var ltWidth = hasLock ?
34827                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34828
34829         var scrollHeight = bt.offsetHeight;
34830         var scrollWidth = ltWidth + bt.offsetWidth;
34831         var vscroll = false, hscroll = false;
34832
34833         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34834
34835         var lw = this.lockedWrap, mw = this.mainWrap;
34836         var lb = this.lockedBody, mb = this.mainBody;
34837
34838         setTimeout(function(){
34839             var t = s.dom.offsetTop;
34840             var w = s.dom.clientWidth,
34841                 h = s.dom.clientHeight;
34842
34843             lw.setTop(t);
34844             lw.setSize(ltWidth, h);
34845
34846             mw.setLeftTop(ltWidth, t);
34847             mw.setSize(w-ltWidth, h);
34848
34849             lb.setHeight(h-hdHeight);
34850             mb.setHeight(h-hdHeight);
34851
34852             if(is2ndPass !== true && !gv.userResized && expandCol){
34853                 // high speed resize without full column calculation
34854                 
34855                 var ci = cm.getIndexById(expandCol);
34856                 if (ci < 0) {
34857                     ci = cm.findColumnIndex(expandCol);
34858                 }
34859                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34860                 var expandId = cm.getColumnId(ci);
34861                 var  tw = cm.getTotalWidth(false);
34862                 var currentWidth = cm.getColumnWidth(ci);
34863                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34864                 if(currentWidth != cw){
34865                     cm.setColumnWidth(ci, cw, true);
34866                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34867                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34868                     gv.updateSplitters();
34869                     gv.layout(false, true);
34870                 }
34871             }
34872
34873             if(initialRender){
34874                 lw.show();
34875                 mw.show();
34876             }
34877             //c.endMeasure();
34878         }, 10);
34879     },
34880
34881     onWindowResize : function(){
34882         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34883             return;
34884         }
34885         this.layout();
34886     },
34887
34888     appendFooter : function(parentEl){
34889         return null;
34890     },
34891
34892     sortAscText : "Sort Ascending",
34893     sortDescText : "Sort Descending",
34894     lockText : "Lock Column",
34895     unlockText : "Unlock Column",
34896     columnsText : "Columns",
34897  
34898     columnsWiderText : "Wider",
34899     columnsNarrowText : "Thinner"
34900 });
34901
34902
34903 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34904     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34905     this.proxy.el.addClass('x-grid3-col-dd');
34906 };
34907
34908 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34909     handleMouseDown : function(e){
34910
34911     },
34912
34913     callHandleMouseDown : function(e){
34914         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34915     }
34916 });
34917 /*
34918  * Based on:
34919  * Ext JS Library 1.1.1
34920  * Copyright(c) 2006-2007, Ext JS, LLC.
34921  *
34922  * Originally Released Under LGPL - original licence link has changed is not relivant.
34923  *
34924  * Fork - LGPL
34925  * <script type="text/javascript">
34926  */
34927  
34928 // private
34929 // This is a support class used internally by the Grid components
34930 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34931     this.grid = grid;
34932     this.view = grid.getView();
34933     this.proxy = this.view.resizeProxy;
34934     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34935         "gridSplitters" + this.grid.getGridEl().id, {
34936         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34937     });
34938     this.setHandleElId(Roo.id(hd));
34939     this.setOuterHandleElId(Roo.id(hd2));
34940     this.scroll = false;
34941 };
34942 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34943     fly: Roo.Element.fly,
34944
34945     b4StartDrag : function(x, y){
34946         this.view.headersDisabled = true;
34947         this.proxy.setHeight(this.view.mainWrap.getHeight());
34948         var w = this.cm.getColumnWidth(this.cellIndex);
34949         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34950         this.resetConstraints();
34951         this.setXConstraint(minw, 1000);
34952         this.setYConstraint(0, 0);
34953         this.minX = x - minw;
34954         this.maxX = x + 1000;
34955         this.startPos = x;
34956         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34957     },
34958
34959
34960     handleMouseDown : function(e){
34961         ev = Roo.EventObject.setEvent(e);
34962         var t = this.fly(ev.getTarget());
34963         if(t.hasClass("x-grid-split")){
34964             this.cellIndex = this.view.getCellIndex(t.dom);
34965             this.split = t.dom;
34966             this.cm = this.grid.colModel;
34967             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34968                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34969             }
34970         }
34971     },
34972
34973     endDrag : function(e){
34974         this.view.headersDisabled = false;
34975         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34976         var diff = endX - this.startPos;
34977         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34978     },
34979
34980     autoOffset : function(){
34981         this.setDelta(0,0);
34982     }
34983 });/*
34984  * Based on:
34985  * Ext JS Library 1.1.1
34986  * Copyright(c) 2006-2007, Ext JS, LLC.
34987  *
34988  * Originally Released Under LGPL - original licence link has changed is not relivant.
34989  *
34990  * Fork - LGPL
34991  * <script type="text/javascript">
34992  */
34993  
34994 // private
34995 // This is a support class used internally by the Grid components
34996 Roo.grid.GridDragZone = function(grid, config){
34997     this.view = grid.getView();
34998     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34999     if(this.view.lockedBody){
35000         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35001         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35002     }
35003     this.scroll = false;
35004     this.grid = grid;
35005     this.ddel = document.createElement('div');
35006     this.ddel.className = 'x-grid-dd-wrap';
35007 };
35008
35009 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35010     ddGroup : "GridDD",
35011
35012     getDragData : function(e){
35013         var t = Roo.lib.Event.getTarget(e);
35014         var rowIndex = this.view.findRowIndex(t);
35015         var sm = this.grid.selModel;
35016             
35017         //Roo.log(rowIndex);
35018         
35019         if (sm.getSelectedCell) {
35020             // cell selection..
35021             if (!sm.getSelectedCell()) {
35022                 return false;
35023             }
35024             if (rowIndex != sm.getSelectedCell()[0]) {
35025                 return false;
35026             }
35027         
35028         }
35029         if (sm.getSelections && sm.getSelections().length < 1) {
35030             return false;
35031         }
35032         
35033         
35034         // before it used to all dragging of unseleted... - now we dont do that.
35035         if(rowIndex !== false){
35036             
35037             // if editorgrid.. 
35038             
35039             
35040             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35041                
35042             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35043               //  
35044             //}
35045             if (e.hasModifier()){
35046                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35047             }
35048             
35049             Roo.log("getDragData");
35050             
35051             return {
35052                 grid: this.grid,
35053                 ddel: this.ddel,
35054                 rowIndex: rowIndex,
35055                 selections: sm.getSelections ? sm.getSelections() : (
35056                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
35057             };
35058         }
35059         return false;
35060     },
35061     
35062     
35063     onInitDrag : function(e){
35064         var data = this.dragData;
35065         this.ddel.innerHTML = this.grid.getDragDropText();
35066         this.proxy.update(this.ddel);
35067         // fire start drag?
35068     },
35069
35070     afterRepair : function(){
35071         this.dragging = false;
35072     },
35073
35074     getRepairXY : function(e, data){
35075         return false;
35076     },
35077
35078     onEndDrag : function(data, e){
35079         // fire end drag?
35080     },
35081
35082     onValidDrop : function(dd, e, id){
35083         // fire drag drop?
35084         this.hideProxy();
35085     },
35086
35087     beforeInvalidDrop : function(e, id){
35088
35089     }
35090 });/*
35091  * Based on:
35092  * Ext JS Library 1.1.1
35093  * Copyright(c) 2006-2007, Ext JS, LLC.
35094  *
35095  * Originally Released Under LGPL - original licence link has changed is not relivant.
35096  *
35097  * Fork - LGPL
35098  * <script type="text/javascript">
35099  */
35100  
35101
35102 /**
35103  * @class Roo.grid.ColumnModel
35104  * @extends Roo.util.Observable
35105  * This is the default implementation of a ColumnModel used by the Grid. It defines
35106  * the columns in the grid.
35107  * <br>Usage:<br>
35108  <pre><code>
35109  var colModel = new Roo.grid.ColumnModel([
35110         {header: "Ticker", width: 60, sortable: true, locked: true},
35111         {header: "Company Name", width: 150, sortable: true},
35112         {header: "Market Cap.", width: 100, sortable: true},
35113         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35114         {header: "Employees", width: 100, sortable: true, resizable: false}
35115  ]);
35116  </code></pre>
35117  * <p>
35118  
35119  * The config options listed for this class are options which may appear in each
35120  * individual column definition.
35121  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35122  * @constructor
35123  * @param {Object} config An Array of column config objects. See this class's
35124  * config objects for details.
35125 */
35126 Roo.grid.ColumnModel = function(config){
35127         /**
35128      * The config passed into the constructor
35129      */
35130     this.config = config;
35131     this.lookup = {};
35132
35133     // if no id, create one
35134     // if the column does not have a dataIndex mapping,
35135     // map it to the order it is in the config
35136     for(var i = 0, len = config.length; i < len; i++){
35137         var c = config[i];
35138         if(typeof c.dataIndex == "undefined"){
35139             c.dataIndex = i;
35140         }
35141         if(typeof c.renderer == "string"){
35142             c.renderer = Roo.util.Format[c.renderer];
35143         }
35144         if(typeof c.id == "undefined"){
35145             c.id = Roo.id();
35146         }
35147         if(c.editor && c.editor.xtype){
35148             c.editor  = Roo.factory(c.editor, Roo.grid);
35149         }
35150         if(c.editor && c.editor.isFormField){
35151             c.editor = new Roo.grid.GridEditor(c.editor);
35152         }
35153         this.lookup[c.id] = c;
35154     }
35155
35156     /**
35157      * The width of columns which have no width specified (defaults to 100)
35158      * @type Number
35159      */
35160     this.defaultWidth = 100;
35161
35162     /**
35163      * Default sortable of columns which have no sortable specified (defaults to false)
35164      * @type Boolean
35165      */
35166     this.defaultSortable = false;
35167
35168     this.addEvents({
35169         /**
35170              * @event widthchange
35171              * Fires when the width of a column changes.
35172              * @param {ColumnModel} this
35173              * @param {Number} columnIndex The column index
35174              * @param {Number} newWidth The new width
35175              */
35176             "widthchange": true,
35177         /**
35178              * @event headerchange
35179              * Fires when the text of a header changes.
35180              * @param {ColumnModel} this
35181              * @param {Number} columnIndex The column index
35182              * @param {Number} newText The new header text
35183              */
35184             "headerchange": true,
35185         /**
35186              * @event hiddenchange
35187              * Fires when a column is hidden or "unhidden".
35188              * @param {ColumnModel} this
35189              * @param {Number} columnIndex The column index
35190              * @param {Boolean} hidden true if hidden, false otherwise
35191              */
35192             "hiddenchange": true,
35193             /**
35194          * @event columnmoved
35195          * Fires when a column is moved.
35196          * @param {ColumnModel} this
35197          * @param {Number} oldIndex
35198          * @param {Number} newIndex
35199          */
35200         "columnmoved" : true,
35201         /**
35202          * @event columlockchange
35203          * Fires when a column's locked state is changed
35204          * @param {ColumnModel} this
35205          * @param {Number} colIndex
35206          * @param {Boolean} locked true if locked
35207          */
35208         "columnlockchange" : true
35209     });
35210     Roo.grid.ColumnModel.superclass.constructor.call(this);
35211 };
35212 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35213     /**
35214      * @cfg {String} header The header text to display in the Grid view.
35215      */
35216     /**
35217      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35218      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35219      * specified, the column's index is used as an index into the Record's data Array.
35220      */
35221     /**
35222      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35223      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35224      */
35225     /**
35226      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35227      * Defaults to the value of the {@link #defaultSortable} property.
35228      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35229      */
35230     /**
35231      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35232      */
35233     /**
35234      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35235      */
35236     /**
35237      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35238      */
35239     /**
35240      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35241      */
35242     /**
35243      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35244      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35245      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35246      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35247      */
35248        /**
35249      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35250      */
35251     /**
35252      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35253      */
35254     /**
35255      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35256      */
35257     /**
35258      * @cfg {String} cursor (Optional)
35259      */
35260     /**
35261      * @cfg {String} tooltip (Optional)
35262      */
35263     /**
35264      * @cfg {Number} xs (Optional)
35265      */
35266     /**
35267      * @cfg {Number} sm (Optional)
35268      */
35269     /**
35270      * @cfg {Number} md (Optional)
35271      */
35272     /**
35273      * @cfg {Number} lg (Optional)
35274      */
35275     /**
35276      * Returns the id of the column at the specified index.
35277      * @param {Number} index The column index
35278      * @return {String} the id
35279      */
35280     getColumnId : function(index){
35281         return this.config[index].id;
35282     },
35283
35284     /**
35285      * Returns the column for a specified id.
35286      * @param {String} id The column id
35287      * @return {Object} the column
35288      */
35289     getColumnById : function(id){
35290         return this.lookup[id];
35291     },
35292
35293     
35294     /**
35295      * Returns the column for a specified dataIndex.
35296      * @param {String} dataIndex The column dataIndex
35297      * @return {Object|Boolean} the column or false if not found
35298      */
35299     getColumnByDataIndex: function(dataIndex){
35300         var index = this.findColumnIndex(dataIndex);
35301         return index > -1 ? this.config[index] : false;
35302     },
35303     
35304     /**
35305      * Returns the index for a specified column id.
35306      * @param {String} id The column id
35307      * @return {Number} the index, or -1 if not found
35308      */
35309     getIndexById : function(id){
35310         for(var i = 0, len = this.config.length; i < len; i++){
35311             if(this.config[i].id == id){
35312                 return i;
35313             }
35314         }
35315         return -1;
35316     },
35317     
35318     /**
35319      * Returns the index for a specified column dataIndex.
35320      * @param {String} dataIndex The column dataIndex
35321      * @return {Number} the index, or -1 if not found
35322      */
35323     
35324     findColumnIndex : function(dataIndex){
35325         for(var i = 0, len = this.config.length; i < len; i++){
35326             if(this.config[i].dataIndex == dataIndex){
35327                 return i;
35328             }
35329         }
35330         return -1;
35331     },
35332     
35333     
35334     moveColumn : function(oldIndex, newIndex){
35335         var c = this.config[oldIndex];
35336         this.config.splice(oldIndex, 1);
35337         this.config.splice(newIndex, 0, c);
35338         this.dataMap = null;
35339         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35340     },
35341
35342     isLocked : function(colIndex){
35343         return this.config[colIndex].locked === true;
35344     },
35345
35346     setLocked : function(colIndex, value, suppressEvent){
35347         if(this.isLocked(colIndex) == value){
35348             return;
35349         }
35350         this.config[colIndex].locked = value;
35351         if(!suppressEvent){
35352             this.fireEvent("columnlockchange", this, colIndex, value);
35353         }
35354     },
35355
35356     getTotalLockedWidth : function(){
35357         var totalWidth = 0;
35358         for(var i = 0; i < this.config.length; i++){
35359             if(this.isLocked(i) && !this.isHidden(i)){
35360                 this.totalWidth += this.getColumnWidth(i);
35361             }
35362         }
35363         return totalWidth;
35364     },
35365
35366     getLockedCount : function(){
35367         for(var i = 0, len = this.config.length; i < len; i++){
35368             if(!this.isLocked(i)){
35369                 return i;
35370             }
35371         }
35372         
35373         return this.config.length;
35374     },
35375
35376     /**
35377      * Returns the number of columns.
35378      * @return {Number}
35379      */
35380     getColumnCount : function(visibleOnly){
35381         if(visibleOnly === true){
35382             var c = 0;
35383             for(var i = 0, len = this.config.length; i < len; i++){
35384                 if(!this.isHidden(i)){
35385                     c++;
35386                 }
35387             }
35388             return c;
35389         }
35390         return this.config.length;
35391     },
35392
35393     /**
35394      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35395      * @param {Function} fn
35396      * @param {Object} scope (optional)
35397      * @return {Array} result
35398      */
35399     getColumnsBy : function(fn, scope){
35400         var r = [];
35401         for(var i = 0, len = this.config.length; i < len; i++){
35402             var c = this.config[i];
35403             if(fn.call(scope||this, c, i) === true){
35404                 r[r.length] = c;
35405             }
35406         }
35407         return r;
35408     },
35409
35410     /**
35411      * Returns true if the specified column is sortable.
35412      * @param {Number} col The column index
35413      * @return {Boolean}
35414      */
35415     isSortable : function(col){
35416         if(typeof this.config[col].sortable == "undefined"){
35417             return this.defaultSortable;
35418         }
35419         return this.config[col].sortable;
35420     },
35421
35422     /**
35423      * Returns the rendering (formatting) function defined for the column.
35424      * @param {Number} col The column index.
35425      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35426      */
35427     getRenderer : function(col){
35428         if(!this.config[col].renderer){
35429             return Roo.grid.ColumnModel.defaultRenderer;
35430         }
35431         return this.config[col].renderer;
35432     },
35433
35434     /**
35435      * Sets the rendering (formatting) function for a column.
35436      * @param {Number} col The column index
35437      * @param {Function} fn The function to use to process the cell's raw data
35438      * to return HTML markup for the grid view. The render function is called with
35439      * the following parameters:<ul>
35440      * <li>Data value.</li>
35441      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35442      * <li>css A CSS style string to apply to the table cell.</li>
35443      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35444      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35445      * <li>Row index</li>
35446      * <li>Column index</li>
35447      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35448      */
35449     setRenderer : function(col, fn){
35450         this.config[col].renderer = fn;
35451     },
35452
35453     /**
35454      * Returns the width for the specified column.
35455      * @param {Number} col The column index
35456      * @return {Number}
35457      */
35458     getColumnWidth : function(col){
35459         return this.config[col].width * 1 || this.defaultWidth;
35460     },
35461
35462     /**
35463      * Sets the width for a column.
35464      * @param {Number} col The column index
35465      * @param {Number} width The new width
35466      */
35467     setColumnWidth : function(col, width, suppressEvent){
35468         this.config[col].width = width;
35469         this.totalWidth = null;
35470         if(!suppressEvent){
35471              this.fireEvent("widthchange", this, col, width);
35472         }
35473     },
35474
35475     /**
35476      * Returns the total width of all columns.
35477      * @param {Boolean} includeHidden True to include hidden column widths
35478      * @return {Number}
35479      */
35480     getTotalWidth : function(includeHidden){
35481         if(!this.totalWidth){
35482             this.totalWidth = 0;
35483             for(var i = 0, len = this.config.length; i < len; i++){
35484                 if(includeHidden || !this.isHidden(i)){
35485                     this.totalWidth += this.getColumnWidth(i);
35486                 }
35487             }
35488         }
35489         return this.totalWidth;
35490     },
35491
35492     /**
35493      * Returns the header for the specified column.
35494      * @param {Number} col The column index
35495      * @return {String}
35496      */
35497     getColumnHeader : function(col){
35498         return this.config[col].header;
35499     },
35500
35501     /**
35502      * Sets the header for a column.
35503      * @param {Number} col The column index
35504      * @param {String} header The new header
35505      */
35506     setColumnHeader : function(col, header){
35507         this.config[col].header = header;
35508         this.fireEvent("headerchange", this, col, header);
35509     },
35510
35511     /**
35512      * Returns the tooltip for the specified column.
35513      * @param {Number} col The column index
35514      * @return {String}
35515      */
35516     getColumnTooltip : function(col){
35517             return this.config[col].tooltip;
35518     },
35519     /**
35520      * Sets the tooltip for a column.
35521      * @param {Number} col The column index
35522      * @param {String} tooltip The new tooltip
35523      */
35524     setColumnTooltip : function(col, tooltip){
35525             this.config[col].tooltip = tooltip;
35526     },
35527
35528     /**
35529      * Returns the dataIndex for the specified column.
35530      * @param {Number} col The column index
35531      * @return {Number}
35532      */
35533     getDataIndex : function(col){
35534         return this.config[col].dataIndex;
35535     },
35536
35537     /**
35538      * Sets the dataIndex for a column.
35539      * @param {Number} col The column index
35540      * @param {Number} dataIndex The new dataIndex
35541      */
35542     setDataIndex : function(col, dataIndex){
35543         this.config[col].dataIndex = dataIndex;
35544     },
35545
35546     
35547     
35548     /**
35549      * Returns true if the cell is editable.
35550      * @param {Number} colIndex The column index
35551      * @param {Number} rowIndex The row index - this is nto actually used..?
35552      * @return {Boolean}
35553      */
35554     isCellEditable : function(colIndex, rowIndex){
35555         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35556     },
35557
35558     /**
35559      * Returns the editor defined for the cell/column.
35560      * return false or null to disable editing.
35561      * @param {Number} colIndex The column index
35562      * @param {Number} rowIndex The row index
35563      * @return {Object}
35564      */
35565     getCellEditor : function(colIndex, rowIndex){
35566         return this.config[colIndex].editor;
35567     },
35568
35569     /**
35570      * Sets if a column is editable.
35571      * @param {Number} col The column index
35572      * @param {Boolean} editable True if the column is editable
35573      */
35574     setEditable : function(col, editable){
35575         this.config[col].editable = editable;
35576     },
35577
35578
35579     /**
35580      * Returns true if the column is hidden.
35581      * @param {Number} colIndex The column index
35582      * @return {Boolean}
35583      */
35584     isHidden : function(colIndex){
35585         return this.config[colIndex].hidden;
35586     },
35587
35588
35589     /**
35590      * Returns true if the column width cannot be changed
35591      */
35592     isFixed : function(colIndex){
35593         return this.config[colIndex].fixed;
35594     },
35595
35596     /**
35597      * Returns true if the column can be resized
35598      * @return {Boolean}
35599      */
35600     isResizable : function(colIndex){
35601         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35602     },
35603     /**
35604      * Sets if a column is hidden.
35605      * @param {Number} colIndex The column index
35606      * @param {Boolean} hidden True if the column is hidden
35607      */
35608     setHidden : function(colIndex, hidden){
35609         this.config[colIndex].hidden = hidden;
35610         this.totalWidth = null;
35611         this.fireEvent("hiddenchange", this, colIndex, hidden);
35612     },
35613
35614     /**
35615      * Sets the editor for a column.
35616      * @param {Number} col The column index
35617      * @param {Object} editor The editor object
35618      */
35619     setEditor : function(col, editor){
35620         this.config[col].editor = editor;
35621     }
35622 });
35623
35624 Roo.grid.ColumnModel.defaultRenderer = function(value)
35625 {
35626     if(typeof value == "object") {
35627         return value;
35628     }
35629         if(typeof value == "string" && value.length < 1){
35630             return "&#160;";
35631         }
35632     
35633         return String.format("{0}", value);
35634 };
35635
35636 // Alias for backwards compatibility
35637 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35638 /*
35639  * Based on:
35640  * Ext JS Library 1.1.1
35641  * Copyright(c) 2006-2007, Ext JS, LLC.
35642  *
35643  * Originally Released Under LGPL - original licence link has changed is not relivant.
35644  *
35645  * Fork - LGPL
35646  * <script type="text/javascript">
35647  */
35648
35649 /**
35650  * @class Roo.grid.AbstractSelectionModel
35651  * @extends Roo.util.Observable
35652  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35653  * implemented by descendant classes.  This class should not be directly instantiated.
35654  * @constructor
35655  */
35656 Roo.grid.AbstractSelectionModel = function(){
35657     this.locked = false;
35658     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35659 };
35660
35661 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35662     /** @ignore Called by the grid automatically. Do not call directly. */
35663     init : function(grid){
35664         this.grid = grid;
35665         this.initEvents();
35666     },
35667
35668     /**
35669      * Locks the selections.
35670      */
35671     lock : function(){
35672         this.locked = true;
35673     },
35674
35675     /**
35676      * Unlocks the selections.
35677      */
35678     unlock : function(){
35679         this.locked = false;
35680     },
35681
35682     /**
35683      * Returns true if the selections are locked.
35684      * @return {Boolean}
35685      */
35686     isLocked : function(){
35687         return this.locked;
35688     }
35689 });/*
35690  * Based on:
35691  * Ext JS Library 1.1.1
35692  * Copyright(c) 2006-2007, Ext JS, LLC.
35693  *
35694  * Originally Released Under LGPL - original licence link has changed is not relivant.
35695  *
35696  * Fork - LGPL
35697  * <script type="text/javascript">
35698  */
35699 /**
35700  * @extends Roo.grid.AbstractSelectionModel
35701  * @class Roo.grid.RowSelectionModel
35702  * The default SelectionModel used by {@link Roo.grid.Grid}.
35703  * It supports multiple selections and keyboard selection/navigation. 
35704  * @constructor
35705  * @param {Object} config
35706  */
35707 Roo.grid.RowSelectionModel = function(config){
35708     Roo.apply(this, config);
35709     this.selections = new Roo.util.MixedCollection(false, function(o){
35710         return o.id;
35711     });
35712
35713     this.last = false;
35714     this.lastActive = false;
35715
35716     this.addEvents({
35717         /**
35718              * @event selectionchange
35719              * Fires when the selection changes
35720              * @param {SelectionModel} this
35721              */
35722             "selectionchange" : true,
35723         /**
35724              * @event afterselectionchange
35725              * Fires after the selection changes (eg. by key press or clicking)
35726              * @param {SelectionModel} this
35727              */
35728             "afterselectionchange" : true,
35729         /**
35730              * @event beforerowselect
35731              * Fires when a row is selected being selected, return false to cancel.
35732              * @param {SelectionModel} this
35733              * @param {Number} rowIndex The selected index
35734              * @param {Boolean} keepExisting False if other selections will be cleared
35735              */
35736             "beforerowselect" : true,
35737         /**
35738              * @event rowselect
35739              * Fires when a row is selected.
35740              * @param {SelectionModel} this
35741              * @param {Number} rowIndex The selected index
35742              * @param {Roo.data.Record} r The record
35743              */
35744             "rowselect" : true,
35745         /**
35746              * @event rowdeselect
35747              * Fires when a row is deselected.
35748              * @param {SelectionModel} this
35749              * @param {Number} rowIndex The selected index
35750              */
35751         "rowdeselect" : true
35752     });
35753     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35754     this.locked = false;
35755 };
35756
35757 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35758     /**
35759      * @cfg {Boolean} singleSelect
35760      * True to allow selection of only one row at a time (defaults to false)
35761      */
35762     singleSelect : false,
35763
35764     // private
35765     initEvents : function(){
35766
35767         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35768             this.grid.on("mousedown", this.handleMouseDown, this);
35769         }else{ // allow click to work like normal
35770             this.grid.on("rowclick", this.handleDragableRowClick, this);
35771         }
35772
35773         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35774             "up" : function(e){
35775                 if(!e.shiftKey){
35776                     this.selectPrevious(e.shiftKey);
35777                 }else if(this.last !== false && this.lastActive !== false){
35778                     var last = this.last;
35779                     this.selectRange(this.last,  this.lastActive-1);
35780                     this.grid.getView().focusRow(this.lastActive);
35781                     if(last !== false){
35782                         this.last = last;
35783                     }
35784                 }else{
35785                     this.selectFirstRow();
35786                 }
35787                 this.fireEvent("afterselectionchange", this);
35788             },
35789             "down" : function(e){
35790                 if(!e.shiftKey){
35791                     this.selectNext(e.shiftKey);
35792                 }else if(this.last !== false && this.lastActive !== false){
35793                     var last = this.last;
35794                     this.selectRange(this.last,  this.lastActive+1);
35795                     this.grid.getView().focusRow(this.lastActive);
35796                     if(last !== false){
35797                         this.last = last;
35798                     }
35799                 }else{
35800                     this.selectFirstRow();
35801                 }
35802                 this.fireEvent("afterselectionchange", this);
35803             },
35804             scope: this
35805         });
35806
35807         var view = this.grid.view;
35808         view.on("refresh", this.onRefresh, this);
35809         view.on("rowupdated", this.onRowUpdated, this);
35810         view.on("rowremoved", this.onRemove, this);
35811     },
35812
35813     // private
35814     onRefresh : function(){
35815         var ds = this.grid.dataSource, i, v = this.grid.view;
35816         var s = this.selections;
35817         s.each(function(r){
35818             if((i = ds.indexOfId(r.id)) != -1){
35819                 v.onRowSelect(i);
35820                 s.add(ds.getAt(i)); // updating the selection relate data
35821             }else{
35822                 s.remove(r);
35823             }
35824         });
35825     },
35826
35827     // private
35828     onRemove : function(v, index, r){
35829         this.selections.remove(r);
35830     },
35831
35832     // private
35833     onRowUpdated : function(v, index, r){
35834         if(this.isSelected(r)){
35835             v.onRowSelect(index);
35836         }
35837     },
35838
35839     /**
35840      * Select records.
35841      * @param {Array} records The records to select
35842      * @param {Boolean} keepExisting (optional) True to keep existing selections
35843      */
35844     selectRecords : function(records, keepExisting){
35845         if(!keepExisting){
35846             this.clearSelections();
35847         }
35848         var ds = this.grid.dataSource;
35849         for(var i = 0, len = records.length; i < len; i++){
35850             this.selectRow(ds.indexOf(records[i]), true);
35851         }
35852     },
35853
35854     /**
35855      * Gets the number of selected rows.
35856      * @return {Number}
35857      */
35858     getCount : function(){
35859         return this.selections.length;
35860     },
35861
35862     /**
35863      * Selects the first row in the grid.
35864      */
35865     selectFirstRow : function(){
35866         this.selectRow(0);
35867     },
35868
35869     /**
35870      * Select the last row.
35871      * @param {Boolean} keepExisting (optional) True to keep existing selections
35872      */
35873     selectLastRow : function(keepExisting){
35874         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35875     },
35876
35877     /**
35878      * Selects the row immediately following the last selected row.
35879      * @param {Boolean} keepExisting (optional) True to keep existing selections
35880      */
35881     selectNext : function(keepExisting){
35882         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35883             this.selectRow(this.last+1, keepExisting);
35884             this.grid.getView().focusRow(this.last);
35885         }
35886     },
35887
35888     /**
35889      * Selects the row that precedes the last selected row.
35890      * @param {Boolean} keepExisting (optional) True to keep existing selections
35891      */
35892     selectPrevious : function(keepExisting){
35893         if(this.last){
35894             this.selectRow(this.last-1, keepExisting);
35895             this.grid.getView().focusRow(this.last);
35896         }
35897     },
35898
35899     /**
35900      * Returns the selected records
35901      * @return {Array} Array of selected records
35902      */
35903     getSelections : function(){
35904         return [].concat(this.selections.items);
35905     },
35906
35907     /**
35908      * Returns the first selected record.
35909      * @return {Record}
35910      */
35911     getSelected : function(){
35912         return this.selections.itemAt(0);
35913     },
35914
35915
35916     /**
35917      * Clears all selections.
35918      */
35919     clearSelections : function(fast){
35920         if(this.locked) {
35921             return;
35922         }
35923         if(fast !== true){
35924             var ds = this.grid.dataSource;
35925             var s = this.selections;
35926             s.each(function(r){
35927                 this.deselectRow(ds.indexOfId(r.id));
35928             }, this);
35929             s.clear();
35930         }else{
35931             this.selections.clear();
35932         }
35933         this.last = false;
35934     },
35935
35936
35937     /**
35938      * Selects all rows.
35939      */
35940     selectAll : function(){
35941         if(this.locked) {
35942             return;
35943         }
35944         this.selections.clear();
35945         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35946             this.selectRow(i, true);
35947         }
35948     },
35949
35950     /**
35951      * Returns True if there is a selection.
35952      * @return {Boolean}
35953      */
35954     hasSelection : function(){
35955         return this.selections.length > 0;
35956     },
35957
35958     /**
35959      * Returns True if the specified row is selected.
35960      * @param {Number/Record} record The record or index of the record to check
35961      * @return {Boolean}
35962      */
35963     isSelected : function(index){
35964         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35965         return (r && this.selections.key(r.id) ? true : false);
35966     },
35967
35968     /**
35969      * Returns True if the specified record id is selected.
35970      * @param {String} id The id of record to check
35971      * @return {Boolean}
35972      */
35973     isIdSelected : function(id){
35974         return (this.selections.key(id) ? true : false);
35975     },
35976
35977     // private
35978     handleMouseDown : function(e, t){
35979         var view = this.grid.getView(), rowIndex;
35980         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35981             return;
35982         };
35983         if(e.shiftKey && this.last !== false){
35984             var last = this.last;
35985             this.selectRange(last, rowIndex, e.ctrlKey);
35986             this.last = last; // reset the last
35987             view.focusRow(rowIndex);
35988         }else{
35989             var isSelected = this.isSelected(rowIndex);
35990             if(e.button !== 0 && isSelected){
35991                 view.focusRow(rowIndex);
35992             }else if(e.ctrlKey && isSelected){
35993                 this.deselectRow(rowIndex);
35994             }else if(!isSelected){
35995                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35996                 view.focusRow(rowIndex);
35997             }
35998         }
35999         this.fireEvent("afterselectionchange", this);
36000     },
36001     // private
36002     handleDragableRowClick :  function(grid, rowIndex, e) 
36003     {
36004         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36005             this.selectRow(rowIndex, false);
36006             grid.view.focusRow(rowIndex);
36007              this.fireEvent("afterselectionchange", this);
36008         }
36009     },
36010     
36011     /**
36012      * Selects multiple rows.
36013      * @param {Array} rows Array of the indexes of the row to select
36014      * @param {Boolean} keepExisting (optional) True to keep existing selections
36015      */
36016     selectRows : function(rows, keepExisting){
36017         if(!keepExisting){
36018             this.clearSelections();
36019         }
36020         for(var i = 0, len = rows.length; i < len; i++){
36021             this.selectRow(rows[i], true);
36022         }
36023     },
36024
36025     /**
36026      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36027      * @param {Number} startRow The index of the first row in the range
36028      * @param {Number} endRow The index of the last row in the range
36029      * @param {Boolean} keepExisting (optional) True to retain existing selections
36030      */
36031     selectRange : function(startRow, endRow, keepExisting){
36032         if(this.locked) {
36033             return;
36034         }
36035         if(!keepExisting){
36036             this.clearSelections();
36037         }
36038         if(startRow <= endRow){
36039             for(var i = startRow; i <= endRow; i++){
36040                 this.selectRow(i, true);
36041             }
36042         }else{
36043             for(var i = startRow; i >= endRow; i--){
36044                 this.selectRow(i, true);
36045             }
36046         }
36047     },
36048
36049     /**
36050      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36051      * @param {Number} startRow The index of the first row in the range
36052      * @param {Number} endRow The index of the last row in the range
36053      */
36054     deselectRange : function(startRow, endRow, preventViewNotify){
36055         if(this.locked) {
36056             return;
36057         }
36058         for(var i = startRow; i <= endRow; i++){
36059             this.deselectRow(i, preventViewNotify);
36060         }
36061     },
36062
36063     /**
36064      * Selects a row.
36065      * @param {Number} row The index of the row to select
36066      * @param {Boolean} keepExisting (optional) True to keep existing selections
36067      */
36068     selectRow : function(index, keepExisting, preventViewNotify){
36069         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
36070             return;
36071         }
36072         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36073             if(!keepExisting || this.singleSelect){
36074                 this.clearSelections();
36075             }
36076             var r = this.grid.dataSource.getAt(index);
36077             this.selections.add(r);
36078             this.last = this.lastActive = index;
36079             if(!preventViewNotify){
36080                 this.grid.getView().onRowSelect(index);
36081             }
36082             this.fireEvent("rowselect", this, index, r);
36083             this.fireEvent("selectionchange", this);
36084         }
36085     },
36086
36087     /**
36088      * Deselects a row.
36089      * @param {Number} row The index of the row to deselect
36090      */
36091     deselectRow : function(index, preventViewNotify){
36092         if(this.locked) {
36093             return;
36094         }
36095         if(this.last == index){
36096             this.last = false;
36097         }
36098         if(this.lastActive == index){
36099             this.lastActive = false;
36100         }
36101         var r = this.grid.dataSource.getAt(index);
36102         this.selections.remove(r);
36103         if(!preventViewNotify){
36104             this.grid.getView().onRowDeselect(index);
36105         }
36106         this.fireEvent("rowdeselect", this, index);
36107         this.fireEvent("selectionchange", this);
36108     },
36109
36110     // private
36111     restoreLast : function(){
36112         if(this._last){
36113             this.last = this._last;
36114         }
36115     },
36116
36117     // private
36118     acceptsNav : function(row, col, cm){
36119         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36120     },
36121
36122     // private
36123     onEditorKey : function(field, e){
36124         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36125         if(k == e.TAB){
36126             e.stopEvent();
36127             ed.completeEdit();
36128             if(e.shiftKey){
36129                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36130             }else{
36131                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36132             }
36133         }else if(k == e.ENTER && !e.ctrlKey){
36134             e.stopEvent();
36135             ed.completeEdit();
36136             if(e.shiftKey){
36137                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36138             }else{
36139                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36140             }
36141         }else if(k == e.ESC){
36142             ed.cancelEdit();
36143         }
36144         if(newCell){
36145             g.startEditing(newCell[0], newCell[1]);
36146         }
36147     }
36148 });/*
36149  * Based on:
36150  * Ext JS Library 1.1.1
36151  * Copyright(c) 2006-2007, Ext JS, LLC.
36152  *
36153  * Originally Released Under LGPL - original licence link has changed is not relivant.
36154  *
36155  * Fork - LGPL
36156  * <script type="text/javascript">
36157  */
36158 /**
36159  * @class Roo.grid.CellSelectionModel
36160  * @extends Roo.grid.AbstractSelectionModel
36161  * This class provides the basic implementation for cell selection in a grid.
36162  * @constructor
36163  * @param {Object} config The object containing the configuration of this model.
36164  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36165  */
36166 Roo.grid.CellSelectionModel = function(config){
36167     Roo.apply(this, config);
36168
36169     this.selection = null;
36170
36171     this.addEvents({
36172         /**
36173              * @event beforerowselect
36174              * Fires before a cell is selected.
36175              * @param {SelectionModel} this
36176              * @param {Number} rowIndex The selected row index
36177              * @param {Number} colIndex The selected cell index
36178              */
36179             "beforecellselect" : true,
36180         /**
36181              * @event cellselect
36182              * Fires when a cell is selected.
36183              * @param {SelectionModel} this
36184              * @param {Number} rowIndex The selected row index
36185              * @param {Number} colIndex The selected cell index
36186              */
36187             "cellselect" : true,
36188         /**
36189              * @event selectionchange
36190              * Fires when the active selection changes.
36191              * @param {SelectionModel} this
36192              * @param {Object} selection null for no selection or an object (o) with two properties
36193                 <ul>
36194                 <li>o.record: the record object for the row the selection is in</li>
36195                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36196                 </ul>
36197              */
36198             "selectionchange" : true,
36199         /**
36200              * @event tabend
36201              * Fires when the tab (or enter) was pressed on the last editable cell
36202              * You can use this to trigger add new row.
36203              * @param {SelectionModel} this
36204              */
36205             "tabend" : true,
36206          /**
36207              * @event beforeeditnext
36208              * Fires before the next editable sell is made active
36209              * You can use this to skip to another cell or fire the tabend
36210              *    if you set cell to false
36211              * @param {Object} eventdata object : { cell : [ row, col ] } 
36212              */
36213             "beforeeditnext" : true
36214     });
36215     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36216 };
36217
36218 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36219     
36220     enter_is_tab: false,
36221
36222     /** @ignore */
36223     initEvents : function(){
36224         this.grid.on("mousedown", this.handleMouseDown, this);
36225         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36226         var view = this.grid.view;
36227         view.on("refresh", this.onViewChange, this);
36228         view.on("rowupdated", this.onRowUpdated, this);
36229         view.on("beforerowremoved", this.clearSelections, this);
36230         view.on("beforerowsinserted", this.clearSelections, this);
36231         if(this.grid.isEditor){
36232             this.grid.on("beforeedit", this.beforeEdit,  this);
36233         }
36234     },
36235
36236         //private
36237     beforeEdit : function(e){
36238         this.select(e.row, e.column, false, true, e.record);
36239     },
36240
36241         //private
36242     onRowUpdated : function(v, index, r){
36243         if(this.selection && this.selection.record == r){
36244             v.onCellSelect(index, this.selection.cell[1]);
36245         }
36246     },
36247
36248         //private
36249     onViewChange : function(){
36250         this.clearSelections(true);
36251     },
36252
36253         /**
36254          * Returns the currently selected cell,.
36255          * @return {Array} The selected cell (row, column) or null if none selected.
36256          */
36257     getSelectedCell : function(){
36258         return this.selection ? this.selection.cell : null;
36259     },
36260
36261     /**
36262      * Clears all selections.
36263      * @param {Boolean} true to prevent the gridview from being notified about the change.
36264      */
36265     clearSelections : function(preventNotify){
36266         var s = this.selection;
36267         if(s){
36268             if(preventNotify !== true){
36269                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36270             }
36271             this.selection = null;
36272             this.fireEvent("selectionchange", this, null);
36273         }
36274     },
36275
36276     /**
36277      * Returns true if there is a selection.
36278      * @return {Boolean}
36279      */
36280     hasSelection : function(){
36281         return this.selection ? true : false;
36282     },
36283
36284     /** @ignore */
36285     handleMouseDown : function(e, t){
36286         var v = this.grid.getView();
36287         if(this.isLocked()){
36288             return;
36289         };
36290         var row = v.findRowIndex(t);
36291         var cell = v.findCellIndex(t);
36292         if(row !== false && cell !== false){
36293             this.select(row, cell);
36294         }
36295     },
36296
36297     /**
36298      * Selects a cell.
36299      * @param {Number} rowIndex
36300      * @param {Number} collIndex
36301      */
36302     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36303         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36304             this.clearSelections();
36305             r = r || this.grid.dataSource.getAt(rowIndex);
36306             this.selection = {
36307                 record : r,
36308                 cell : [rowIndex, colIndex]
36309             };
36310             if(!preventViewNotify){
36311                 var v = this.grid.getView();
36312                 v.onCellSelect(rowIndex, colIndex);
36313                 if(preventFocus !== true){
36314                     v.focusCell(rowIndex, colIndex);
36315                 }
36316             }
36317             this.fireEvent("cellselect", this, rowIndex, colIndex);
36318             this.fireEvent("selectionchange", this, this.selection);
36319         }
36320     },
36321
36322         //private
36323     isSelectable : function(rowIndex, colIndex, cm){
36324         return !cm.isHidden(colIndex);
36325     },
36326
36327     /** @ignore */
36328     handleKeyDown : function(e){
36329         //Roo.log('Cell Sel Model handleKeyDown');
36330         if(!e.isNavKeyPress()){
36331             return;
36332         }
36333         var g = this.grid, s = this.selection;
36334         if(!s){
36335             e.stopEvent();
36336             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36337             if(cell){
36338                 this.select(cell[0], cell[1]);
36339             }
36340             return;
36341         }
36342         var sm = this;
36343         var walk = function(row, col, step){
36344             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36345         };
36346         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36347         var newCell;
36348
36349       
36350
36351         switch(k){
36352             case e.TAB:
36353                 // handled by onEditorKey
36354                 if (g.isEditor && g.editing) {
36355                     return;
36356                 }
36357                 if(e.shiftKey) {
36358                     newCell = walk(r, c-1, -1);
36359                 } else {
36360                     newCell = walk(r, c+1, 1);
36361                 }
36362                 break;
36363             
36364             case e.DOWN:
36365                newCell = walk(r+1, c, 1);
36366                 break;
36367             
36368             case e.UP:
36369                 newCell = walk(r-1, c, -1);
36370                 break;
36371             
36372             case e.RIGHT:
36373                 newCell = walk(r, c+1, 1);
36374                 break;
36375             
36376             case e.LEFT:
36377                 newCell = walk(r, c-1, -1);
36378                 break;
36379             
36380             case e.ENTER:
36381                 
36382                 if(g.isEditor && !g.editing){
36383                    g.startEditing(r, c);
36384                    e.stopEvent();
36385                    return;
36386                 }
36387                 
36388                 
36389              break;
36390         };
36391         if(newCell){
36392             this.select(newCell[0], newCell[1]);
36393             e.stopEvent();
36394             
36395         }
36396     },
36397
36398     acceptsNav : function(row, col, cm){
36399         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36400     },
36401     /**
36402      * Selects a cell.
36403      * @param {Number} field (not used) - as it's normally used as a listener
36404      * @param {Number} e - event - fake it by using
36405      *
36406      * var e = Roo.EventObjectImpl.prototype;
36407      * e.keyCode = e.TAB
36408      *
36409      * 
36410      */
36411     onEditorKey : function(field, e){
36412         
36413         var k = e.getKey(),
36414             newCell,
36415             g = this.grid,
36416             ed = g.activeEditor,
36417             forward = false;
36418         ///Roo.log('onEditorKey' + k);
36419         
36420         
36421         if (this.enter_is_tab && k == e.ENTER) {
36422             k = e.TAB;
36423         }
36424         
36425         if(k == e.TAB){
36426             if(e.shiftKey){
36427                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36428             }else{
36429                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36430                 forward = true;
36431             }
36432             
36433             e.stopEvent();
36434             
36435         } else if(k == e.ENTER &&  !e.ctrlKey){
36436             ed.completeEdit();
36437             e.stopEvent();
36438             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36439         
36440                 } else if(k == e.ESC){
36441             ed.cancelEdit();
36442         }
36443                 
36444         if (newCell) {
36445             var ecall = { cell : newCell, forward : forward };
36446             this.fireEvent('beforeeditnext', ecall );
36447             newCell = ecall.cell;
36448                         forward = ecall.forward;
36449         }
36450                 
36451         if(newCell){
36452             //Roo.log('next cell after edit');
36453             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36454         } else if (forward) {
36455             // tabbed past last
36456             this.fireEvent.defer(100, this, ['tabend',this]);
36457         }
36458     }
36459 });/*
36460  * Based on:
36461  * Ext JS Library 1.1.1
36462  * Copyright(c) 2006-2007, Ext JS, LLC.
36463  *
36464  * Originally Released Under LGPL - original licence link has changed is not relivant.
36465  *
36466  * Fork - LGPL
36467  * <script type="text/javascript">
36468  */
36469  
36470 /**
36471  * @class Roo.grid.EditorGrid
36472  * @extends Roo.grid.Grid
36473  * Class for creating and editable grid.
36474  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36475  * The container MUST have some type of size defined for the grid to fill. The container will be 
36476  * automatically set to position relative if it isn't already.
36477  * @param {Object} dataSource The data model to bind to
36478  * @param {Object} colModel The column model with info about this grid's columns
36479  */
36480 Roo.grid.EditorGrid = function(container, config){
36481     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36482     this.getGridEl().addClass("xedit-grid");
36483
36484     if(!this.selModel){
36485         this.selModel = new Roo.grid.CellSelectionModel();
36486     }
36487
36488     this.activeEditor = null;
36489
36490         this.addEvents({
36491             /**
36492              * @event beforeedit
36493              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36494              * <ul style="padding:5px;padding-left:16px;">
36495              * <li>grid - This grid</li>
36496              * <li>record - The record being edited</li>
36497              * <li>field - The field name being edited</li>
36498              * <li>value - The value for the field being edited.</li>
36499              * <li>row - The grid row index</li>
36500              * <li>column - The grid column index</li>
36501              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36502              * </ul>
36503              * @param {Object} e An edit event (see above for description)
36504              */
36505             "beforeedit" : true,
36506             /**
36507              * @event afteredit
36508              * Fires after a cell is edited. <br />
36509              * <ul style="padding:5px;padding-left:16px;">
36510              * <li>grid - This grid</li>
36511              * <li>record - The record being edited</li>
36512              * <li>field - The field name being edited</li>
36513              * <li>value - The value being set</li>
36514              * <li>originalValue - The original value for the field, before the edit.</li>
36515              * <li>row - The grid row index</li>
36516              * <li>column - The grid column index</li>
36517              * </ul>
36518              * @param {Object} e An edit event (see above for description)
36519              */
36520             "afteredit" : true,
36521             /**
36522              * @event validateedit
36523              * Fires after a cell is edited, but before the value is set in the record. 
36524          * You can use this to modify the value being set in the field, Return false
36525              * to cancel the change. The edit event object has the following properties <br />
36526              * <ul style="padding:5px;padding-left:16px;">
36527          * <li>editor - This editor</li>
36528              * <li>grid - This grid</li>
36529              * <li>record - The record being edited</li>
36530              * <li>field - The field name being edited</li>
36531              * <li>value - The value being set</li>
36532              * <li>originalValue - The original value for the field, before the edit.</li>
36533              * <li>row - The grid row index</li>
36534              * <li>column - The grid column index</li>
36535              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36536              * </ul>
36537              * @param {Object} e An edit event (see above for description)
36538              */
36539             "validateedit" : true
36540         });
36541     this.on("bodyscroll", this.stopEditing,  this);
36542     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36543 };
36544
36545 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36546     /**
36547      * @cfg {Number} clicksToEdit
36548      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36549      */
36550     clicksToEdit: 2,
36551
36552     // private
36553     isEditor : true,
36554     // private
36555     trackMouseOver: false, // causes very odd FF errors
36556
36557     onCellDblClick : function(g, row, col){
36558         this.startEditing(row, col);
36559     },
36560
36561     onEditComplete : function(ed, value, startValue){
36562         this.editing = false;
36563         this.activeEditor = null;
36564         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36565         var r = ed.record;
36566         var field = this.colModel.getDataIndex(ed.col);
36567         var e = {
36568             grid: this,
36569             record: r,
36570             field: field,
36571             originalValue: startValue,
36572             value: value,
36573             row: ed.row,
36574             column: ed.col,
36575             cancel:false,
36576             editor: ed
36577         };
36578         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36579         cell.show();
36580           
36581         if(String(value) !== String(startValue)){
36582             
36583             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36584                 r.set(field, e.value);
36585                 // if we are dealing with a combo box..
36586                 // then we also set the 'name' colum to be the displayField
36587                 if (ed.field.displayField && ed.field.name) {
36588                     r.set(ed.field.name, ed.field.el.dom.value);
36589                 }
36590                 
36591                 delete e.cancel; //?? why!!!
36592                 this.fireEvent("afteredit", e);
36593             }
36594         } else {
36595             this.fireEvent("afteredit", e); // always fire it!
36596         }
36597         this.view.focusCell(ed.row, ed.col);
36598     },
36599
36600     /**
36601      * Starts editing the specified for the specified row/column
36602      * @param {Number} rowIndex
36603      * @param {Number} colIndex
36604      */
36605     startEditing : function(row, col){
36606         this.stopEditing();
36607         if(this.colModel.isCellEditable(col, row)){
36608             this.view.ensureVisible(row, col, true);
36609           
36610             var r = this.dataSource.getAt(row);
36611             var field = this.colModel.getDataIndex(col);
36612             var cell = Roo.get(this.view.getCell(row,col));
36613             var e = {
36614                 grid: this,
36615                 record: r,
36616                 field: field,
36617                 value: r.data[field],
36618                 row: row,
36619                 column: col,
36620                 cancel:false 
36621             };
36622             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36623                 this.editing = true;
36624                 var ed = this.colModel.getCellEditor(col, row);
36625                 
36626                 if (!ed) {
36627                     return;
36628                 }
36629                 if(!ed.rendered){
36630                     ed.render(ed.parentEl || document.body);
36631                 }
36632                 ed.field.reset();
36633                
36634                 cell.hide();
36635                 
36636                 (function(){ // complex but required for focus issues in safari, ie and opera
36637                     ed.row = row;
36638                     ed.col = col;
36639                     ed.record = r;
36640                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36641                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36642                     this.activeEditor = ed;
36643                     var v = r.data[field];
36644                     ed.startEdit(this.view.getCell(row, col), v);
36645                     // combo's with 'displayField and name set
36646                     if (ed.field.displayField && ed.field.name) {
36647                         ed.field.el.dom.value = r.data[ed.field.name];
36648                     }
36649                     
36650                     
36651                 }).defer(50, this);
36652             }
36653         }
36654     },
36655         
36656     /**
36657      * Stops any active editing
36658      */
36659     stopEditing : function(){
36660         if(this.activeEditor){
36661             this.activeEditor.completeEdit();
36662         }
36663         this.activeEditor = null;
36664     },
36665         
36666          /**
36667      * Called to get grid's drag proxy text, by default returns this.ddText.
36668      * @return {String}
36669      */
36670     getDragDropText : function(){
36671         var count = this.selModel.getSelectedCell() ? 1 : 0;
36672         return String.format(this.ddText, count, count == 1 ? '' : 's');
36673     }
36674         
36675 });/*
36676  * Based on:
36677  * Ext JS Library 1.1.1
36678  * Copyright(c) 2006-2007, Ext JS, LLC.
36679  *
36680  * Originally Released Under LGPL - original licence link has changed is not relivant.
36681  *
36682  * Fork - LGPL
36683  * <script type="text/javascript">
36684  */
36685
36686 // private - not really -- you end up using it !
36687 // This is a support class used internally by the Grid components
36688
36689 /**
36690  * @class Roo.grid.GridEditor
36691  * @extends Roo.Editor
36692  * Class for creating and editable grid elements.
36693  * @param {Object} config any settings (must include field)
36694  */
36695 Roo.grid.GridEditor = function(field, config){
36696     if (!config && field.field) {
36697         config = field;
36698         field = Roo.factory(config.field, Roo.form);
36699     }
36700     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36701     field.monitorTab = false;
36702 };
36703
36704 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36705     
36706     /**
36707      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36708      */
36709     
36710     alignment: "tl-tl",
36711     autoSize: "width",
36712     hideEl : false,
36713     cls: "x-small-editor x-grid-editor",
36714     shim:false,
36715     shadow:"frame"
36716 });/*
36717  * Based on:
36718  * Ext JS Library 1.1.1
36719  * Copyright(c) 2006-2007, Ext JS, LLC.
36720  *
36721  * Originally Released Under LGPL - original licence link has changed is not relivant.
36722  *
36723  * Fork - LGPL
36724  * <script type="text/javascript">
36725  */
36726   
36727
36728   
36729 Roo.grid.PropertyRecord = Roo.data.Record.create([
36730     {name:'name',type:'string'},  'value'
36731 ]);
36732
36733
36734 Roo.grid.PropertyStore = function(grid, source){
36735     this.grid = grid;
36736     this.store = new Roo.data.Store({
36737         recordType : Roo.grid.PropertyRecord
36738     });
36739     this.store.on('update', this.onUpdate,  this);
36740     if(source){
36741         this.setSource(source);
36742     }
36743     Roo.grid.PropertyStore.superclass.constructor.call(this);
36744 };
36745
36746
36747
36748 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36749     setSource : function(o){
36750         this.source = o;
36751         this.store.removeAll();
36752         var data = [];
36753         for(var k in o){
36754             if(this.isEditableValue(o[k])){
36755                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36756             }
36757         }
36758         this.store.loadRecords({records: data}, {}, true);
36759     },
36760
36761     onUpdate : function(ds, record, type){
36762         if(type == Roo.data.Record.EDIT){
36763             var v = record.data['value'];
36764             var oldValue = record.modified['value'];
36765             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36766                 this.source[record.id] = v;
36767                 record.commit();
36768                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36769             }else{
36770                 record.reject();
36771             }
36772         }
36773     },
36774
36775     getProperty : function(row){
36776        return this.store.getAt(row);
36777     },
36778
36779     isEditableValue: function(val){
36780         if(val && val instanceof Date){
36781             return true;
36782         }else if(typeof val == 'object' || typeof val == 'function'){
36783             return false;
36784         }
36785         return true;
36786     },
36787
36788     setValue : function(prop, value){
36789         this.source[prop] = value;
36790         this.store.getById(prop).set('value', value);
36791     },
36792
36793     getSource : function(){
36794         return this.source;
36795     }
36796 });
36797
36798 Roo.grid.PropertyColumnModel = function(grid, store){
36799     this.grid = grid;
36800     var g = Roo.grid;
36801     g.PropertyColumnModel.superclass.constructor.call(this, [
36802         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36803         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36804     ]);
36805     this.store = store;
36806     this.bselect = Roo.DomHelper.append(document.body, {
36807         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36808             {tag: 'option', value: 'true', html: 'true'},
36809             {tag: 'option', value: 'false', html: 'false'}
36810         ]
36811     });
36812     Roo.id(this.bselect);
36813     var f = Roo.form;
36814     this.editors = {
36815         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36816         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36817         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36818         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36819         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36820     };
36821     this.renderCellDelegate = this.renderCell.createDelegate(this);
36822     this.renderPropDelegate = this.renderProp.createDelegate(this);
36823 };
36824
36825 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36826     
36827     
36828     nameText : 'Name',
36829     valueText : 'Value',
36830     
36831     dateFormat : 'm/j/Y',
36832     
36833     
36834     renderDate : function(dateVal){
36835         return dateVal.dateFormat(this.dateFormat);
36836     },
36837
36838     renderBool : function(bVal){
36839         return bVal ? 'true' : 'false';
36840     },
36841
36842     isCellEditable : function(colIndex, rowIndex){
36843         return colIndex == 1;
36844     },
36845
36846     getRenderer : function(col){
36847         return col == 1 ?
36848             this.renderCellDelegate : this.renderPropDelegate;
36849     },
36850
36851     renderProp : function(v){
36852         return this.getPropertyName(v);
36853     },
36854
36855     renderCell : function(val){
36856         var rv = val;
36857         if(val instanceof Date){
36858             rv = this.renderDate(val);
36859         }else if(typeof val == 'boolean'){
36860             rv = this.renderBool(val);
36861         }
36862         return Roo.util.Format.htmlEncode(rv);
36863     },
36864
36865     getPropertyName : function(name){
36866         var pn = this.grid.propertyNames;
36867         return pn && pn[name] ? pn[name] : name;
36868     },
36869
36870     getCellEditor : function(colIndex, rowIndex){
36871         var p = this.store.getProperty(rowIndex);
36872         var n = p.data['name'], val = p.data['value'];
36873         
36874         if(typeof(this.grid.customEditors[n]) == 'string'){
36875             return this.editors[this.grid.customEditors[n]];
36876         }
36877         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36878             return this.grid.customEditors[n];
36879         }
36880         if(val instanceof Date){
36881             return this.editors['date'];
36882         }else if(typeof val == 'number'){
36883             return this.editors['number'];
36884         }else if(typeof val == 'boolean'){
36885             return this.editors['boolean'];
36886         }else{
36887             return this.editors['string'];
36888         }
36889     }
36890 });
36891
36892 /**
36893  * @class Roo.grid.PropertyGrid
36894  * @extends Roo.grid.EditorGrid
36895  * This class represents the  interface of a component based property grid control.
36896  * <br><br>Usage:<pre><code>
36897  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36898       
36899  });
36900  // set any options
36901  grid.render();
36902  * </code></pre>
36903   
36904  * @constructor
36905  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36906  * The container MUST have some type of size defined for the grid to fill. The container will be
36907  * automatically set to position relative if it isn't already.
36908  * @param {Object} config A config object that sets properties on this grid.
36909  */
36910 Roo.grid.PropertyGrid = function(container, config){
36911     config = config || {};
36912     var store = new Roo.grid.PropertyStore(this);
36913     this.store = store;
36914     var cm = new Roo.grid.PropertyColumnModel(this, store);
36915     store.store.sort('name', 'ASC');
36916     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36917         ds: store.store,
36918         cm: cm,
36919         enableColLock:false,
36920         enableColumnMove:false,
36921         stripeRows:false,
36922         trackMouseOver: false,
36923         clicksToEdit:1
36924     }, config));
36925     this.getGridEl().addClass('x-props-grid');
36926     this.lastEditRow = null;
36927     this.on('columnresize', this.onColumnResize, this);
36928     this.addEvents({
36929          /**
36930              * @event beforepropertychange
36931              * Fires before a property changes (return false to stop?)
36932              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36933              * @param {String} id Record Id
36934              * @param {String} newval New Value
36935          * @param {String} oldval Old Value
36936              */
36937         "beforepropertychange": true,
36938         /**
36939              * @event propertychange
36940              * Fires after a property changes
36941              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36942              * @param {String} id Record Id
36943              * @param {String} newval New Value
36944          * @param {String} oldval Old Value
36945              */
36946         "propertychange": true
36947     });
36948     this.customEditors = this.customEditors || {};
36949 };
36950 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36951     
36952      /**
36953      * @cfg {Object} customEditors map of colnames=> custom editors.
36954      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36955      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36956      * false disables editing of the field.
36957          */
36958     
36959       /**
36960      * @cfg {Object} propertyNames map of property Names to their displayed value
36961          */
36962     
36963     render : function(){
36964         Roo.grid.PropertyGrid.superclass.render.call(this);
36965         this.autoSize.defer(100, this);
36966     },
36967
36968     autoSize : function(){
36969         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36970         if(this.view){
36971             this.view.fitColumns();
36972         }
36973     },
36974
36975     onColumnResize : function(){
36976         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36977         this.autoSize();
36978     },
36979     /**
36980      * Sets the data for the Grid
36981      * accepts a Key => Value object of all the elements avaiable.
36982      * @param {Object} data  to appear in grid.
36983      */
36984     setSource : function(source){
36985         this.store.setSource(source);
36986         //this.autoSize();
36987     },
36988     /**
36989      * Gets all the data from the grid.
36990      * @return {Object} data  data stored in grid
36991      */
36992     getSource : function(){
36993         return this.store.getSource();
36994     }
36995 });/*
36996   
36997  * Licence LGPL
36998  
36999  */
37000  
37001 /**
37002  * @class Roo.grid.Calendar
37003  * @extends Roo.util.Grid
37004  * This class extends the Grid to provide a calendar widget
37005  * <br><br>Usage:<pre><code>
37006  var grid = new Roo.grid.Calendar("my-container-id", {
37007      ds: myDataStore,
37008      cm: myColModel,
37009      selModel: mySelectionModel,
37010      autoSizeColumns: true,
37011      monitorWindowResize: false,
37012      trackMouseOver: true
37013      eventstore : real data store..
37014  });
37015  // set any options
37016  grid.render();
37017   
37018   * @constructor
37019  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37020  * The container MUST have some type of size defined for the grid to fill. The container will be
37021  * automatically set to position relative if it isn't already.
37022  * @param {Object} config A config object that sets properties on this grid.
37023  */
37024 Roo.grid.Calendar = function(container, config){
37025         // initialize the container
37026         this.container = Roo.get(container);
37027         this.container.update("");
37028         this.container.setStyle("overflow", "hidden");
37029     this.container.addClass('x-grid-container');
37030
37031     this.id = this.container.id;
37032
37033     Roo.apply(this, config);
37034     // check and correct shorthanded configs
37035     
37036     var rows = [];
37037     var d =1;
37038     for (var r = 0;r < 6;r++) {
37039         
37040         rows[r]=[];
37041         for (var c =0;c < 7;c++) {
37042             rows[r][c]= '';
37043         }
37044     }
37045     if (this.eventStore) {
37046         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37047         this.eventStore.on('load',this.onLoad, this);
37048         this.eventStore.on('beforeload',this.clearEvents, this);
37049          
37050     }
37051     
37052     this.dataSource = new Roo.data.Store({
37053             proxy: new Roo.data.MemoryProxy(rows),
37054             reader: new Roo.data.ArrayReader({}, [
37055                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37056     });
37057
37058     this.dataSource.load();
37059     this.ds = this.dataSource;
37060     this.ds.xmodule = this.xmodule || false;
37061     
37062     
37063     var cellRender = function(v,x,r)
37064     {
37065         return String.format(
37066             '<div class="fc-day  fc-widget-content"><div>' +
37067                 '<div class="fc-event-container"></div>' +
37068                 '<div class="fc-day-number">{0}</div>'+
37069                 
37070                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37071             '</div></div>', v);
37072     
37073     }
37074     
37075     
37076     this.colModel = new Roo.grid.ColumnModel( [
37077         {
37078             xtype: 'ColumnModel',
37079             xns: Roo.grid,
37080             dataIndex : 'weekday0',
37081             header : 'Sunday',
37082             renderer : cellRender
37083         },
37084         {
37085             xtype: 'ColumnModel',
37086             xns: Roo.grid,
37087             dataIndex : 'weekday1',
37088             header : 'Monday',
37089             renderer : cellRender
37090         },
37091         {
37092             xtype: 'ColumnModel',
37093             xns: Roo.grid,
37094             dataIndex : 'weekday2',
37095             header : 'Tuesday',
37096             renderer : cellRender
37097         },
37098         {
37099             xtype: 'ColumnModel',
37100             xns: Roo.grid,
37101             dataIndex : 'weekday3',
37102             header : 'Wednesday',
37103             renderer : cellRender
37104         },
37105         {
37106             xtype: 'ColumnModel',
37107             xns: Roo.grid,
37108             dataIndex : 'weekday4',
37109             header : 'Thursday',
37110             renderer : cellRender
37111         },
37112         {
37113             xtype: 'ColumnModel',
37114             xns: Roo.grid,
37115             dataIndex : 'weekday5',
37116             header : 'Friday',
37117             renderer : cellRender
37118         },
37119         {
37120             xtype: 'ColumnModel',
37121             xns: Roo.grid,
37122             dataIndex : 'weekday6',
37123             header : 'Saturday',
37124             renderer : cellRender
37125         }
37126     ]);
37127     this.cm = this.colModel;
37128     this.cm.xmodule = this.xmodule || false;
37129  
37130         
37131           
37132     //this.selModel = new Roo.grid.CellSelectionModel();
37133     //this.sm = this.selModel;
37134     //this.selModel.init(this);
37135     
37136     
37137     if(this.width){
37138         this.container.setWidth(this.width);
37139     }
37140
37141     if(this.height){
37142         this.container.setHeight(this.height);
37143     }
37144     /** @private */
37145         this.addEvents({
37146         // raw events
37147         /**
37148          * @event click
37149          * The raw click event for the entire grid.
37150          * @param {Roo.EventObject} e
37151          */
37152         "click" : true,
37153         /**
37154          * @event dblclick
37155          * The raw dblclick event for the entire grid.
37156          * @param {Roo.EventObject} e
37157          */
37158         "dblclick" : true,
37159         /**
37160          * @event contextmenu
37161          * The raw contextmenu event for the entire grid.
37162          * @param {Roo.EventObject} e
37163          */
37164         "contextmenu" : true,
37165         /**
37166          * @event mousedown
37167          * The raw mousedown event for the entire grid.
37168          * @param {Roo.EventObject} e
37169          */
37170         "mousedown" : true,
37171         /**
37172          * @event mouseup
37173          * The raw mouseup event for the entire grid.
37174          * @param {Roo.EventObject} e
37175          */
37176         "mouseup" : true,
37177         /**
37178          * @event mouseover
37179          * The raw mouseover event for the entire grid.
37180          * @param {Roo.EventObject} e
37181          */
37182         "mouseover" : true,
37183         /**
37184          * @event mouseout
37185          * The raw mouseout event for the entire grid.
37186          * @param {Roo.EventObject} e
37187          */
37188         "mouseout" : true,
37189         /**
37190          * @event keypress
37191          * The raw keypress event for the entire grid.
37192          * @param {Roo.EventObject} e
37193          */
37194         "keypress" : true,
37195         /**
37196          * @event keydown
37197          * The raw keydown event for the entire grid.
37198          * @param {Roo.EventObject} e
37199          */
37200         "keydown" : true,
37201
37202         // custom events
37203
37204         /**
37205          * @event cellclick
37206          * Fires when a cell is clicked
37207          * @param {Grid} this
37208          * @param {Number} rowIndex
37209          * @param {Number} columnIndex
37210          * @param {Roo.EventObject} e
37211          */
37212         "cellclick" : true,
37213         /**
37214          * @event celldblclick
37215          * Fires when a cell is double clicked
37216          * @param {Grid} this
37217          * @param {Number} rowIndex
37218          * @param {Number} columnIndex
37219          * @param {Roo.EventObject} e
37220          */
37221         "celldblclick" : true,
37222         /**
37223          * @event rowclick
37224          * Fires when a row is clicked
37225          * @param {Grid} this
37226          * @param {Number} rowIndex
37227          * @param {Roo.EventObject} e
37228          */
37229         "rowclick" : true,
37230         /**
37231          * @event rowdblclick
37232          * Fires when a row is double clicked
37233          * @param {Grid} this
37234          * @param {Number} rowIndex
37235          * @param {Roo.EventObject} e
37236          */
37237         "rowdblclick" : true,
37238         /**
37239          * @event headerclick
37240          * Fires when a header is clicked
37241          * @param {Grid} this
37242          * @param {Number} columnIndex
37243          * @param {Roo.EventObject} e
37244          */
37245         "headerclick" : true,
37246         /**
37247          * @event headerdblclick
37248          * Fires when a header cell is double clicked
37249          * @param {Grid} this
37250          * @param {Number} columnIndex
37251          * @param {Roo.EventObject} e
37252          */
37253         "headerdblclick" : true,
37254         /**
37255          * @event rowcontextmenu
37256          * Fires when a row is right clicked
37257          * @param {Grid} this
37258          * @param {Number} rowIndex
37259          * @param {Roo.EventObject} e
37260          */
37261         "rowcontextmenu" : true,
37262         /**
37263          * @event cellcontextmenu
37264          * Fires when a cell is right clicked
37265          * @param {Grid} this
37266          * @param {Number} rowIndex
37267          * @param {Number} cellIndex
37268          * @param {Roo.EventObject} e
37269          */
37270          "cellcontextmenu" : true,
37271         /**
37272          * @event headercontextmenu
37273          * Fires when a header is right clicked
37274          * @param {Grid} this
37275          * @param {Number} columnIndex
37276          * @param {Roo.EventObject} e
37277          */
37278         "headercontextmenu" : true,
37279         /**
37280          * @event bodyscroll
37281          * Fires when the body element is scrolled
37282          * @param {Number} scrollLeft
37283          * @param {Number} scrollTop
37284          */
37285         "bodyscroll" : true,
37286         /**
37287          * @event columnresize
37288          * Fires when the user resizes a column
37289          * @param {Number} columnIndex
37290          * @param {Number} newSize
37291          */
37292         "columnresize" : true,
37293         /**
37294          * @event columnmove
37295          * Fires when the user moves a column
37296          * @param {Number} oldIndex
37297          * @param {Number} newIndex
37298          */
37299         "columnmove" : true,
37300         /**
37301          * @event startdrag
37302          * Fires when row(s) start being dragged
37303          * @param {Grid} this
37304          * @param {Roo.GridDD} dd The drag drop object
37305          * @param {event} e The raw browser event
37306          */
37307         "startdrag" : true,
37308         /**
37309          * @event enddrag
37310          * Fires when a drag operation is complete
37311          * @param {Grid} this
37312          * @param {Roo.GridDD} dd The drag drop object
37313          * @param {event} e The raw browser event
37314          */
37315         "enddrag" : true,
37316         /**
37317          * @event dragdrop
37318          * Fires when dragged row(s) are dropped on a valid DD target
37319          * @param {Grid} this
37320          * @param {Roo.GridDD} dd The drag drop object
37321          * @param {String} targetId The target drag drop object
37322          * @param {event} e The raw browser event
37323          */
37324         "dragdrop" : true,
37325         /**
37326          * @event dragover
37327          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37328          * @param {Grid} this
37329          * @param {Roo.GridDD} dd The drag drop object
37330          * @param {String} targetId The target drag drop object
37331          * @param {event} e The raw browser event
37332          */
37333         "dragover" : true,
37334         /**
37335          * @event dragenter
37336          *  Fires when the dragged row(s) first cross another DD target while being dragged
37337          * @param {Grid} this
37338          * @param {Roo.GridDD} dd The drag drop object
37339          * @param {String} targetId The target drag drop object
37340          * @param {event} e The raw browser event
37341          */
37342         "dragenter" : true,
37343         /**
37344          * @event dragout
37345          * Fires when the dragged row(s) leave another DD target while being dragged
37346          * @param {Grid} this
37347          * @param {Roo.GridDD} dd The drag drop object
37348          * @param {String} targetId The target drag drop object
37349          * @param {event} e The raw browser event
37350          */
37351         "dragout" : true,
37352         /**
37353          * @event rowclass
37354          * Fires when a row is rendered, so you can change add a style to it.
37355          * @param {GridView} gridview   The grid view
37356          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37357          */
37358         'rowclass' : true,
37359
37360         /**
37361          * @event render
37362          * Fires when the grid is rendered
37363          * @param {Grid} grid
37364          */
37365         'render' : true,
37366             /**
37367              * @event select
37368              * Fires when a date is selected
37369              * @param {DatePicker} this
37370              * @param {Date} date The selected date
37371              */
37372         'select': true,
37373         /**
37374              * @event monthchange
37375              * Fires when the displayed month changes 
37376              * @param {DatePicker} this
37377              * @param {Date} date The selected month
37378              */
37379         'monthchange': true,
37380         /**
37381              * @event evententer
37382              * Fires when mouse over an event
37383              * @param {Calendar} this
37384              * @param {event} Event
37385              */
37386         'evententer': true,
37387         /**
37388              * @event eventleave
37389              * Fires when the mouse leaves an
37390              * @param {Calendar} this
37391              * @param {event}
37392              */
37393         'eventleave': true,
37394         /**
37395              * @event eventclick
37396              * Fires when the mouse click an
37397              * @param {Calendar} this
37398              * @param {event}
37399              */
37400         'eventclick': true,
37401         /**
37402              * @event eventrender
37403              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37404              * @param {Calendar} this
37405              * @param {data} data to be modified
37406              */
37407         'eventrender': true
37408         
37409     });
37410
37411     Roo.grid.Grid.superclass.constructor.call(this);
37412     this.on('render', function() {
37413         this.view.el.addClass('x-grid-cal'); 
37414         
37415         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37416
37417     },this);
37418     
37419     if (!Roo.grid.Calendar.style) {
37420         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37421             
37422             
37423             '.x-grid-cal .x-grid-col' :  {
37424                 height: 'auto !important',
37425                 'vertical-align': 'top'
37426             },
37427             '.x-grid-cal  .fc-event-hori' : {
37428                 height: '14px'
37429             }
37430              
37431             
37432         }, Roo.id());
37433     }
37434
37435     
37436     
37437 };
37438 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37439     /**
37440      * @cfg {Store} eventStore The store that loads events.
37441      */
37442     eventStore : 25,
37443
37444      
37445     activeDate : false,
37446     startDay : 0,
37447     autoWidth : true,
37448     monitorWindowResize : false,
37449
37450     
37451     resizeColumns : function() {
37452         var col = (this.view.el.getWidth() / 7) - 3;
37453         // loop through cols, and setWidth
37454         for(var i =0 ; i < 7 ; i++){
37455             this.cm.setColumnWidth(i, col);
37456         }
37457     },
37458      setDate :function(date) {
37459         
37460         Roo.log('setDate?');
37461         
37462         this.resizeColumns();
37463         var vd = this.activeDate;
37464         this.activeDate = date;
37465 //        if(vd && this.el){
37466 //            var t = date.getTime();
37467 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37468 //                Roo.log('using add remove');
37469 //                
37470 //                this.fireEvent('monthchange', this, date);
37471 //                
37472 //                this.cells.removeClass("fc-state-highlight");
37473 //                this.cells.each(function(c){
37474 //                   if(c.dateValue == t){
37475 //                       c.addClass("fc-state-highlight");
37476 //                       setTimeout(function(){
37477 //                            try{c.dom.firstChild.focus();}catch(e){}
37478 //                       }, 50);
37479 //                       return false;
37480 //                   }
37481 //                   return true;
37482 //                });
37483 //                return;
37484 //            }
37485 //        }
37486         
37487         var days = date.getDaysInMonth();
37488         
37489         var firstOfMonth = date.getFirstDateOfMonth();
37490         var startingPos = firstOfMonth.getDay()-this.startDay;
37491         
37492         if(startingPos < this.startDay){
37493             startingPos += 7;
37494         }
37495         
37496         var pm = date.add(Date.MONTH, -1);
37497         var prevStart = pm.getDaysInMonth()-startingPos;
37498 //        
37499         
37500         
37501         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37502         
37503         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37504         //this.cells.addClassOnOver('fc-state-hover');
37505         
37506         var cells = this.cells.elements;
37507         var textEls = this.textNodes;
37508         
37509         //Roo.each(cells, function(cell){
37510         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37511         //});
37512         
37513         days += startingPos;
37514
37515         // convert everything to numbers so it's fast
37516         var day = 86400000;
37517         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37518         //Roo.log(d);
37519         //Roo.log(pm);
37520         //Roo.log(prevStart);
37521         
37522         var today = new Date().clearTime().getTime();
37523         var sel = date.clearTime().getTime();
37524         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37525         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37526         var ddMatch = this.disabledDatesRE;
37527         var ddText = this.disabledDatesText;
37528         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37529         var ddaysText = this.disabledDaysText;
37530         var format = this.format;
37531         
37532         var setCellClass = function(cal, cell){
37533             
37534             //Roo.log('set Cell Class');
37535             cell.title = "";
37536             var t = d.getTime();
37537             
37538             //Roo.log(d);
37539             
37540             
37541             cell.dateValue = t;
37542             if(t == today){
37543                 cell.className += " fc-today";
37544                 cell.className += " fc-state-highlight";
37545                 cell.title = cal.todayText;
37546             }
37547             if(t == sel){
37548                 // disable highlight in other month..
37549                 cell.className += " fc-state-highlight";
37550                 
37551             }
37552             // disabling
37553             if(t < min) {
37554                 //cell.className = " fc-state-disabled";
37555                 cell.title = cal.minText;
37556                 return;
37557             }
37558             if(t > max) {
37559                 //cell.className = " fc-state-disabled";
37560                 cell.title = cal.maxText;
37561                 return;
37562             }
37563             if(ddays){
37564                 if(ddays.indexOf(d.getDay()) != -1){
37565                     // cell.title = ddaysText;
37566                    // cell.className = " fc-state-disabled";
37567                 }
37568             }
37569             if(ddMatch && format){
37570                 var fvalue = d.dateFormat(format);
37571                 if(ddMatch.test(fvalue)){
37572                     cell.title = ddText.replace("%0", fvalue);
37573                    cell.className = " fc-state-disabled";
37574                 }
37575             }
37576             
37577             if (!cell.initialClassName) {
37578                 cell.initialClassName = cell.dom.className;
37579             }
37580             
37581             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37582         };
37583
37584         var i = 0;
37585         
37586         for(; i < startingPos; i++) {
37587             cells[i].dayName =  (++prevStart);
37588             Roo.log(textEls[i]);
37589             d.setDate(d.getDate()+1);
37590             
37591             //cells[i].className = "fc-past fc-other-month";
37592             setCellClass(this, cells[i]);
37593         }
37594         
37595         var intDay = 0;
37596         
37597         for(; i < days; i++){
37598             intDay = i - startingPos + 1;
37599             cells[i].dayName =  (intDay);
37600             d.setDate(d.getDate()+1);
37601             
37602             cells[i].className = ''; // "x-date-active";
37603             setCellClass(this, cells[i]);
37604         }
37605         var extraDays = 0;
37606         
37607         for(; i < 42; i++) {
37608             //textEls[i].innerHTML = (++extraDays);
37609             
37610             d.setDate(d.getDate()+1);
37611             cells[i].dayName = (++extraDays);
37612             cells[i].className = "fc-future fc-other-month";
37613             setCellClass(this, cells[i]);
37614         }
37615         
37616         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37617         
37618         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37619         
37620         // this will cause all the cells to mis
37621         var rows= [];
37622         var i =0;
37623         for (var r = 0;r < 6;r++) {
37624             for (var c =0;c < 7;c++) {
37625                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37626             }    
37627         }
37628         
37629         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37630         for(i=0;i<cells.length;i++) {
37631             
37632             this.cells.elements[i].dayName = cells[i].dayName ;
37633             this.cells.elements[i].className = cells[i].className;
37634             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37635             this.cells.elements[i].title = cells[i].title ;
37636             this.cells.elements[i].dateValue = cells[i].dateValue ;
37637         }
37638         
37639         
37640         
37641         
37642         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37643         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37644         
37645         ////if(totalRows != 6){
37646             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37647            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37648        // }
37649         
37650         this.fireEvent('monthchange', this, date);
37651         
37652         
37653     },
37654  /**
37655      * Returns the grid's SelectionModel.
37656      * @return {SelectionModel}
37657      */
37658     getSelectionModel : function(){
37659         if(!this.selModel){
37660             this.selModel = new Roo.grid.CellSelectionModel();
37661         }
37662         return this.selModel;
37663     },
37664
37665     load: function() {
37666         this.eventStore.load()
37667         
37668         
37669         
37670     },
37671     
37672     findCell : function(dt) {
37673         dt = dt.clearTime().getTime();
37674         var ret = false;
37675         this.cells.each(function(c){
37676             //Roo.log("check " +c.dateValue + '?=' + dt);
37677             if(c.dateValue == dt){
37678                 ret = c;
37679                 return false;
37680             }
37681             return true;
37682         });
37683         
37684         return ret;
37685     },
37686     
37687     findCells : function(rec) {
37688         var s = rec.data.start_dt.clone().clearTime().getTime();
37689        // Roo.log(s);
37690         var e= rec.data.end_dt.clone().clearTime().getTime();
37691        // Roo.log(e);
37692         var ret = [];
37693         this.cells.each(function(c){
37694              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37695             
37696             if(c.dateValue > e){
37697                 return ;
37698             }
37699             if(c.dateValue < s){
37700                 return ;
37701             }
37702             ret.push(c);
37703         });
37704         
37705         return ret;    
37706     },
37707     
37708     findBestRow: function(cells)
37709     {
37710         var ret = 0;
37711         
37712         for (var i =0 ; i < cells.length;i++) {
37713             ret  = Math.max(cells[i].rows || 0,ret);
37714         }
37715         return ret;
37716         
37717     },
37718     
37719     
37720     addItem : function(rec)
37721     {
37722         // look for vertical location slot in
37723         var cells = this.findCells(rec);
37724         
37725         rec.row = this.findBestRow(cells);
37726         
37727         // work out the location.
37728         
37729         var crow = false;
37730         var rows = [];
37731         for(var i =0; i < cells.length; i++) {
37732             if (!crow) {
37733                 crow = {
37734                     start : cells[i],
37735                     end :  cells[i]
37736                 };
37737                 continue;
37738             }
37739             if (crow.start.getY() == cells[i].getY()) {
37740                 // on same row.
37741                 crow.end = cells[i];
37742                 continue;
37743             }
37744             // different row.
37745             rows.push(crow);
37746             crow = {
37747                 start: cells[i],
37748                 end : cells[i]
37749             };
37750             
37751         }
37752         
37753         rows.push(crow);
37754         rec.els = [];
37755         rec.rows = rows;
37756         rec.cells = cells;
37757         for (var i = 0; i < cells.length;i++) {
37758             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37759             
37760         }
37761         
37762         
37763     },
37764     
37765     clearEvents: function() {
37766         
37767         if (!this.eventStore.getCount()) {
37768             return;
37769         }
37770         // reset number of rows in cells.
37771         Roo.each(this.cells.elements, function(c){
37772             c.rows = 0;
37773         });
37774         
37775         this.eventStore.each(function(e) {
37776             this.clearEvent(e);
37777         },this);
37778         
37779     },
37780     
37781     clearEvent : function(ev)
37782     {
37783         if (ev.els) {
37784             Roo.each(ev.els, function(el) {
37785                 el.un('mouseenter' ,this.onEventEnter, this);
37786                 el.un('mouseleave' ,this.onEventLeave, this);
37787                 el.remove();
37788             },this);
37789             ev.els = [];
37790         }
37791     },
37792     
37793     
37794     renderEvent : function(ev,ctr) {
37795         if (!ctr) {
37796              ctr = this.view.el.select('.fc-event-container',true).first();
37797         }
37798         
37799          
37800         this.clearEvent(ev);
37801             //code
37802        
37803         
37804         
37805         ev.els = [];
37806         var cells = ev.cells;
37807         var rows = ev.rows;
37808         this.fireEvent('eventrender', this, ev);
37809         
37810         for(var i =0; i < rows.length; i++) {
37811             
37812             cls = '';
37813             if (i == 0) {
37814                 cls += ' fc-event-start';
37815             }
37816             if ((i+1) == rows.length) {
37817                 cls += ' fc-event-end';
37818             }
37819             
37820             //Roo.log(ev.data);
37821             // how many rows should it span..
37822             var cg = this.eventTmpl.append(ctr,Roo.apply({
37823                 fccls : cls
37824                 
37825             }, ev.data) , true);
37826             
37827             
37828             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37829             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37830             cg.on('click', this.onEventClick, this, ev);
37831             
37832             ev.els.push(cg);
37833             
37834             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37835             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37836             //Roo.log(cg);
37837              
37838             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37839             cg.setWidth(ebox.right - sbox.x -2);
37840         }
37841     },
37842     
37843     renderEvents: function()
37844     {   
37845         // first make sure there is enough space..
37846         
37847         if (!this.eventTmpl) {
37848             this.eventTmpl = new Roo.Template(
37849                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37850                     '<div class="fc-event-inner">' +
37851                         '<span class="fc-event-time">{time}</span>' +
37852                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37853                     '</div>' +
37854                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37855                 '</div>'
37856             );
37857                 
37858         }
37859                
37860         
37861         
37862         this.cells.each(function(c) {
37863             //Roo.log(c.select('.fc-day-content div',true).first());
37864             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37865         });
37866         
37867         var ctr = this.view.el.select('.fc-event-container',true).first();
37868         
37869         var cls;
37870         this.eventStore.each(function(ev){
37871             
37872             this.renderEvent(ev);
37873              
37874              
37875         }, this);
37876         this.view.layout();
37877         
37878     },
37879     
37880     onEventEnter: function (e, el,event,d) {
37881         this.fireEvent('evententer', this, el, event);
37882     },
37883     
37884     onEventLeave: function (e, el,event,d) {
37885         this.fireEvent('eventleave', this, el, event);
37886     },
37887     
37888     onEventClick: function (e, el,event,d) {
37889         this.fireEvent('eventclick', this, el, event);
37890     },
37891     
37892     onMonthChange: function () {
37893         this.store.load();
37894     },
37895     
37896     onLoad: function () {
37897         
37898         //Roo.log('calendar onload');
37899 //         
37900         if(this.eventStore.getCount() > 0){
37901             
37902            
37903             
37904             this.eventStore.each(function(d){
37905                 
37906                 
37907                 // FIXME..
37908                 var add =   d.data;
37909                 if (typeof(add.end_dt) == 'undefined')  {
37910                     Roo.log("Missing End time in calendar data: ");
37911                     Roo.log(d);
37912                     return;
37913                 }
37914                 if (typeof(add.start_dt) == 'undefined')  {
37915                     Roo.log("Missing Start time in calendar data: ");
37916                     Roo.log(d);
37917                     return;
37918                 }
37919                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37920                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37921                 add.id = add.id || d.id;
37922                 add.title = add.title || '??';
37923                 
37924                 this.addItem(d);
37925                 
37926              
37927             },this);
37928         }
37929         
37930         this.renderEvents();
37931     }
37932     
37933
37934 });
37935 /*
37936  grid : {
37937                 xtype: 'Grid',
37938                 xns: Roo.grid,
37939                 listeners : {
37940                     render : function ()
37941                     {
37942                         _this.grid = this;
37943                         
37944                         if (!this.view.el.hasClass('course-timesheet')) {
37945                             this.view.el.addClass('course-timesheet');
37946                         }
37947                         if (this.tsStyle) {
37948                             this.ds.load({});
37949                             return; 
37950                         }
37951                         Roo.log('width');
37952                         Roo.log(_this.grid.view.el.getWidth());
37953                         
37954                         
37955                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37956                             '.course-timesheet .x-grid-row' : {
37957                                 height: '80px'
37958                             },
37959                             '.x-grid-row td' : {
37960                                 'vertical-align' : 0
37961                             },
37962                             '.course-edit-link' : {
37963                                 'color' : 'blue',
37964                                 'text-overflow' : 'ellipsis',
37965                                 'overflow' : 'hidden',
37966                                 'white-space' : 'nowrap',
37967                                 'cursor' : 'pointer'
37968                             },
37969                             '.sub-link' : {
37970                                 'color' : 'green'
37971                             },
37972                             '.de-act-sup-link' : {
37973                                 'color' : 'purple',
37974                                 'text-decoration' : 'line-through'
37975                             },
37976                             '.de-act-link' : {
37977                                 'color' : 'red',
37978                                 'text-decoration' : 'line-through'
37979                             },
37980                             '.course-timesheet .course-highlight' : {
37981                                 'border-top-style': 'dashed !important',
37982                                 'border-bottom-bottom': 'dashed !important'
37983                             },
37984                             '.course-timesheet .course-item' : {
37985                                 'font-family'   : 'tahoma, arial, helvetica',
37986                                 'font-size'     : '11px',
37987                                 'overflow'      : 'hidden',
37988                                 'padding-left'  : '10px',
37989                                 'padding-right' : '10px',
37990                                 'padding-top' : '10px' 
37991                             }
37992                             
37993                         }, Roo.id());
37994                                 this.ds.load({});
37995                     }
37996                 },
37997                 autoWidth : true,
37998                 monitorWindowResize : false,
37999                 cellrenderer : function(v,x,r)
38000                 {
38001                     return v;
38002                 },
38003                 sm : {
38004                     xtype: 'CellSelectionModel',
38005                     xns: Roo.grid
38006                 },
38007                 dataSource : {
38008                     xtype: 'Store',
38009                     xns: Roo.data,
38010                     listeners : {
38011                         beforeload : function (_self, options)
38012                         {
38013                             options.params = options.params || {};
38014                             options.params._month = _this.monthField.getValue();
38015                             options.params.limit = 9999;
38016                             options.params['sort'] = 'when_dt';    
38017                             options.params['dir'] = 'ASC';    
38018                             this.proxy.loadResponse = this.loadResponse;
38019                             Roo.log("load?");
38020                             //this.addColumns();
38021                         },
38022                         load : function (_self, records, options)
38023                         {
38024                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38025                                 // if you click on the translation.. you can edit it...
38026                                 var el = Roo.get(this);
38027                                 var id = el.dom.getAttribute('data-id');
38028                                 var d = el.dom.getAttribute('data-date');
38029                                 var t = el.dom.getAttribute('data-time');
38030                                 //var id = this.child('span').dom.textContent;
38031                                 
38032                                 //Roo.log(this);
38033                                 Pman.Dialog.CourseCalendar.show({
38034                                     id : id,
38035                                     when_d : d,
38036                                     when_t : t,
38037                                     productitem_active : id ? 1 : 0
38038                                 }, function() {
38039                                     _this.grid.ds.load({});
38040                                 });
38041                            
38042                            });
38043                            
38044                            _this.panel.fireEvent('resize', [ '', '' ]);
38045                         }
38046                     },
38047                     loadResponse : function(o, success, response){
38048                             // this is overridden on before load..
38049                             
38050                             Roo.log("our code?");       
38051                             //Roo.log(success);
38052                             //Roo.log(response)
38053                             delete this.activeRequest;
38054                             if(!success){
38055                                 this.fireEvent("loadexception", this, o, response);
38056                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38057                                 return;
38058                             }
38059                             var result;
38060                             try {
38061                                 result = o.reader.read(response);
38062                             }catch(e){
38063                                 Roo.log("load exception?");
38064                                 this.fireEvent("loadexception", this, o, response, e);
38065                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38066                                 return;
38067                             }
38068                             Roo.log("ready...");        
38069                             // loop through result.records;
38070                             // and set this.tdate[date] = [] << array of records..
38071                             _this.tdata  = {};
38072                             Roo.each(result.records, function(r){
38073                                 //Roo.log(r.data);
38074                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38075                                     _this.tdata[r.data.when_dt.format('j')] = [];
38076                                 }
38077                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38078                             });
38079                             
38080                             //Roo.log(_this.tdata);
38081                             
38082                             result.records = [];
38083                             result.totalRecords = 6;
38084                     
38085                             // let's generate some duumy records for the rows.
38086                             //var st = _this.dateField.getValue();
38087                             
38088                             // work out monday..
38089                             //st = st.add(Date.DAY, -1 * st.format('w'));
38090                             
38091                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38092                             
38093                             var firstOfMonth = date.getFirstDayOfMonth();
38094                             var days = date.getDaysInMonth();
38095                             var d = 1;
38096                             var firstAdded = false;
38097                             for (var i = 0; i < result.totalRecords ; i++) {
38098                                 //var d= st.add(Date.DAY, i);
38099                                 var row = {};
38100                                 var added = 0;
38101                                 for(var w = 0 ; w < 7 ; w++){
38102                                     if(!firstAdded && firstOfMonth != w){
38103                                         continue;
38104                                     }
38105                                     if(d > days){
38106                                         continue;
38107                                     }
38108                                     firstAdded = true;
38109                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38110                                     row['weekday'+w] = String.format(
38111                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38112                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38113                                                     d,
38114                                                     date.format('Y-m-')+dd
38115                                                 );
38116                                     added++;
38117                                     if(typeof(_this.tdata[d]) != 'undefined'){
38118                                         Roo.each(_this.tdata[d], function(r){
38119                                             var is_sub = '';
38120                                             var deactive = '';
38121                                             var id = r.id;
38122                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38123                                             if(r.parent_id*1>0){
38124                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38125                                                 id = r.parent_id;
38126                                             }
38127                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38128                                                 deactive = 'de-act-link';
38129                                             }
38130                                             
38131                                             row['weekday'+w] += String.format(
38132                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38133                                                     id, //0
38134                                                     r.product_id_name, //1
38135                                                     r.when_dt.format('h:ia'), //2
38136                                                     is_sub, //3
38137                                                     deactive, //4
38138                                                     desc // 5
38139                                             );
38140                                         });
38141                                     }
38142                                     d++;
38143                                 }
38144                                 
38145                                 // only do this if something added..
38146                                 if(added > 0){ 
38147                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38148                                 }
38149                                 
38150                                 
38151                                 // push it twice. (second one with an hour..
38152                                 
38153                             }
38154                             //Roo.log(result);
38155                             this.fireEvent("load", this, o, o.request.arg);
38156                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38157                         },
38158                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38159                     proxy : {
38160                         xtype: 'HttpProxy',
38161                         xns: Roo.data,
38162                         method : 'GET',
38163                         url : baseURL + '/Roo/Shop_course.php'
38164                     },
38165                     reader : {
38166                         xtype: 'JsonReader',
38167                         xns: Roo.data,
38168                         id : 'id',
38169                         fields : [
38170                             {
38171                                 'name': 'id',
38172                                 'type': 'int'
38173                             },
38174                             {
38175                                 'name': 'when_dt',
38176                                 'type': 'string'
38177                             },
38178                             {
38179                                 'name': 'end_dt',
38180                                 'type': 'string'
38181                             },
38182                             {
38183                                 'name': 'parent_id',
38184                                 'type': 'int'
38185                             },
38186                             {
38187                                 'name': 'product_id',
38188                                 'type': 'int'
38189                             },
38190                             {
38191                                 'name': 'productitem_id',
38192                                 'type': 'int'
38193                             },
38194                             {
38195                                 'name': 'guid',
38196                                 'type': 'int'
38197                             }
38198                         ]
38199                     }
38200                 },
38201                 toolbar : {
38202                     xtype: 'Toolbar',
38203                     xns: Roo,
38204                     items : [
38205                         {
38206                             xtype: 'Button',
38207                             xns: Roo.Toolbar,
38208                             listeners : {
38209                                 click : function (_self, e)
38210                                 {
38211                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38212                                     sd.setMonth(sd.getMonth()-1);
38213                                     _this.monthField.setValue(sd.format('Y-m-d'));
38214                                     _this.grid.ds.load({});
38215                                 }
38216                             },
38217                             text : "Back"
38218                         },
38219                         {
38220                             xtype: 'Separator',
38221                             xns: Roo.Toolbar
38222                         },
38223                         {
38224                             xtype: 'MonthField',
38225                             xns: Roo.form,
38226                             listeners : {
38227                                 render : function (_self)
38228                                 {
38229                                     _this.monthField = _self;
38230                                    // _this.monthField.set  today
38231                                 },
38232                                 select : function (combo, date)
38233                                 {
38234                                     _this.grid.ds.load({});
38235                                 }
38236                             },
38237                             value : (function() { return new Date(); })()
38238                         },
38239                         {
38240                             xtype: 'Separator',
38241                             xns: Roo.Toolbar
38242                         },
38243                         {
38244                             xtype: 'TextItem',
38245                             xns: Roo.Toolbar,
38246                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38247                         },
38248                         {
38249                             xtype: 'Fill',
38250                             xns: Roo.Toolbar
38251                         },
38252                         {
38253                             xtype: 'Button',
38254                             xns: Roo.Toolbar,
38255                             listeners : {
38256                                 click : function (_self, e)
38257                                 {
38258                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38259                                     sd.setMonth(sd.getMonth()+1);
38260                                     _this.monthField.setValue(sd.format('Y-m-d'));
38261                                     _this.grid.ds.load({});
38262                                 }
38263                             },
38264                             text : "Next"
38265                         }
38266                     ]
38267                 },
38268                  
38269             }
38270         };
38271         
38272         *//*
38273  * Based on:
38274  * Ext JS Library 1.1.1
38275  * Copyright(c) 2006-2007, Ext JS, LLC.
38276  *
38277  * Originally Released Under LGPL - original licence link has changed is not relivant.
38278  *
38279  * Fork - LGPL
38280  * <script type="text/javascript">
38281  */
38282  
38283 /**
38284  * @class Roo.LoadMask
38285  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38286  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38287  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38288  * element's UpdateManager load indicator and will be destroyed after the initial load.
38289  * @constructor
38290  * Create a new LoadMask
38291  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38292  * @param {Object} config The config object
38293  */
38294 Roo.LoadMask = function(el, config){
38295     this.el = Roo.get(el);
38296     Roo.apply(this, config);
38297     if(this.store){
38298         this.store.on('beforeload', this.onBeforeLoad, this);
38299         this.store.on('load', this.onLoad, this);
38300         this.store.on('loadexception', this.onLoadException, this);
38301         this.removeMask = false;
38302     }else{
38303         var um = this.el.getUpdateManager();
38304         um.showLoadIndicator = false; // disable the default indicator
38305         um.on('beforeupdate', this.onBeforeLoad, this);
38306         um.on('update', this.onLoad, this);
38307         um.on('failure', this.onLoad, this);
38308         this.removeMask = true;
38309     }
38310 };
38311
38312 Roo.LoadMask.prototype = {
38313     /**
38314      * @cfg {Boolean} removeMask
38315      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38316      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38317      */
38318     /**
38319      * @cfg {String} msg
38320      * The text to display in a centered loading message box (defaults to 'Loading...')
38321      */
38322     msg : 'Loading...',
38323     /**
38324      * @cfg {String} msgCls
38325      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38326      */
38327     msgCls : 'x-mask-loading',
38328
38329     /**
38330      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38331      * @type Boolean
38332      */
38333     disabled: false,
38334
38335     /**
38336      * Disables the mask to prevent it from being displayed
38337      */
38338     disable : function(){
38339        this.disabled = true;
38340     },
38341
38342     /**
38343      * Enables the mask so that it can be displayed
38344      */
38345     enable : function(){
38346         this.disabled = false;
38347     },
38348     
38349     onLoadException : function()
38350     {
38351         Roo.log(arguments);
38352         
38353         if (typeof(arguments[3]) != 'undefined') {
38354             Roo.MessageBox.alert("Error loading",arguments[3]);
38355         } 
38356         /*
38357         try {
38358             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38359                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38360             }   
38361         } catch(e) {
38362             
38363         }
38364         */
38365     
38366         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38367     },
38368     // private
38369     onLoad : function()
38370     {
38371         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38372     },
38373
38374     // private
38375     onBeforeLoad : function(){
38376         if(!this.disabled){
38377             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38378         }
38379     },
38380
38381     // private
38382     destroy : function(){
38383         if(this.store){
38384             this.store.un('beforeload', this.onBeforeLoad, this);
38385             this.store.un('load', this.onLoad, this);
38386             this.store.un('loadexception', this.onLoadException, this);
38387         }else{
38388             var um = this.el.getUpdateManager();
38389             um.un('beforeupdate', this.onBeforeLoad, this);
38390             um.un('update', this.onLoad, this);
38391             um.un('failure', this.onLoad, this);
38392         }
38393     }
38394 };/*
38395  * Based on:
38396  * Ext JS Library 1.1.1
38397  * Copyright(c) 2006-2007, Ext JS, LLC.
38398  *
38399  * Originally Released Under LGPL - original licence link has changed is not relivant.
38400  *
38401  * Fork - LGPL
38402  * <script type="text/javascript">
38403  */
38404
38405
38406 /**
38407  * @class Roo.XTemplate
38408  * @extends Roo.Template
38409  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38410 <pre><code>
38411 var t = new Roo.XTemplate(
38412         '&lt;select name="{name}"&gt;',
38413                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38414         '&lt;/select&gt;'
38415 );
38416  
38417 // then append, applying the master template values
38418  </code></pre>
38419  *
38420  * Supported features:
38421  *
38422  *  Tags:
38423
38424 <pre><code>
38425       {a_variable} - output encoded.
38426       {a_variable.format:("Y-m-d")} - call a method on the variable
38427       {a_variable:raw} - unencoded output
38428       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38429       {a_variable:this.method_on_template(...)} - call a method on the template object.
38430  
38431 </code></pre>
38432  *  The tpl tag:
38433 <pre><code>
38434         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38435         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38436         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38437         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38438   
38439         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38440         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38441 </code></pre>
38442  *      
38443  */
38444 Roo.XTemplate = function()
38445 {
38446     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38447     if (this.html) {
38448         this.compile();
38449     }
38450 };
38451
38452
38453 Roo.extend(Roo.XTemplate, Roo.Template, {
38454
38455     /**
38456      * The various sub templates
38457      */
38458     tpls : false,
38459     /**
38460      *
38461      * basic tag replacing syntax
38462      * WORD:WORD()
38463      *
38464      * // you can fake an object call by doing this
38465      *  x.t:(test,tesT) 
38466      * 
38467      */
38468     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38469
38470     /**
38471      * compile the template
38472      *
38473      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38474      *
38475      */
38476     compile: function()
38477     {
38478         var s = this.html;
38479      
38480         s = ['<tpl>', s, '</tpl>'].join('');
38481     
38482         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38483             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38484             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38485             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38486             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38487             m,
38488             id     = 0,
38489             tpls   = [];
38490     
38491         while(true == !!(m = s.match(re))){
38492             var forMatch   = m[0].match(nameRe),
38493                 ifMatch   = m[0].match(ifRe),
38494                 execMatch   = m[0].match(execRe),
38495                 namedMatch   = m[0].match(namedRe),
38496                 
38497                 exp  = null, 
38498                 fn   = null,
38499                 exec = null,
38500                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38501                 
38502             if (ifMatch) {
38503                 // if - puts fn into test..
38504                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38505                 if(exp){
38506                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38507                 }
38508             }
38509             
38510             if (execMatch) {
38511                 // exec - calls a function... returns empty if true is  returned.
38512                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38513                 if(exp){
38514                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38515                 }
38516             }
38517             
38518             
38519             if (name) {
38520                 // for = 
38521                 switch(name){
38522                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38523                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38524                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38525                 }
38526             }
38527             var uid = namedMatch ? namedMatch[1] : id;
38528             
38529             
38530             tpls.push({
38531                 id:     namedMatch ? namedMatch[1] : id,
38532                 target: name,
38533                 exec:   exec,
38534                 test:   fn,
38535                 body:   m[1] || ''
38536             });
38537             if (namedMatch) {
38538                 s = s.replace(m[0], '');
38539             } else { 
38540                 s = s.replace(m[0], '{xtpl'+ id + '}');
38541             }
38542             ++id;
38543         }
38544         this.tpls = [];
38545         for(var i = tpls.length-1; i >= 0; --i){
38546             this.compileTpl(tpls[i]);
38547             this.tpls[tpls[i].id] = tpls[i];
38548         }
38549         this.master = tpls[tpls.length-1];
38550         return this;
38551     },
38552     /**
38553      * same as applyTemplate, except it's done to one of the subTemplates
38554      * when using named templates, you can do:
38555      *
38556      * var str = pl.applySubTemplate('your-name', values);
38557      *
38558      * 
38559      * @param {Number} id of the template
38560      * @param {Object} values to apply to template
38561      * @param {Object} parent (normaly the instance of this object)
38562      */
38563     applySubTemplate : function(id, values, parent)
38564     {
38565         
38566         
38567         var t = this.tpls[id];
38568         
38569         
38570         try { 
38571             if(t.test && !t.test.call(this, values, parent)){
38572                 return '';
38573             }
38574         } catch(e) {
38575             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38576             Roo.log(e.toString());
38577             Roo.log(t.test);
38578             return ''
38579         }
38580         try { 
38581             
38582             if(t.exec && t.exec.call(this, values, parent)){
38583                 return '';
38584             }
38585         } catch(e) {
38586             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38587             Roo.log(e.toString());
38588             Roo.log(t.exec);
38589             return ''
38590         }
38591         try {
38592             var vs = t.target ? t.target.call(this, values, parent) : values;
38593             parent = t.target ? values : parent;
38594             if(t.target && vs instanceof Array){
38595                 var buf = [];
38596                 for(var i = 0, len = vs.length; i < len; i++){
38597                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38598                 }
38599                 return buf.join('');
38600             }
38601             return t.compiled.call(this, vs, parent);
38602         } catch (e) {
38603             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38604             Roo.log(e.toString());
38605             Roo.log(t.compiled);
38606             return '';
38607         }
38608     },
38609
38610     compileTpl : function(tpl)
38611     {
38612         var fm = Roo.util.Format;
38613         var useF = this.disableFormats !== true;
38614         var sep = Roo.isGecko ? "+" : ",";
38615         var undef = function(str) {
38616             Roo.log("Property not found :"  + str);
38617             return '';
38618         };
38619         
38620         var fn = function(m, name, format, args)
38621         {
38622             //Roo.log(arguments);
38623             args = args ? args.replace(/\\'/g,"'") : args;
38624             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38625             if (typeof(format) == 'undefined') {
38626                 format= 'htmlEncode';
38627             }
38628             if (format == 'raw' ) {
38629                 format = false;
38630             }
38631             
38632             if(name.substr(0, 4) == 'xtpl'){
38633                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38634             }
38635             
38636             // build an array of options to determine if value is undefined..
38637             
38638             // basically get 'xxxx.yyyy' then do
38639             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38640             //    (function () { Roo.log("Property not found"); return ''; })() :
38641             //    ......
38642             
38643             var udef_ar = [];
38644             var lookfor = '';
38645             Roo.each(name.split('.'), function(st) {
38646                 lookfor += (lookfor.length ? '.': '') + st;
38647                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38648             });
38649             
38650             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38651             
38652             
38653             if(format && useF){
38654                 
38655                 args = args ? ',' + args : "";
38656                  
38657                 if(format.substr(0, 5) != "this."){
38658                     format = "fm." + format + '(';
38659                 }else{
38660                     format = 'this.call("'+ format.substr(5) + '", ';
38661                     args = ", values";
38662                 }
38663                 
38664                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38665             }
38666              
38667             if (args.length) {
38668                 // called with xxyx.yuu:(test,test)
38669                 // change to ()
38670                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38671             }
38672             // raw.. - :raw modifier..
38673             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38674             
38675         };
38676         var body;
38677         // branched to use + in gecko and [].join() in others
38678         if(Roo.isGecko){
38679             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38680                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38681                     "';};};";
38682         }else{
38683             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38684             body.push(tpl.body.replace(/(\r\n|\n)/g,
38685                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38686             body.push("'].join('');};};");
38687             body = body.join('');
38688         }
38689         
38690         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38691        
38692         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38693         eval(body);
38694         
38695         return this;
38696     },
38697
38698     applyTemplate : function(values){
38699         return this.master.compiled.call(this, values, {});
38700         //var s = this.subs;
38701     },
38702
38703     apply : function(){
38704         return this.applyTemplate.apply(this, arguments);
38705     }
38706
38707  });
38708
38709 Roo.XTemplate.from = function(el){
38710     el = Roo.getDom(el);
38711     return new Roo.XTemplate(el.value || el.innerHTML);
38712 };