roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787     
788      /**
789      * 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     mode: false,
3059     /**
3060      * @cfg {String} offset
3061      * The number of pixels to offset the shadow from the element (defaults to 4)
3062      */
3063     offset: 4,
3064
3065     // private
3066     defaultMode: "drop",
3067
3068     /**
3069      * Displays the shadow under the target element
3070      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3071      */
3072     show : function(target){
3073         target = Roo.get(target);
3074         if(!this.el){
3075             this.el = Roo.Shadow.Pool.pull();
3076             if(this.el.dom.nextSibling != target.dom){
3077                 this.el.insertBefore(target);
3078             }
3079         }
3080         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3081         if(Roo.isIE){
3082             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3083         }
3084         this.realign(
3085             target.getLeft(true),
3086             target.getTop(true),
3087             target.getWidth(),
3088             target.getHeight()
3089         );
3090         this.el.dom.style.display = "block";
3091     },
3092
3093     /**
3094      * Returns true if the shadow is visible, else false
3095      */
3096     isVisible : function(){
3097         return this.el ? true : false;  
3098     },
3099
3100     /**
3101      * Direct alignment when values are already available. Show must be called at least once before
3102      * calling this method to ensure it is initialized.
3103      * @param {Number} left The target element left position
3104      * @param {Number} top The target element top position
3105      * @param {Number} width The target element width
3106      * @param {Number} height The target element height
3107      */
3108     realign : function(l, t, w, h){
3109         if(!this.el){
3110             return;
3111         }
3112         var a = this.adjusts, d = this.el.dom, s = d.style;
3113         var iea = 0;
3114         s.left = (l+a.l)+"px";
3115         s.top = (t+a.t)+"px";
3116         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3117  
3118         if(s.width != sws || s.height != shs){
3119             s.width = sws;
3120             s.height = shs;
3121             if(!Roo.isIE){
3122                 var cn = d.childNodes;
3123                 var sww = Math.max(0, (sw-12))+"px";
3124                 cn[0].childNodes[1].style.width = sww;
3125                 cn[1].childNodes[1].style.width = sww;
3126                 cn[2].childNodes[1].style.width = sww;
3127                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3128             }
3129         }
3130     },
3131
3132     /**
3133      * Hides this shadow
3134      */
3135     hide : function(){
3136         if(this.el){
3137             this.el.dom.style.display = "none";
3138             Roo.Shadow.Pool.push(this.el);
3139             delete this.el;
3140         }
3141     },
3142
3143     /**
3144      * Adjust the z-index of this shadow
3145      * @param {Number} zindex The new z-index
3146      */
3147     setZIndex : function(z){
3148         this.zIndex = z;
3149         if(this.el){
3150             this.el.setStyle("z-index", z);
3151         }
3152     }
3153 };
3154
3155 // Private utility class that manages the internal Shadow cache
3156 Roo.Shadow.Pool = function(){
3157     var p = [];
3158     var markup = Roo.isIE ?
3159                  '<div class="x-ie-shadow"></div>' :
3160                  '<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>';
3161     return {
3162         pull : function(){
3163             var sh = p.shift();
3164             if(!sh){
3165                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3166                 sh.autoBoxAdjust = false;
3167             }
3168             return sh;
3169         },
3170
3171         push : function(sh){
3172             p.push(sh);
3173         }
3174     };
3175 }();/*
3176  * Based on:
3177  * Ext JS Library 1.1.1
3178  * Copyright(c) 2006-2007, Ext JS, LLC.
3179  *
3180  * Originally Released Under LGPL - original licence link has changed is not relivant.
3181  *
3182  * Fork - LGPL
3183  * <script type="text/javascript">
3184  */
3185
3186
3187 /**
3188  * @class Roo.SplitBar
3189  * @extends Roo.util.Observable
3190  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3191  * <br><br>
3192  * Usage:
3193  * <pre><code>
3194 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3195                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3196 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3197 split.minSize = 100;
3198 split.maxSize = 600;
3199 split.animate = true;
3200 split.on('moved', splitterMoved);
3201 </code></pre>
3202  * @constructor
3203  * Create a new SplitBar
3204  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3205  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3206  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3207  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3208                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3209                         position of the SplitBar).
3210  */
3211 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3212     
3213     /** @private */
3214     this.el = Roo.get(dragElement, true);
3215     this.el.dom.unselectable = "on";
3216     /** @private */
3217     this.resizingEl = Roo.get(resizingElement, true);
3218
3219     /**
3220      * @private
3221      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3222      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3223      * @type Number
3224      */
3225     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3226     
3227     /**
3228      * The minimum size of the resizing element. (Defaults to 0)
3229      * @type Number
3230      */
3231     this.minSize = 0;
3232     
3233     /**
3234      * The maximum size of the resizing element. (Defaults to 2000)
3235      * @type Number
3236      */
3237     this.maxSize = 2000;
3238     
3239     /**
3240      * Whether to animate the transition to the new size
3241      * @type Boolean
3242      */
3243     this.animate = false;
3244     
3245     /**
3246      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3247      * @type Boolean
3248      */
3249     this.useShim = false;
3250     
3251     /** @private */
3252     this.shim = null;
3253     
3254     if(!existingProxy){
3255         /** @private */
3256         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3257     }else{
3258         this.proxy = Roo.get(existingProxy).dom;
3259     }
3260     /** @private */
3261     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3262     
3263     /** @private */
3264     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3265     
3266     /** @private */
3267     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3268     
3269     /** @private */
3270     this.dragSpecs = {};
3271     
3272     /**
3273      * @private The adapter to use to positon and resize elements
3274      */
3275     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3276     this.adapter.init(this);
3277     
3278     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3279         /** @private */
3280         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3281         this.el.addClass("x-splitbar-h");
3282     }else{
3283         /** @private */
3284         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3285         this.el.addClass("x-splitbar-v");
3286     }
3287     
3288     this.addEvents({
3289         /**
3290          * @event resize
3291          * Fires when the splitter is moved (alias for {@link #event-moved})
3292          * @param {Roo.SplitBar} this
3293          * @param {Number} newSize the new width or height
3294          */
3295         "resize" : true,
3296         /**
3297          * @event moved
3298          * Fires when the splitter is moved
3299          * @param {Roo.SplitBar} this
3300          * @param {Number} newSize the new width or height
3301          */
3302         "moved" : true,
3303         /**
3304          * @event beforeresize
3305          * Fires before the splitter is dragged
3306          * @param {Roo.SplitBar} this
3307          */
3308         "beforeresize" : true,
3309
3310         "beforeapply" : true
3311     });
3312
3313     Roo.util.Observable.call(this);
3314 };
3315
3316 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3317     onStartProxyDrag : function(x, y){
3318         this.fireEvent("beforeresize", this);
3319         if(!this.overlay){
3320             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3321             o.unselectable();
3322             o.enableDisplayMode("block");
3323             // all splitbars share the same overlay
3324             Roo.SplitBar.prototype.overlay = o;
3325         }
3326         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3327         this.overlay.show();
3328         Roo.get(this.proxy).setDisplayed("block");
3329         var size = this.adapter.getElementSize(this);
3330         this.activeMinSize = this.getMinimumSize();;
3331         this.activeMaxSize = this.getMaximumSize();;
3332         var c1 = size - this.activeMinSize;
3333         var c2 = Math.max(this.activeMaxSize - size, 0);
3334         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3335             this.dd.resetConstraints();
3336             this.dd.setXConstraint(
3337                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3338                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3339             );
3340             this.dd.setYConstraint(0, 0);
3341         }else{
3342             this.dd.resetConstraints();
3343             this.dd.setXConstraint(0, 0);
3344             this.dd.setYConstraint(
3345                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3346                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3347             );
3348          }
3349         this.dragSpecs.startSize = size;
3350         this.dragSpecs.startPoint = [x, y];
3351         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3352     },
3353     
3354     /** 
3355      * @private Called after the drag operation by the DDProxy
3356      */
3357     onEndProxyDrag : function(e){
3358         Roo.get(this.proxy).setDisplayed(false);
3359         var endPoint = Roo.lib.Event.getXY(e);
3360         if(this.overlay){
3361             this.overlay.hide();
3362         }
3363         var newSize;
3364         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3365             newSize = this.dragSpecs.startSize + 
3366                 (this.placement == Roo.SplitBar.LEFT ?
3367                     endPoint[0] - this.dragSpecs.startPoint[0] :
3368                     this.dragSpecs.startPoint[0] - endPoint[0]
3369                 );
3370         }else{
3371             newSize = this.dragSpecs.startSize + 
3372                 (this.placement == Roo.SplitBar.TOP ?
3373                     endPoint[1] - this.dragSpecs.startPoint[1] :
3374                     this.dragSpecs.startPoint[1] - endPoint[1]
3375                 );
3376         }
3377         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3378         if(newSize != this.dragSpecs.startSize){
3379             if(this.fireEvent('beforeapply', this, newSize) !== false){
3380                 this.adapter.setElementSize(this, newSize);
3381                 this.fireEvent("moved", this, newSize);
3382                 this.fireEvent("resize", this, newSize);
3383             }
3384         }
3385     },
3386     
3387     /**
3388      * Get the adapter this SplitBar uses
3389      * @return The adapter object
3390      */
3391     getAdapter : function(){
3392         return this.adapter;
3393     },
3394     
3395     /**
3396      * Set the adapter this SplitBar uses
3397      * @param {Object} adapter A SplitBar adapter object
3398      */
3399     setAdapter : function(adapter){
3400         this.adapter = adapter;
3401         this.adapter.init(this);
3402     },
3403     
3404     /**
3405      * Gets the minimum size for the resizing element
3406      * @return {Number} The minimum size
3407      */
3408     getMinimumSize : function(){
3409         return this.minSize;
3410     },
3411     
3412     /**
3413      * Sets the minimum size for the resizing element
3414      * @param {Number} minSize The minimum size
3415      */
3416     setMinimumSize : function(minSize){
3417         this.minSize = minSize;
3418     },
3419     
3420     /**
3421      * Gets the maximum size for the resizing element
3422      * @return {Number} The maximum size
3423      */
3424     getMaximumSize : function(){
3425         return this.maxSize;
3426     },
3427     
3428     /**
3429      * Sets the maximum size for the resizing element
3430      * @param {Number} maxSize The maximum size
3431      */
3432     setMaximumSize : function(maxSize){
3433         this.maxSize = maxSize;
3434     },
3435     
3436     /**
3437      * Sets the initialize size for the resizing element
3438      * @param {Number} size The initial size
3439      */
3440     setCurrentSize : function(size){
3441         var oldAnimate = this.animate;
3442         this.animate = false;
3443         this.adapter.setElementSize(this, size);
3444         this.animate = oldAnimate;
3445     },
3446     
3447     /**
3448      * Destroy this splitbar. 
3449      * @param {Boolean} removeEl True to remove the element
3450      */
3451     destroy : function(removeEl){
3452         if(this.shim){
3453             this.shim.remove();
3454         }
3455         this.dd.unreg();
3456         this.proxy.parentNode.removeChild(this.proxy);
3457         if(removeEl){
3458             this.el.remove();
3459         }
3460     }
3461 });
3462
3463 /**
3464  * @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.
3465  */
3466 Roo.SplitBar.createProxy = function(dir){
3467     var proxy = new Roo.Element(document.createElement("div"));
3468     proxy.unselectable();
3469     var cls = 'x-splitbar-proxy';
3470     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3471     document.body.appendChild(proxy.dom);
3472     return proxy.dom;
3473 };
3474
3475 /** 
3476  * @class Roo.SplitBar.BasicLayoutAdapter
3477  * Default Adapter. It assumes the splitter and resizing element are not positioned
3478  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3479  */
3480 Roo.SplitBar.BasicLayoutAdapter = function(){
3481 };
3482
3483 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3484     // do nothing for now
3485     init : function(s){
3486     
3487     },
3488     /**
3489      * Called before drag operations to get the current size of the resizing element. 
3490      * @param {Roo.SplitBar} s The SplitBar using this adapter
3491      */
3492      getElementSize : function(s){
3493         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3494             return s.resizingEl.getWidth();
3495         }else{
3496             return s.resizingEl.getHeight();
3497         }
3498     },
3499     
3500     /**
3501      * Called after drag operations to set the size of the resizing element.
3502      * @param {Roo.SplitBar} s The SplitBar using this adapter
3503      * @param {Number} newSize The new size to set
3504      * @param {Function} onComplete A function to be invoked when resizing is complete
3505      */
3506     setElementSize : function(s, newSize, onComplete){
3507         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3508             if(!s.animate){
3509                 s.resizingEl.setWidth(newSize);
3510                 if(onComplete){
3511                     onComplete(s, newSize);
3512                 }
3513             }else{
3514                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3515             }
3516         }else{
3517             
3518             if(!s.animate){
3519                 s.resizingEl.setHeight(newSize);
3520                 if(onComplete){
3521                     onComplete(s, newSize);
3522                 }
3523             }else{
3524                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3525             }
3526         }
3527     }
3528 };
3529
3530 /** 
3531  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3532  * @extends Roo.SplitBar.BasicLayoutAdapter
3533  * Adapter that  moves the splitter element to align with the resized sizing element. 
3534  * Used with an absolute positioned SplitBar.
3535  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3536  * document.body, make sure you assign an id to the body element.
3537  */
3538 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3539     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3540     this.container = Roo.get(container);
3541 };
3542
3543 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3544     init : function(s){
3545         this.basic.init(s);
3546     },
3547     
3548     getElementSize : function(s){
3549         return this.basic.getElementSize(s);
3550     },
3551     
3552     setElementSize : function(s, newSize, onComplete){
3553         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3554     },
3555     
3556     moveSplitter : function(s){
3557         var yes = Roo.SplitBar;
3558         switch(s.placement){
3559             case yes.LEFT:
3560                 s.el.setX(s.resizingEl.getRight());
3561                 break;
3562             case yes.RIGHT:
3563                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3564                 break;
3565             case yes.TOP:
3566                 s.el.setY(s.resizingEl.getBottom());
3567                 break;
3568             case yes.BOTTOM:
3569                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3570                 break;
3571         }
3572     }
3573 };
3574
3575 /**
3576  * Orientation constant - Create a vertical SplitBar
3577  * @static
3578  * @type Number
3579  */
3580 Roo.SplitBar.VERTICAL = 1;
3581
3582 /**
3583  * Orientation constant - Create a horizontal SplitBar
3584  * @static
3585  * @type Number
3586  */
3587 Roo.SplitBar.HORIZONTAL = 2;
3588
3589 /**
3590  * Placement constant - The resizing element is to the left of the splitter element
3591  * @static
3592  * @type Number
3593  */
3594 Roo.SplitBar.LEFT = 1;
3595
3596 /**
3597  * Placement constant - The resizing element is to the right of the splitter element
3598  * @static
3599  * @type Number
3600  */
3601 Roo.SplitBar.RIGHT = 2;
3602
3603 /**
3604  * Placement constant - The resizing element is positioned above the splitter element
3605  * @static
3606  * @type Number
3607  */
3608 Roo.SplitBar.TOP = 3;
3609
3610 /**
3611  * Placement constant - The resizing element is positioned under splitter element
3612  * @static
3613  * @type Number
3614  */
3615 Roo.SplitBar.BOTTOM = 4;
3616 /*
3617  * Based on:
3618  * Ext JS Library 1.1.1
3619  * Copyright(c) 2006-2007, Ext JS, LLC.
3620  *
3621  * Originally Released Under LGPL - original licence link has changed is not relivant.
3622  *
3623  * Fork - LGPL
3624  * <script type="text/javascript">
3625  */
3626
3627 /**
3628  * @class Roo.View
3629  * @extends Roo.util.Observable
3630  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3631  * This class also supports single and multi selection modes. <br>
3632  * Create a data model bound view:
3633  <pre><code>
3634  var store = new Roo.data.Store(...);
3635
3636  var view = new Roo.View({
3637     el : "my-element",
3638     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3639  
3640     singleSelect: true,
3641     selectedClass: "ydataview-selected",
3642     store: store
3643  });
3644
3645  // listen for node click?
3646  view.on("click", function(vw, index, node, e){
3647  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3648  });
3649
3650  // load XML data
3651  dataModel.load("foobar.xml");
3652  </code></pre>
3653  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3654  * <br><br>
3655  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3656  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3657  * 
3658  * Note: old style constructor is still suported (container, template, config)
3659  * 
3660  * @constructor
3661  * Create a new View
3662  * @param {Object} config The config object
3663  * 
3664  */
3665 Roo.View = function(config, depreciated_tpl, depreciated_config){
3666     
3667     this.parent = false;
3668     
3669     if (typeof(depreciated_tpl) == 'undefined') {
3670         // new way.. - universal constructor.
3671         Roo.apply(this, config);
3672         this.el  = Roo.get(this.el);
3673     } else {
3674         // old format..
3675         this.el  = Roo.get(config);
3676         this.tpl = depreciated_tpl;
3677         Roo.apply(this, depreciated_config);
3678     }
3679     this.wrapEl  = this.el.wrap().wrap();
3680     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3681     
3682     
3683     if(typeof(this.tpl) == "string"){
3684         this.tpl = new Roo.Template(this.tpl);
3685     } else {
3686         // support xtype ctors..
3687         this.tpl = new Roo.factory(this.tpl, Roo);
3688     }
3689     
3690     
3691     this.tpl.compile();
3692     
3693     /** @private */
3694     this.addEvents({
3695         /**
3696          * @event beforeclick
3697          * Fires before a click is processed. Returns false to cancel the default action.
3698          * @param {Roo.View} this
3699          * @param {Number} index The index of the target node
3700          * @param {HTMLElement} node The target node
3701          * @param {Roo.EventObject} e The raw event object
3702          */
3703             "beforeclick" : true,
3704         /**
3705          * @event click
3706          * Fires when a template node is clicked.
3707          * @param {Roo.View} this
3708          * @param {Number} index The index of the target node
3709          * @param {HTMLElement} node The target node
3710          * @param {Roo.EventObject} e The raw event object
3711          */
3712             "click" : true,
3713         /**
3714          * @event dblclick
3715          * Fires when a template node is double clicked.
3716          * @param {Roo.View} this
3717          * @param {Number} index The index of the target node
3718          * @param {HTMLElement} node The target node
3719          * @param {Roo.EventObject} e The raw event object
3720          */
3721             "dblclick" : true,
3722         /**
3723          * @event contextmenu
3724          * Fires when a template node is right clicked.
3725          * @param {Roo.View} this
3726          * @param {Number} index The index of the target node
3727          * @param {HTMLElement} node The target node
3728          * @param {Roo.EventObject} e The raw event object
3729          */
3730             "contextmenu" : true,
3731         /**
3732          * @event selectionchange
3733          * Fires when the selected nodes change.
3734          * @param {Roo.View} this
3735          * @param {Array} selections Array of the selected nodes
3736          */
3737             "selectionchange" : true,
3738     
3739         /**
3740          * @event beforeselect
3741          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3742          * @param {Roo.View} this
3743          * @param {HTMLElement} node The node to be selected
3744          * @param {Array} selections Array of currently selected nodes
3745          */
3746             "beforeselect" : true,
3747         /**
3748          * @event preparedata
3749          * Fires on every row to render, to allow you to change the data.
3750          * @param {Roo.View} this
3751          * @param {Object} data to be rendered (change this)
3752          */
3753           "preparedata" : true
3754           
3755           
3756         });
3757
3758
3759
3760     this.el.on({
3761         "click": this.onClick,
3762         "dblclick": this.onDblClick,
3763         "contextmenu": this.onContextMenu,
3764         scope:this
3765     });
3766
3767     this.selections = [];
3768     this.nodes = [];
3769     this.cmp = new Roo.CompositeElementLite([]);
3770     if(this.store){
3771         this.store = Roo.factory(this.store, Roo.data);
3772         this.setStore(this.store, true);
3773     }
3774     
3775     if ( this.footer && this.footer.xtype) {
3776            
3777          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3778         
3779         this.footer.dataSource = this.store;
3780         this.footer.container = fctr;
3781         this.footer = Roo.factory(this.footer, Roo);
3782         fctr.insertFirst(this.el);
3783         
3784         // this is a bit insane - as the paging toolbar seems to detach the el..
3785 //        dom.parentNode.parentNode.parentNode
3786          // they get detached?
3787     }
3788     
3789     
3790     Roo.View.superclass.constructor.call(this);
3791     
3792     
3793 };
3794
3795 Roo.extend(Roo.View, Roo.util.Observable, {
3796     
3797      /**
3798      * @cfg {Roo.data.Store} store Data store to load data from.
3799      */
3800     store : false,
3801     
3802     /**
3803      * @cfg {String|Roo.Element} el The container element.
3804      */
3805     el : '',
3806     
3807     /**
3808      * @cfg {String|Roo.Template} tpl The template used by this View 
3809      */
3810     tpl : false,
3811     /**
3812      * @cfg {String} dataName the named area of the template to use as the data area
3813      *                          Works with domtemplates roo-name="name"
3814      */
3815     dataName: false,
3816     /**
3817      * @cfg {String} selectedClass The css class to add to selected nodes
3818      */
3819     selectedClass : "x-view-selected",
3820      /**
3821      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3822      */
3823     emptyText : "",
3824     
3825     /**
3826      * @cfg {String} text to display on mask (default Loading)
3827      */
3828     mask : false,
3829     /**
3830      * @cfg {Boolean} multiSelect Allow multiple selection
3831      */
3832     multiSelect : false,
3833     /**
3834      * @cfg {Boolean} singleSelect Allow single selection
3835      */
3836     singleSelect:  false,
3837     
3838     /**
3839      * @cfg {Boolean} toggleSelect - selecting 
3840      */
3841     toggleSelect : false,
3842     
3843     /**
3844      * @cfg {Boolean} tickable - selecting 
3845      */
3846     tickable : false,
3847     
3848     /**
3849      * Returns the element this view is bound to.
3850      * @return {Roo.Element}
3851      */
3852     getEl : function(){
3853         return this.wrapEl;
3854     },
3855     
3856     
3857
3858     /**
3859      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3860      */
3861     refresh : function(){
3862         //Roo.log('refresh');
3863         var t = this.tpl;
3864         
3865         // if we are using something like 'domtemplate', then
3866         // the what gets used is:
3867         // t.applySubtemplate(NAME, data, wrapping data..)
3868         // the outer template then get' applied with
3869         //     the store 'extra data'
3870         // and the body get's added to the
3871         //      roo-name="data" node?
3872         //      <span class='roo-tpl-{name}'></span> ?????
3873         
3874         
3875         
3876         this.clearSelections();
3877         this.el.update("");
3878         var html = [];
3879         var records = this.store.getRange();
3880         if(records.length < 1) {
3881             
3882             // is this valid??  = should it render a template??
3883             
3884             this.el.update(this.emptyText);
3885             return;
3886         }
3887         var el = this.el;
3888         if (this.dataName) {
3889             this.el.update(t.apply(this.store.meta)); //????
3890             el = this.el.child('.roo-tpl-' + this.dataName);
3891         }
3892         
3893         for(var i = 0, len = records.length; i < len; i++){
3894             var data = this.prepareData(records[i].data, i, records[i]);
3895             this.fireEvent("preparedata", this, data, i, records[i]);
3896             
3897             var d = Roo.apply({}, data);
3898             
3899             if(this.tickable){
3900                 Roo.apply(d, {'roo-id' : Roo.id()});
3901                 
3902                 var _this = this;
3903             
3904                 Roo.each(this.parent.item, function(item){
3905                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3906                         return;
3907                     }
3908                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3909                 });
3910             }
3911             
3912             html[html.length] = Roo.util.Format.trim(
3913                 this.dataName ?
3914                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3915                     t.apply(d)
3916             );
3917         }
3918         
3919         
3920         
3921         el.update(html.join(""));
3922         this.nodes = el.dom.childNodes;
3923         this.updateIndexes(0);
3924     },
3925     
3926
3927     /**
3928      * Function to override to reformat the data that is sent to
3929      * the template for each node.
3930      * DEPRICATED - use the preparedata event handler.
3931      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3932      * a JSON object for an UpdateManager bound view).
3933      */
3934     prepareData : function(data, index, record)
3935     {
3936         this.fireEvent("preparedata", this, data, index, record);
3937         return data;
3938     },
3939
3940     onUpdate : function(ds, record){
3941         // Roo.log('on update');   
3942         this.clearSelections();
3943         var index = this.store.indexOf(record);
3944         var n = this.nodes[index];
3945         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3946         n.parentNode.removeChild(n);
3947         this.updateIndexes(index, index);
3948     },
3949
3950     
3951     
3952 // --------- FIXME     
3953     onAdd : function(ds, records, index)
3954     {
3955         //Roo.log(['on Add', ds, records, index] );        
3956         this.clearSelections();
3957         if(this.nodes.length == 0){
3958             this.refresh();
3959             return;
3960         }
3961         var n = this.nodes[index];
3962         for(var i = 0, len = records.length; i < len; i++){
3963             var d = this.prepareData(records[i].data, i, records[i]);
3964             if(n){
3965                 this.tpl.insertBefore(n, d);
3966             }else{
3967                 
3968                 this.tpl.append(this.el, d);
3969             }
3970         }
3971         this.updateIndexes(index);
3972     },
3973
3974     onRemove : function(ds, record, index){
3975        // Roo.log('onRemove');
3976         this.clearSelections();
3977         var el = this.dataName  ?
3978             this.el.child('.roo-tpl-' + this.dataName) :
3979             this.el; 
3980         
3981         el.dom.removeChild(this.nodes[index]);
3982         this.updateIndexes(index);
3983     },
3984
3985     /**
3986      * Refresh an individual node.
3987      * @param {Number} index
3988      */
3989     refreshNode : function(index){
3990         this.onUpdate(this.store, this.store.getAt(index));
3991     },
3992
3993     updateIndexes : function(startIndex, endIndex){
3994         var ns = this.nodes;
3995         startIndex = startIndex || 0;
3996         endIndex = endIndex || ns.length - 1;
3997         for(var i = startIndex; i <= endIndex; i++){
3998             ns[i].nodeIndex = i;
3999         }
4000     },
4001
4002     /**
4003      * Changes the data store this view uses and refresh the view.
4004      * @param {Store} store
4005      */
4006     setStore : function(store, initial){
4007         if(!initial && this.store){
4008             this.store.un("datachanged", this.refresh);
4009             this.store.un("add", this.onAdd);
4010             this.store.un("remove", this.onRemove);
4011             this.store.un("update", this.onUpdate);
4012             this.store.un("clear", this.refresh);
4013             this.store.un("beforeload", this.onBeforeLoad);
4014             this.store.un("load", this.onLoad);
4015             this.store.un("loadexception", this.onLoad);
4016         }
4017         if(store){
4018           
4019             store.on("datachanged", this.refresh, this);
4020             store.on("add", this.onAdd, this);
4021             store.on("remove", this.onRemove, this);
4022             store.on("update", this.onUpdate, this);
4023             store.on("clear", this.refresh, this);
4024             store.on("beforeload", this.onBeforeLoad, this);
4025             store.on("load", this.onLoad, this);
4026             store.on("loadexception", this.onLoad, this);
4027         }
4028         
4029         if(store){
4030             this.refresh();
4031         }
4032     },
4033     /**
4034      * onbeforeLoad - masks the loading area.
4035      *
4036      */
4037     onBeforeLoad : function(store,opts)
4038     {
4039          //Roo.log('onBeforeLoad');   
4040         if (!opts.add) {
4041             this.el.update("");
4042         }
4043         this.el.mask(this.mask ? this.mask : "Loading" ); 
4044     },
4045     onLoad : function ()
4046     {
4047         this.el.unmask();
4048     },
4049     
4050
4051     /**
4052      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4053      * @param {HTMLElement} node
4054      * @return {HTMLElement} The template node
4055      */
4056     findItemFromChild : function(node){
4057         var el = this.dataName  ?
4058             this.el.child('.roo-tpl-' + this.dataName,true) :
4059             this.el.dom; 
4060         
4061         if(!node || node.parentNode == el){
4062                     return node;
4063             }
4064             var p = node.parentNode;
4065             while(p && p != el){
4066             if(p.parentNode == el){
4067                 return p;
4068             }
4069             p = p.parentNode;
4070         }
4071             return null;
4072     },
4073
4074     /** @ignore */
4075     onClick : function(e){
4076         var item = this.findItemFromChild(e.getTarget());
4077         if(item){
4078             var index = this.indexOf(item);
4079             if(this.onItemClick(item, index, e) !== false){
4080                 this.fireEvent("click", this, index, item, e);
4081             }
4082         }else{
4083             this.clearSelections();
4084         }
4085     },
4086
4087     /** @ignore */
4088     onContextMenu : function(e){
4089         var item = this.findItemFromChild(e.getTarget());
4090         if(item){
4091             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4092         }
4093     },
4094
4095     /** @ignore */
4096     onDblClick : function(e){
4097         var item = this.findItemFromChild(e.getTarget());
4098         if(item){
4099             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4100         }
4101     },
4102
4103     onItemClick : function(item, index, e)
4104     {
4105         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4106             return false;
4107         }
4108         if (this.toggleSelect) {
4109             var m = this.isSelected(item) ? 'unselect' : 'select';
4110             //Roo.log(m);
4111             var _t = this;
4112             _t[m](item, true, false);
4113             return true;
4114         }
4115         if(this.multiSelect || this.singleSelect){
4116             if(this.multiSelect && e.shiftKey && this.lastSelection){
4117                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4118             }else{
4119                 this.select(item, this.multiSelect && e.ctrlKey);
4120                 this.lastSelection = item;
4121             }
4122             
4123             if(!this.tickable){
4124                 e.preventDefault();
4125             }
4126             
4127         }
4128         return true;
4129     },
4130
4131     /**
4132      * Get the number of selected nodes.
4133      * @return {Number}
4134      */
4135     getSelectionCount : function(){
4136         return this.selections.length;
4137     },
4138
4139     /**
4140      * Get the currently selected nodes.
4141      * @return {Array} An array of HTMLElements
4142      */
4143     getSelectedNodes : function(){
4144         return this.selections;
4145     },
4146
4147     /**
4148      * Get the indexes of the selected nodes.
4149      * @return {Array}
4150      */
4151     getSelectedIndexes : function(){
4152         var indexes = [], s = this.selections;
4153         for(var i = 0, len = s.length; i < len; i++){
4154             indexes.push(s[i].nodeIndex);
4155         }
4156         return indexes;
4157     },
4158
4159     /**
4160      * Clear all selections
4161      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4162      */
4163     clearSelections : function(suppressEvent){
4164         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4165             this.cmp.elements = this.selections;
4166             this.cmp.removeClass(this.selectedClass);
4167             this.selections = [];
4168             if(!suppressEvent){
4169                 this.fireEvent("selectionchange", this, this.selections);
4170             }
4171         }
4172     },
4173
4174     /**
4175      * Returns true if the passed node is selected
4176      * @param {HTMLElement/Number} node The node or node index
4177      * @return {Boolean}
4178      */
4179     isSelected : function(node){
4180         var s = this.selections;
4181         if(s.length < 1){
4182             return false;
4183         }
4184         node = this.getNode(node);
4185         return s.indexOf(node) !== -1;
4186     },
4187
4188     /**
4189      * Selects nodes.
4190      * @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
4191      * @param {Boolean} keepExisting (optional) true to keep existing selections
4192      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4193      */
4194     select : function(nodeInfo, keepExisting, suppressEvent){
4195         if(nodeInfo instanceof Array){
4196             if(!keepExisting){
4197                 this.clearSelections(true);
4198             }
4199             for(var i = 0, len = nodeInfo.length; i < len; i++){
4200                 this.select(nodeInfo[i], true, true);
4201             }
4202             return;
4203         } 
4204         var node = this.getNode(nodeInfo);
4205         if(!node || this.isSelected(node)){
4206             return; // already selected.
4207         }
4208         if(!keepExisting){
4209             this.clearSelections(true);
4210         }
4211         
4212         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4213             Roo.fly(node).addClass(this.selectedClass);
4214             this.selections.push(node);
4215             if(!suppressEvent){
4216                 this.fireEvent("selectionchange", this, this.selections);
4217             }
4218         }
4219         
4220         
4221     },
4222       /**
4223      * Unselects nodes.
4224      * @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
4225      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4226      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4227      */
4228     unselect : function(nodeInfo, keepExisting, suppressEvent)
4229     {
4230         if(nodeInfo instanceof Array){
4231             Roo.each(this.selections, function(s) {
4232                 this.unselect(s, nodeInfo);
4233             }, this);
4234             return;
4235         }
4236         var node = this.getNode(nodeInfo);
4237         if(!node || !this.isSelected(node)){
4238             //Roo.log("not selected");
4239             return; // not selected.
4240         }
4241         // fireevent???
4242         var ns = [];
4243         Roo.each(this.selections, function(s) {
4244             if (s == node ) {
4245                 Roo.fly(node).removeClass(this.selectedClass);
4246
4247                 return;
4248             }
4249             ns.push(s);
4250         },this);
4251         
4252         this.selections= ns;
4253         this.fireEvent("selectionchange", this, this.selections);
4254     },
4255
4256     /**
4257      * Gets a template node.
4258      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4259      * @return {HTMLElement} The node or null if it wasn't found
4260      */
4261     getNode : function(nodeInfo){
4262         if(typeof nodeInfo == "string"){
4263             return document.getElementById(nodeInfo);
4264         }else if(typeof nodeInfo == "number"){
4265             return this.nodes[nodeInfo];
4266         }
4267         return nodeInfo;
4268     },
4269
4270     /**
4271      * Gets a range template nodes.
4272      * @param {Number} startIndex
4273      * @param {Number} endIndex
4274      * @return {Array} An array of nodes
4275      */
4276     getNodes : function(start, end){
4277         var ns = this.nodes;
4278         start = start || 0;
4279         end = typeof end == "undefined" ? ns.length - 1 : end;
4280         var nodes = [];
4281         if(start <= end){
4282             for(var i = start; i <= end; i++){
4283                 nodes.push(ns[i]);
4284             }
4285         } else{
4286             for(var i = start; i >= end; i--){
4287                 nodes.push(ns[i]);
4288             }
4289         }
4290         return nodes;
4291     },
4292
4293     /**
4294      * Finds the index of the passed node
4295      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4296      * @return {Number} The index of the node or -1
4297      */
4298     indexOf : function(node){
4299         node = this.getNode(node);
4300         if(typeof node.nodeIndex == "number"){
4301             return node.nodeIndex;
4302         }
4303         var ns = this.nodes;
4304         for(var i = 0, len = ns.length; i < len; i++){
4305             if(ns[i] == node){
4306                 return i;
4307             }
4308         }
4309         return -1;
4310     }
4311 });
4312 /*
4313  * Based on:
4314  * Ext JS Library 1.1.1
4315  * Copyright(c) 2006-2007, Ext JS, LLC.
4316  *
4317  * Originally Released Under LGPL - original licence link has changed is not relivant.
4318  *
4319  * Fork - LGPL
4320  * <script type="text/javascript">
4321  */
4322
4323 /**
4324  * @class Roo.JsonView
4325  * @extends Roo.View
4326  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4327 <pre><code>
4328 var view = new Roo.JsonView({
4329     container: "my-element",
4330     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4331     multiSelect: true, 
4332     jsonRoot: "data" 
4333 });
4334
4335 // listen for node click?
4336 view.on("click", function(vw, index, node, e){
4337     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4338 });
4339
4340 // direct load of JSON data
4341 view.load("foobar.php");
4342
4343 // Example from my blog list
4344 var tpl = new Roo.Template(
4345     '&lt;div class="entry"&gt;' +
4346     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4347     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4348     "&lt;/div&gt;&lt;hr /&gt;"
4349 );
4350
4351 var moreView = new Roo.JsonView({
4352     container :  "entry-list", 
4353     template : tpl,
4354     jsonRoot: "posts"
4355 });
4356 moreView.on("beforerender", this.sortEntries, this);
4357 moreView.load({
4358     url: "/blog/get-posts.php",
4359     params: "allposts=true",
4360     text: "Loading Blog Entries..."
4361 });
4362 </code></pre>
4363
4364 * Note: old code is supported with arguments : (container, template, config)
4365
4366
4367  * @constructor
4368  * Create a new JsonView
4369  * 
4370  * @param {Object} config The config object
4371  * 
4372  */
4373 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4374     
4375     
4376     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4377
4378     var um = this.el.getUpdateManager();
4379     um.setRenderer(this);
4380     um.on("update", this.onLoad, this);
4381     um.on("failure", this.onLoadException, this);
4382
4383     /**
4384      * @event beforerender
4385      * Fires before rendering of the downloaded JSON data.
4386      * @param {Roo.JsonView} this
4387      * @param {Object} data The JSON data loaded
4388      */
4389     /**
4390      * @event load
4391      * Fires when data is loaded.
4392      * @param {Roo.JsonView} this
4393      * @param {Object} data The JSON data loaded
4394      * @param {Object} response The raw Connect response object
4395      */
4396     /**
4397      * @event loadexception
4398      * Fires when loading fails.
4399      * @param {Roo.JsonView} this
4400      * @param {Object} response The raw Connect response object
4401      */
4402     this.addEvents({
4403         'beforerender' : true,
4404         'load' : true,
4405         'loadexception' : true
4406     });
4407 };
4408 Roo.extend(Roo.JsonView, Roo.View, {
4409     /**
4410      * @type {String} The root property in the loaded JSON object that contains the data
4411      */
4412     jsonRoot : "",
4413
4414     /**
4415      * Refreshes the view.
4416      */
4417     refresh : function(){
4418         this.clearSelections();
4419         this.el.update("");
4420         var html = [];
4421         var o = this.jsonData;
4422         if(o && o.length > 0){
4423             for(var i = 0, len = o.length; i < len; i++){
4424                 var data = this.prepareData(o[i], i, o);
4425                 html[html.length] = this.tpl.apply(data);
4426             }
4427         }else{
4428             html.push(this.emptyText);
4429         }
4430         this.el.update(html.join(""));
4431         this.nodes = this.el.dom.childNodes;
4432         this.updateIndexes(0);
4433     },
4434
4435     /**
4436      * 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.
4437      * @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:
4438      <pre><code>
4439      view.load({
4440          url: "your-url.php",
4441          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4442          callback: yourFunction,
4443          scope: yourObject, //(optional scope)
4444          discardUrl: false,
4445          nocache: false,
4446          text: "Loading...",
4447          timeout: 30,
4448          scripts: false
4449      });
4450      </code></pre>
4451      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4452      * 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.
4453      * @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}
4454      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4455      * @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.
4456      */
4457     load : function(){
4458         var um = this.el.getUpdateManager();
4459         um.update.apply(um, arguments);
4460     },
4461
4462     // note - render is a standard framework call...
4463     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4464     render : function(el, response){
4465         
4466         this.clearSelections();
4467         this.el.update("");
4468         var o;
4469         try{
4470             if (response != '') {
4471                 o = Roo.util.JSON.decode(response.responseText);
4472                 if(this.jsonRoot){
4473                     
4474                     o = o[this.jsonRoot];
4475                 }
4476             }
4477         } catch(e){
4478         }
4479         /**
4480          * The current JSON data or null
4481          */
4482         this.jsonData = o;
4483         this.beforeRender();
4484         this.refresh();
4485     },
4486
4487 /**
4488  * Get the number of records in the current JSON dataset
4489  * @return {Number}
4490  */
4491     getCount : function(){
4492         return this.jsonData ? this.jsonData.length : 0;
4493     },
4494
4495 /**
4496  * Returns the JSON object for the specified node(s)
4497  * @param {HTMLElement/Array} node The node or an array of nodes
4498  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4499  * you get the JSON object for the node
4500  */
4501     getNodeData : function(node){
4502         if(node instanceof Array){
4503             var data = [];
4504             for(var i = 0, len = node.length; i < len; i++){
4505                 data.push(this.getNodeData(node[i]));
4506             }
4507             return data;
4508         }
4509         return this.jsonData[this.indexOf(node)] || null;
4510     },
4511
4512     beforeRender : function(){
4513         this.snapshot = this.jsonData;
4514         if(this.sortInfo){
4515             this.sort.apply(this, this.sortInfo);
4516         }
4517         this.fireEvent("beforerender", this, this.jsonData);
4518     },
4519
4520     onLoad : function(el, o){
4521         this.fireEvent("load", this, this.jsonData, o);
4522     },
4523
4524     onLoadException : function(el, o){
4525         this.fireEvent("loadexception", this, o);
4526     },
4527
4528 /**
4529  * Filter the data by a specific property.
4530  * @param {String} property A property on your JSON objects
4531  * @param {String/RegExp} value Either string that the property values
4532  * should start with, or a RegExp to test against the property
4533  */
4534     filter : function(property, value){
4535         if(this.jsonData){
4536             var data = [];
4537             var ss = this.snapshot;
4538             if(typeof value == "string"){
4539                 var vlen = value.length;
4540                 if(vlen == 0){
4541                     this.clearFilter();
4542                     return;
4543                 }
4544                 value = value.toLowerCase();
4545                 for(var i = 0, len = ss.length; i < len; i++){
4546                     var o = ss[i];
4547                     if(o[property].substr(0, vlen).toLowerCase() == value){
4548                         data.push(o);
4549                     }
4550                 }
4551             } else if(value.exec){ // regex?
4552                 for(var i = 0, len = ss.length; i < len; i++){
4553                     var o = ss[i];
4554                     if(value.test(o[property])){
4555                         data.push(o);
4556                     }
4557                 }
4558             } else{
4559                 return;
4560             }
4561             this.jsonData = data;
4562             this.refresh();
4563         }
4564     },
4565
4566 /**
4567  * Filter by a function. The passed function will be called with each
4568  * object in the current dataset. If the function returns true the value is kept,
4569  * otherwise it is filtered.
4570  * @param {Function} fn
4571  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4572  */
4573     filterBy : function(fn, scope){
4574         if(this.jsonData){
4575             var data = [];
4576             var ss = this.snapshot;
4577             for(var i = 0, len = ss.length; i < len; i++){
4578                 var o = ss[i];
4579                 if(fn.call(scope || this, o)){
4580                     data.push(o);
4581                 }
4582             }
4583             this.jsonData = data;
4584             this.refresh();
4585         }
4586     },
4587
4588 /**
4589  * Clears the current filter.
4590  */
4591     clearFilter : function(){
4592         if(this.snapshot && this.jsonData != this.snapshot){
4593             this.jsonData = this.snapshot;
4594             this.refresh();
4595         }
4596     },
4597
4598
4599 /**
4600  * Sorts the data for this view and refreshes it.
4601  * @param {String} property A property on your JSON objects to sort on
4602  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4603  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4604  */
4605     sort : function(property, dir, sortType){
4606         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4607         if(this.jsonData){
4608             var p = property;
4609             var dsc = dir && dir.toLowerCase() == "desc";
4610             var f = function(o1, o2){
4611                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4612                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4613                 ;
4614                 if(v1 < v2){
4615                     return dsc ? +1 : -1;
4616                 } else if(v1 > v2){
4617                     return dsc ? -1 : +1;
4618                 } else{
4619                     return 0;
4620                 }
4621             };
4622             this.jsonData.sort(f);
4623             this.refresh();
4624             if(this.jsonData != this.snapshot){
4625                 this.snapshot.sort(f);
4626             }
4627         }
4628     }
4629 });/*
4630  * Based on:
4631  * Ext JS Library 1.1.1
4632  * Copyright(c) 2006-2007, Ext JS, LLC.
4633  *
4634  * Originally Released Under LGPL - original licence link has changed is not relivant.
4635  *
4636  * Fork - LGPL
4637  * <script type="text/javascript">
4638  */
4639  
4640
4641 /**
4642  * @class Roo.ColorPalette
4643  * @extends Roo.Component
4644  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4645  * Here's an example of typical usage:
4646  * <pre><code>
4647 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4648 cp.render('my-div');
4649
4650 cp.on('select', function(palette, selColor){
4651     // do something with selColor
4652 });
4653 </code></pre>
4654  * @constructor
4655  * Create a new ColorPalette
4656  * @param {Object} config The config object
4657  */
4658 Roo.ColorPalette = function(config){
4659     Roo.ColorPalette.superclass.constructor.call(this, config);
4660     this.addEvents({
4661         /**
4662              * @event select
4663              * Fires when a color is selected
4664              * @param {ColorPalette} this
4665              * @param {String} color The 6-digit color hex code (without the # symbol)
4666              */
4667         select: true
4668     });
4669
4670     if(this.handler){
4671         this.on("select", this.handler, this.scope, true);
4672     }
4673 };
4674 Roo.extend(Roo.ColorPalette, Roo.Component, {
4675     /**
4676      * @cfg {String} itemCls
4677      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4678      */
4679     itemCls : "x-color-palette",
4680     /**
4681      * @cfg {String} value
4682      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4683      * the hex codes are case-sensitive.
4684      */
4685     value : null,
4686     clickEvent:'click',
4687     // private
4688     ctype: "Roo.ColorPalette",
4689
4690     /**
4691      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4692      */
4693     allowReselect : false,
4694
4695     /**
4696      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4697      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4698      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4699      * of colors with the width setting until the box is symmetrical.</p>
4700      * <p>You can override individual colors if needed:</p>
4701      * <pre><code>
4702 var cp = new Roo.ColorPalette();
4703 cp.colors[0] = "FF0000";  // change the first box to red
4704 </code></pre>
4705
4706 Or you can provide a custom array of your own for complete control:
4707 <pre><code>
4708 var cp = new Roo.ColorPalette();
4709 cp.colors = ["000000", "993300", "333300"];
4710 </code></pre>
4711      * @type Array
4712      */
4713     colors : [
4714         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4715         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4716         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4717         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4718         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4719     ],
4720
4721     // private
4722     onRender : function(container, position){
4723         var t = new Roo.MasterTemplate(
4724             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4725         );
4726         var c = this.colors;
4727         for(var i = 0, len = c.length; i < len; i++){
4728             t.add([c[i]]);
4729         }
4730         var el = document.createElement("div");
4731         el.className = this.itemCls;
4732         t.overwrite(el);
4733         container.dom.insertBefore(el, position);
4734         this.el = Roo.get(el);
4735         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4736         if(this.clickEvent != 'click'){
4737             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4738         }
4739     },
4740
4741     // private
4742     afterRender : function(){
4743         Roo.ColorPalette.superclass.afterRender.call(this);
4744         if(this.value){
4745             var s = this.value;
4746             this.value = null;
4747             this.select(s);
4748         }
4749     },
4750
4751     // private
4752     handleClick : function(e, t){
4753         e.preventDefault();
4754         if(!this.disabled){
4755             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4756             this.select(c.toUpperCase());
4757         }
4758     },
4759
4760     /**
4761      * Selects the specified color in the palette (fires the select event)
4762      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4763      */
4764     select : function(color){
4765         color = color.replace("#", "");
4766         if(color != this.value || this.allowReselect){
4767             var el = this.el;
4768             if(this.value){
4769                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4770             }
4771             el.child("a.color-"+color).addClass("x-color-palette-sel");
4772             this.value = color;
4773             this.fireEvent("select", this, color);
4774         }
4775     }
4776 });/*
4777  * Based on:
4778  * Ext JS Library 1.1.1
4779  * Copyright(c) 2006-2007, Ext JS, LLC.
4780  *
4781  * Originally Released Under LGPL - original licence link has changed is not relivant.
4782  *
4783  * Fork - LGPL
4784  * <script type="text/javascript">
4785  */
4786  
4787 /**
4788  * @class Roo.DatePicker
4789  * @extends Roo.Component
4790  * Simple date picker class.
4791  * @constructor
4792  * Create a new DatePicker
4793  * @param {Object} config The config object
4794  */
4795 Roo.DatePicker = function(config){
4796     Roo.DatePicker.superclass.constructor.call(this, config);
4797
4798     this.value = config && config.value ?
4799                  config.value.clearTime() : new Date().clearTime();
4800
4801     this.addEvents({
4802         /**
4803              * @event select
4804              * Fires when a date is selected
4805              * @param {DatePicker} this
4806              * @param {Date} date The selected date
4807              */
4808         'select': true,
4809         /**
4810              * @event monthchange
4811              * Fires when the displayed month changes 
4812              * @param {DatePicker} this
4813              * @param {Date} date The selected month
4814              */
4815         'monthchange': true
4816     });
4817
4818     if(this.handler){
4819         this.on("select", this.handler,  this.scope || this);
4820     }
4821     // build the disabledDatesRE
4822     if(!this.disabledDatesRE && this.disabledDates){
4823         var dd = this.disabledDates;
4824         var re = "(?:";
4825         for(var i = 0; i < dd.length; i++){
4826             re += dd[i];
4827             if(i != dd.length-1) {
4828                 re += "|";
4829             }
4830         }
4831         this.disabledDatesRE = new RegExp(re + ")");
4832     }
4833 };
4834
4835 Roo.extend(Roo.DatePicker, Roo.Component, {
4836     /**
4837      * @cfg {String} todayText
4838      * The text to display on the button that selects the current date (defaults to "Today")
4839      */
4840     todayText : "Today",
4841     /**
4842      * @cfg {String} okText
4843      * The text to display on the ok button
4844      */
4845     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4846     /**
4847      * @cfg {String} cancelText
4848      * The text to display on the cancel button
4849      */
4850     cancelText : "Cancel",
4851     /**
4852      * @cfg {String} todayTip
4853      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4854      */
4855     todayTip : "{0} (Spacebar)",
4856     /**
4857      * @cfg {Date} minDate
4858      * Minimum allowable date (JavaScript date object, defaults to null)
4859      */
4860     minDate : null,
4861     /**
4862      * @cfg {Date} maxDate
4863      * Maximum allowable date (JavaScript date object, defaults to null)
4864      */
4865     maxDate : null,
4866     /**
4867      * @cfg {String} minText
4868      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4869      */
4870     minText : "This date is before the minimum date",
4871     /**
4872      * @cfg {String} maxText
4873      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4874      */
4875     maxText : "This date is after the maximum date",
4876     /**
4877      * @cfg {String} format
4878      * The default date format string which can be overriden for localization support.  The format must be
4879      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4880      */
4881     format : "m/d/y",
4882     /**
4883      * @cfg {Array} disabledDays
4884      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4885      */
4886     disabledDays : null,
4887     /**
4888      * @cfg {String} disabledDaysText
4889      * The tooltip to display when the date falls on a disabled day (defaults to "")
4890      */
4891     disabledDaysText : "",
4892     /**
4893      * @cfg {RegExp} disabledDatesRE
4894      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4895      */
4896     disabledDatesRE : null,
4897     /**
4898      * @cfg {String} disabledDatesText
4899      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4900      */
4901     disabledDatesText : "",
4902     /**
4903      * @cfg {Boolean} constrainToViewport
4904      * True to constrain the date picker to the viewport (defaults to true)
4905      */
4906     constrainToViewport : true,
4907     /**
4908      * @cfg {Array} monthNames
4909      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4910      */
4911     monthNames : Date.monthNames,
4912     /**
4913      * @cfg {Array} dayNames
4914      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4915      */
4916     dayNames : Date.dayNames,
4917     /**
4918      * @cfg {String} nextText
4919      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4920      */
4921     nextText: 'Next Month (Control+Right)',
4922     /**
4923      * @cfg {String} prevText
4924      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4925      */
4926     prevText: 'Previous Month (Control+Left)',
4927     /**
4928      * @cfg {String} monthYearText
4929      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4930      */
4931     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4932     /**
4933      * @cfg {Number} startDay
4934      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4935      */
4936     startDay : 0,
4937     /**
4938      * @cfg {Bool} showClear
4939      * Show a clear button (usefull for date form elements that can be blank.)
4940      */
4941     
4942     showClear: false,
4943     
4944     /**
4945      * Sets the value of the date field
4946      * @param {Date} value The date to set
4947      */
4948     setValue : function(value){
4949         var old = this.value;
4950         
4951         if (typeof(value) == 'string') {
4952          
4953             value = Date.parseDate(value, this.format);
4954         }
4955         if (!value) {
4956             value = new Date();
4957         }
4958         
4959         this.value = value.clearTime(true);
4960         if(this.el){
4961             this.update(this.value);
4962         }
4963     },
4964
4965     /**
4966      * Gets the current selected value of the date field
4967      * @return {Date} The selected date
4968      */
4969     getValue : function(){
4970         return this.value;
4971     },
4972
4973     // private
4974     focus : function(){
4975         if(this.el){
4976             this.update(this.activeDate);
4977         }
4978     },
4979
4980     // privateval
4981     onRender : function(container, position){
4982         
4983         var m = [
4984              '<table cellspacing="0">',
4985                 '<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>',
4986                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
4987         var dn = this.dayNames;
4988         for(var i = 0; i < 7; i++){
4989             var d = this.startDay+i;
4990             if(d > 6){
4991                 d = d-7;
4992             }
4993             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
4994         }
4995         m[m.length] = "</tr></thead><tbody><tr>";
4996         for(var i = 0; i < 42; i++) {
4997             if(i % 7 == 0 && i != 0){
4998                 m[m.length] = "</tr><tr>";
4999             }
5000             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5001         }
5002         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5003             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5004
5005         var el = document.createElement("div");
5006         el.className = "x-date-picker";
5007         el.innerHTML = m.join("");
5008
5009         container.dom.insertBefore(el, position);
5010
5011         this.el = Roo.get(el);
5012         this.eventEl = Roo.get(el.firstChild);
5013
5014         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5015             handler: this.showPrevMonth,
5016             scope: this,
5017             preventDefault:true,
5018             stopDefault:true
5019         });
5020
5021         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5022             handler: this.showNextMonth,
5023             scope: this,
5024             preventDefault:true,
5025             stopDefault:true
5026         });
5027
5028         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5029
5030         this.monthPicker = this.el.down('div.x-date-mp');
5031         this.monthPicker.enableDisplayMode('block');
5032         
5033         var kn = new Roo.KeyNav(this.eventEl, {
5034             "left" : function(e){
5035                 e.ctrlKey ?
5036                     this.showPrevMonth() :
5037                     this.update(this.activeDate.add("d", -1));
5038             },
5039
5040             "right" : function(e){
5041                 e.ctrlKey ?
5042                     this.showNextMonth() :
5043                     this.update(this.activeDate.add("d", 1));
5044             },
5045
5046             "up" : function(e){
5047                 e.ctrlKey ?
5048                     this.showNextYear() :
5049                     this.update(this.activeDate.add("d", -7));
5050             },
5051
5052             "down" : function(e){
5053                 e.ctrlKey ?
5054                     this.showPrevYear() :
5055                     this.update(this.activeDate.add("d", 7));
5056             },
5057
5058             "pageUp" : function(e){
5059                 this.showNextMonth();
5060             },
5061
5062             "pageDown" : function(e){
5063                 this.showPrevMonth();
5064             },
5065
5066             "enter" : function(e){
5067                 e.stopPropagation();
5068                 return true;
5069             },
5070
5071             scope : this
5072         });
5073
5074         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5075
5076         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5077
5078         this.el.unselectable();
5079         
5080         this.cells = this.el.select("table.x-date-inner tbody td");
5081         this.textNodes = this.el.query("table.x-date-inner tbody span");
5082
5083         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5084             text: "&#160;",
5085             tooltip: this.monthYearText
5086         });
5087
5088         this.mbtn.on('click', this.showMonthPicker, this);
5089         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5090
5091
5092         var today = (new Date()).dateFormat(this.format);
5093         
5094         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5095         if (this.showClear) {
5096             baseTb.add( new Roo.Toolbar.Fill());
5097         }
5098         baseTb.add({
5099             text: String.format(this.todayText, today),
5100             tooltip: String.format(this.todayTip, today),
5101             handler: this.selectToday,
5102             scope: this
5103         });
5104         
5105         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5106             
5107         //});
5108         if (this.showClear) {
5109             
5110             baseTb.add( new Roo.Toolbar.Fill());
5111             baseTb.add({
5112                 text: '&#160;',
5113                 cls: 'x-btn-icon x-btn-clear',
5114                 handler: function() {
5115                     //this.value = '';
5116                     this.fireEvent("select", this, '');
5117                 },
5118                 scope: this
5119             });
5120         }
5121         
5122         
5123         if(Roo.isIE){
5124             this.el.repaint();
5125         }
5126         this.update(this.value);
5127     },
5128
5129     createMonthPicker : function(){
5130         if(!this.monthPicker.dom.firstChild){
5131             var buf = ['<table border="0" cellspacing="0">'];
5132             for(var i = 0; i < 6; i++){
5133                 buf.push(
5134                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5135                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5136                     i == 0 ?
5137                     '<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>' :
5138                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5139                 );
5140             }
5141             buf.push(
5142                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5143                     this.okText,
5144                     '</button><button type="button" class="x-date-mp-cancel">',
5145                     this.cancelText,
5146                     '</button></td></tr>',
5147                 '</table>'
5148             );
5149             this.monthPicker.update(buf.join(''));
5150             this.monthPicker.on('click', this.onMonthClick, this);
5151             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5152
5153             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5154             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5155
5156             this.mpMonths.each(function(m, a, i){
5157                 i += 1;
5158                 if((i%2) == 0){
5159                     m.dom.xmonth = 5 + Math.round(i * .5);
5160                 }else{
5161                     m.dom.xmonth = Math.round((i-1) * .5);
5162                 }
5163             });
5164         }
5165     },
5166
5167     showMonthPicker : function(){
5168         this.createMonthPicker();
5169         var size = this.el.getSize();
5170         this.monthPicker.setSize(size);
5171         this.monthPicker.child('table').setSize(size);
5172
5173         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5174         this.updateMPMonth(this.mpSelMonth);
5175         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5176         this.updateMPYear(this.mpSelYear);
5177
5178         this.monthPicker.slideIn('t', {duration:.2});
5179     },
5180
5181     updateMPYear : function(y){
5182         this.mpyear = y;
5183         var ys = this.mpYears.elements;
5184         for(var i = 1; i <= 10; i++){
5185             var td = ys[i-1], y2;
5186             if((i%2) == 0){
5187                 y2 = y + Math.round(i * .5);
5188                 td.firstChild.innerHTML = y2;
5189                 td.xyear = y2;
5190             }else{
5191                 y2 = y - (5-Math.round(i * .5));
5192                 td.firstChild.innerHTML = y2;
5193                 td.xyear = y2;
5194             }
5195             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5196         }
5197     },
5198
5199     updateMPMonth : function(sm){
5200         this.mpMonths.each(function(m, a, i){
5201             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5202         });
5203     },
5204
5205     selectMPMonth: function(m){
5206         
5207     },
5208
5209     onMonthClick : function(e, t){
5210         e.stopEvent();
5211         var el = new Roo.Element(t), pn;
5212         if(el.is('button.x-date-mp-cancel')){
5213             this.hideMonthPicker();
5214         }
5215         else if(el.is('button.x-date-mp-ok')){
5216             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5217             this.hideMonthPicker();
5218         }
5219         else if(pn = el.up('td.x-date-mp-month', 2)){
5220             this.mpMonths.removeClass('x-date-mp-sel');
5221             pn.addClass('x-date-mp-sel');
5222             this.mpSelMonth = pn.dom.xmonth;
5223         }
5224         else if(pn = el.up('td.x-date-mp-year', 2)){
5225             this.mpYears.removeClass('x-date-mp-sel');
5226             pn.addClass('x-date-mp-sel');
5227             this.mpSelYear = pn.dom.xyear;
5228         }
5229         else if(el.is('a.x-date-mp-prev')){
5230             this.updateMPYear(this.mpyear-10);
5231         }
5232         else if(el.is('a.x-date-mp-next')){
5233             this.updateMPYear(this.mpyear+10);
5234         }
5235     },
5236
5237     onMonthDblClick : function(e, t){
5238         e.stopEvent();
5239         var el = new Roo.Element(t), pn;
5240         if(pn = el.up('td.x-date-mp-month', 2)){
5241             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5242             this.hideMonthPicker();
5243         }
5244         else if(pn = el.up('td.x-date-mp-year', 2)){
5245             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5246             this.hideMonthPicker();
5247         }
5248     },
5249
5250     hideMonthPicker : function(disableAnim){
5251         if(this.monthPicker){
5252             if(disableAnim === true){
5253                 this.monthPicker.hide();
5254             }else{
5255                 this.monthPicker.slideOut('t', {duration:.2});
5256             }
5257         }
5258     },
5259
5260     // private
5261     showPrevMonth : function(e){
5262         this.update(this.activeDate.add("mo", -1));
5263     },
5264
5265     // private
5266     showNextMonth : function(e){
5267         this.update(this.activeDate.add("mo", 1));
5268     },
5269
5270     // private
5271     showPrevYear : function(){
5272         this.update(this.activeDate.add("y", -1));
5273     },
5274
5275     // private
5276     showNextYear : function(){
5277         this.update(this.activeDate.add("y", 1));
5278     },
5279
5280     // private
5281     handleMouseWheel : function(e){
5282         var delta = e.getWheelDelta();
5283         if(delta > 0){
5284             this.showPrevMonth();
5285             e.stopEvent();
5286         } else if(delta < 0){
5287             this.showNextMonth();
5288             e.stopEvent();
5289         }
5290     },
5291
5292     // private
5293     handleDateClick : function(e, t){
5294         e.stopEvent();
5295         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5296             this.setValue(new Date(t.dateValue));
5297             this.fireEvent("select", this, this.value);
5298         }
5299     },
5300
5301     // private
5302     selectToday : function(){
5303         this.setValue(new Date().clearTime());
5304         this.fireEvent("select", this, this.value);
5305     },
5306
5307     // private
5308     update : function(date)
5309     {
5310         var vd = this.activeDate;
5311         this.activeDate = date;
5312         if(vd && this.el){
5313             var t = date.getTime();
5314             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5315                 this.cells.removeClass("x-date-selected");
5316                 this.cells.each(function(c){
5317                    if(c.dom.firstChild.dateValue == t){
5318                        c.addClass("x-date-selected");
5319                        setTimeout(function(){
5320                             try{c.dom.firstChild.focus();}catch(e){}
5321                        }, 50);
5322                        return false;
5323                    }
5324                 });
5325                 return;
5326             }
5327         }
5328         
5329         var days = date.getDaysInMonth();
5330         var firstOfMonth = date.getFirstDateOfMonth();
5331         var startingPos = firstOfMonth.getDay()-this.startDay;
5332
5333         if(startingPos <= this.startDay){
5334             startingPos += 7;
5335         }
5336
5337         var pm = date.add("mo", -1);
5338         var prevStart = pm.getDaysInMonth()-startingPos;
5339
5340         var cells = this.cells.elements;
5341         var textEls = this.textNodes;
5342         days += startingPos;
5343
5344         // convert everything to numbers so it's fast
5345         var day = 86400000;
5346         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5347         var today = new Date().clearTime().getTime();
5348         var sel = date.clearTime().getTime();
5349         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5350         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5351         var ddMatch = this.disabledDatesRE;
5352         var ddText = this.disabledDatesText;
5353         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5354         var ddaysText = this.disabledDaysText;
5355         var format = this.format;
5356
5357         var setCellClass = function(cal, cell){
5358             cell.title = "";
5359             var t = d.getTime();
5360             cell.firstChild.dateValue = t;
5361             if(t == today){
5362                 cell.className += " x-date-today";
5363                 cell.title = cal.todayText;
5364             }
5365             if(t == sel){
5366                 cell.className += " x-date-selected";
5367                 setTimeout(function(){
5368                     try{cell.firstChild.focus();}catch(e){}
5369                 }, 50);
5370             }
5371             // disabling
5372             if(t < min) {
5373                 cell.className = " x-date-disabled";
5374                 cell.title = cal.minText;
5375                 return;
5376             }
5377             if(t > max) {
5378                 cell.className = " x-date-disabled";
5379                 cell.title = cal.maxText;
5380                 return;
5381             }
5382             if(ddays){
5383                 if(ddays.indexOf(d.getDay()) != -1){
5384                     cell.title = ddaysText;
5385                     cell.className = " x-date-disabled";
5386                 }
5387             }
5388             if(ddMatch && format){
5389                 var fvalue = d.dateFormat(format);
5390                 if(ddMatch.test(fvalue)){
5391                     cell.title = ddText.replace("%0", fvalue);
5392                     cell.className = " x-date-disabled";
5393                 }
5394             }
5395         };
5396
5397         var i = 0;
5398         for(; i < startingPos; i++) {
5399             textEls[i].innerHTML = (++prevStart);
5400             d.setDate(d.getDate()+1);
5401             cells[i].className = "x-date-prevday";
5402             setCellClass(this, cells[i]);
5403         }
5404         for(; i < days; i++){
5405             intDay = i - startingPos + 1;
5406             textEls[i].innerHTML = (intDay);
5407             d.setDate(d.getDate()+1);
5408             cells[i].className = "x-date-active";
5409             setCellClass(this, cells[i]);
5410         }
5411         var extraDays = 0;
5412         for(; i < 42; i++) {
5413              textEls[i].innerHTML = (++extraDays);
5414              d.setDate(d.getDate()+1);
5415              cells[i].className = "x-date-nextday";
5416              setCellClass(this, cells[i]);
5417         }
5418
5419         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5420         this.fireEvent('monthchange', this, date);
5421         
5422         if(!this.internalRender){
5423             var main = this.el.dom.firstChild;
5424             var w = main.offsetWidth;
5425             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5426             Roo.fly(main).setWidth(w);
5427             this.internalRender = true;
5428             // opera does not respect the auto grow header center column
5429             // then, after it gets a width opera refuses to recalculate
5430             // without a second pass
5431             if(Roo.isOpera && !this.secondPass){
5432                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5433                 this.secondPass = true;
5434                 this.update.defer(10, this, [date]);
5435             }
5436         }
5437         
5438         
5439     }
5440 });        /*
5441  * Based on:
5442  * Ext JS Library 1.1.1
5443  * Copyright(c) 2006-2007, Ext JS, LLC.
5444  *
5445  * Originally Released Under LGPL - original licence link has changed is not relivant.
5446  *
5447  * Fork - LGPL
5448  * <script type="text/javascript">
5449  */
5450 /**
5451  * @class Roo.TabPanel
5452  * @extends Roo.util.Observable
5453  * A lightweight tab container.
5454  * <br><br>
5455  * Usage:
5456  * <pre><code>
5457 // basic tabs 1, built from existing content
5458 var tabs = new Roo.TabPanel("tabs1");
5459 tabs.addTab("script", "View Script");
5460 tabs.addTab("markup", "View Markup");
5461 tabs.activate("script");
5462
5463 // more advanced tabs, built from javascript
5464 var jtabs = new Roo.TabPanel("jtabs");
5465 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5466
5467 // set up the UpdateManager
5468 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5469 var updater = tab2.getUpdateManager();
5470 updater.setDefaultUrl("ajax1.htm");
5471 tab2.on('activate', updater.refresh, updater, true);
5472
5473 // Use setUrl for Ajax loading
5474 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5475 tab3.setUrl("ajax2.htm", null, true);
5476
5477 // Disabled tab
5478 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5479 tab4.disable();
5480
5481 jtabs.activate("jtabs-1");
5482  * </code></pre>
5483  * @constructor
5484  * Create a new TabPanel.
5485  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5486  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5487  */
5488 Roo.TabPanel = function(container, config){
5489     /**
5490     * The container element for this TabPanel.
5491     * @type Roo.Element
5492     */
5493     this.el = Roo.get(container, true);
5494     if(config){
5495         if(typeof config == "boolean"){
5496             this.tabPosition = config ? "bottom" : "top";
5497         }else{
5498             Roo.apply(this, config);
5499         }
5500     }
5501     if(this.tabPosition == "bottom"){
5502         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5503         this.el.addClass("x-tabs-bottom");
5504     }
5505     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5506     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5507     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5508     if(Roo.isIE){
5509         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5510     }
5511     if(this.tabPosition != "bottom"){
5512         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5513          * @type Roo.Element
5514          */
5515         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5516         this.el.addClass("x-tabs-top");
5517     }
5518     this.items = [];
5519
5520     this.bodyEl.setStyle("position", "relative");
5521
5522     this.active = null;
5523     this.activateDelegate = this.activate.createDelegate(this);
5524
5525     this.addEvents({
5526         /**
5527          * @event tabchange
5528          * Fires when the active tab changes
5529          * @param {Roo.TabPanel} this
5530          * @param {Roo.TabPanelItem} activePanel The new active tab
5531          */
5532         "tabchange": true,
5533         /**
5534          * @event beforetabchange
5535          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5536          * @param {Roo.TabPanel} this
5537          * @param {Object} e Set cancel to true on this object to cancel the tab change
5538          * @param {Roo.TabPanelItem} tab The tab being changed to
5539          */
5540         "beforetabchange" : true
5541     });
5542
5543     Roo.EventManager.onWindowResize(this.onResize, this);
5544     this.cpad = this.el.getPadding("lr");
5545     this.hiddenCount = 0;
5546
5547
5548     // toolbar on the tabbar support...
5549     if (this.toolbar) {
5550         var tcfg = this.toolbar;
5551         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5552         this.toolbar = new Roo.Toolbar(tcfg);
5553         if (Roo.isSafari) {
5554             var tbl = tcfg.container.child('table', true);
5555             tbl.setAttribute('width', '100%');
5556         }
5557         
5558     }
5559    
5560
5561
5562     Roo.TabPanel.superclass.constructor.call(this);
5563 };
5564
5565 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5566     /*
5567      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5568      */
5569     tabPosition : "top",
5570     /*
5571      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5572      */
5573     currentTabWidth : 0,
5574     /*
5575      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5576      */
5577     minTabWidth : 40,
5578     /*
5579      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5580      */
5581     maxTabWidth : 250,
5582     /*
5583      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5584      */
5585     preferredTabWidth : 175,
5586     /*
5587      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5588      */
5589     resizeTabs : false,
5590     /*
5591      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5592      */
5593     monitorResize : true,
5594     /*
5595      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5596      */
5597     toolbar : false,
5598
5599     /**
5600      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5601      * @param {String} id The id of the div to use <b>or create</b>
5602      * @param {String} text The text for the tab
5603      * @param {String} content (optional) Content to put in the TabPanelItem body
5604      * @param {Boolean} closable (optional) True to create a close icon on the tab
5605      * @return {Roo.TabPanelItem} The created TabPanelItem
5606      */
5607     addTab : function(id, text, content, closable){
5608         var item = new Roo.TabPanelItem(this, id, text, closable);
5609         this.addTabItem(item);
5610         if(content){
5611             item.setContent(content);
5612         }
5613         return item;
5614     },
5615
5616     /**
5617      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5618      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5619      * @return {Roo.TabPanelItem}
5620      */
5621     getTab : function(id){
5622         return this.items[id];
5623     },
5624
5625     /**
5626      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5627      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5628      */
5629     hideTab : function(id){
5630         var t = this.items[id];
5631         if(!t.isHidden()){
5632            t.setHidden(true);
5633            this.hiddenCount++;
5634            this.autoSizeTabs();
5635         }
5636     },
5637
5638     /**
5639      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5640      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5641      */
5642     unhideTab : function(id){
5643         var t = this.items[id];
5644         if(t.isHidden()){
5645            t.setHidden(false);
5646            this.hiddenCount--;
5647            this.autoSizeTabs();
5648         }
5649     },
5650
5651     /**
5652      * Adds an existing {@link Roo.TabPanelItem}.
5653      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5654      */
5655     addTabItem : function(item){
5656         this.items[item.id] = item;
5657         this.items.push(item);
5658         if(this.resizeTabs){
5659            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5660            this.autoSizeTabs();
5661         }else{
5662             item.autoSize();
5663         }
5664     },
5665
5666     /**
5667      * Removes a {@link Roo.TabPanelItem}.
5668      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5669      */
5670     removeTab : function(id){
5671         var items = this.items;
5672         var tab = items[id];
5673         if(!tab) { return; }
5674         var index = items.indexOf(tab);
5675         if(this.active == tab && items.length > 1){
5676             var newTab = this.getNextAvailable(index);
5677             if(newTab) {
5678                 newTab.activate();
5679             }
5680         }
5681         this.stripEl.dom.removeChild(tab.pnode.dom);
5682         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5683             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5684         }
5685         items.splice(index, 1);
5686         delete this.items[tab.id];
5687         tab.fireEvent("close", tab);
5688         tab.purgeListeners();
5689         this.autoSizeTabs();
5690     },
5691
5692     getNextAvailable : function(start){
5693         var items = this.items;
5694         var index = start;
5695         // look for a next tab that will slide over to
5696         // replace the one being removed
5697         while(index < items.length){
5698             var item = items[++index];
5699             if(item && !item.isHidden()){
5700                 return item;
5701             }
5702         }
5703         // if one isn't found select the previous tab (on the left)
5704         index = start;
5705         while(index >= 0){
5706             var item = items[--index];
5707             if(item && !item.isHidden()){
5708                 return item;
5709             }
5710         }
5711         return null;
5712     },
5713
5714     /**
5715      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5716      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5717      */
5718     disableTab : function(id){
5719         var tab = this.items[id];
5720         if(tab && this.active != tab){
5721             tab.disable();
5722         }
5723     },
5724
5725     /**
5726      * Enables a {@link Roo.TabPanelItem} that is disabled.
5727      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5728      */
5729     enableTab : function(id){
5730         var tab = this.items[id];
5731         tab.enable();
5732     },
5733
5734     /**
5735      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5736      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5737      * @return {Roo.TabPanelItem} The TabPanelItem.
5738      */
5739     activate : function(id){
5740         var tab = this.items[id];
5741         if(!tab){
5742             return null;
5743         }
5744         if(tab == this.active || tab.disabled){
5745             return tab;
5746         }
5747         var e = {};
5748         this.fireEvent("beforetabchange", this, e, tab);
5749         if(e.cancel !== true && !tab.disabled){
5750             if(this.active){
5751                 this.active.hide();
5752             }
5753             this.active = this.items[id];
5754             this.active.show();
5755             this.fireEvent("tabchange", this, this.active);
5756         }
5757         return tab;
5758     },
5759
5760     /**
5761      * Gets the active {@link Roo.TabPanelItem}.
5762      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5763      */
5764     getActiveTab : function(){
5765         return this.active;
5766     },
5767
5768     /**
5769      * Updates the tab body element to fit the height of the container element
5770      * for overflow scrolling
5771      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5772      */
5773     syncHeight : function(targetHeight){
5774         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5775         var bm = this.bodyEl.getMargins();
5776         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5777         this.bodyEl.setHeight(newHeight);
5778         return newHeight;
5779     },
5780
5781     onResize : function(){
5782         if(this.monitorResize){
5783             this.autoSizeTabs();
5784         }
5785     },
5786
5787     /**
5788      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5789      */
5790     beginUpdate : function(){
5791         this.updating = true;
5792     },
5793
5794     /**
5795      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5796      */
5797     endUpdate : function(){
5798         this.updating = false;
5799         this.autoSizeTabs();
5800     },
5801
5802     /**
5803      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5804      */
5805     autoSizeTabs : function(){
5806         var count = this.items.length;
5807         var vcount = count - this.hiddenCount;
5808         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5809             return;
5810         }
5811         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5812         var availWidth = Math.floor(w / vcount);
5813         var b = this.stripBody;
5814         if(b.getWidth() > w){
5815             var tabs = this.items;
5816             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5817             if(availWidth < this.minTabWidth){
5818                 /*if(!this.sleft){    // incomplete scrolling code
5819                     this.createScrollButtons();
5820                 }
5821                 this.showScroll();
5822                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5823             }
5824         }else{
5825             if(this.currentTabWidth < this.preferredTabWidth){
5826                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5827             }
5828         }
5829     },
5830
5831     /**
5832      * Returns the number of tabs in this TabPanel.
5833      * @return {Number}
5834      */
5835      getCount : function(){
5836          return this.items.length;
5837      },
5838
5839     /**
5840      * Resizes all the tabs to the passed width
5841      * @param {Number} The new width
5842      */
5843     setTabWidth : function(width){
5844         this.currentTabWidth = width;
5845         for(var i = 0, len = this.items.length; i < len; i++) {
5846                 if(!this.items[i].isHidden()) {
5847                 this.items[i].setWidth(width);
5848             }
5849         }
5850     },
5851
5852     /**
5853      * Destroys this TabPanel
5854      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5855      */
5856     destroy : function(removeEl){
5857         Roo.EventManager.removeResizeListener(this.onResize, this);
5858         for(var i = 0, len = this.items.length; i < len; i++){
5859             this.items[i].purgeListeners();
5860         }
5861         if(removeEl === true){
5862             this.el.update("");
5863             this.el.remove();
5864         }
5865     }
5866 });
5867
5868 /**
5869  * @class Roo.TabPanelItem
5870  * @extends Roo.util.Observable
5871  * Represents an individual item (tab plus body) in a TabPanel.
5872  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5873  * @param {String} id The id of this TabPanelItem
5874  * @param {String} text The text for the tab of this TabPanelItem
5875  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5876  */
5877 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5878     /**
5879      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5880      * @type Roo.TabPanel
5881      */
5882     this.tabPanel = tabPanel;
5883     /**
5884      * The id for this TabPanelItem
5885      * @type String
5886      */
5887     this.id = id;
5888     /** @private */
5889     this.disabled = false;
5890     /** @private */
5891     this.text = text;
5892     /** @private */
5893     this.loaded = false;
5894     this.closable = closable;
5895
5896     /**
5897      * The body element for this TabPanelItem.
5898      * @type Roo.Element
5899      */
5900     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5901     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5902     this.bodyEl.setStyle("display", "block");
5903     this.bodyEl.setStyle("zoom", "1");
5904     this.hideAction();
5905
5906     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5907     /** @private */
5908     this.el = Roo.get(els.el, true);
5909     this.inner = Roo.get(els.inner, true);
5910     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5911     this.pnode = Roo.get(els.el.parentNode, true);
5912     this.el.on("mousedown", this.onTabMouseDown, this);
5913     this.el.on("click", this.onTabClick, this);
5914     /** @private */
5915     if(closable){
5916         var c = Roo.get(els.close, true);
5917         c.dom.title = this.closeText;
5918         c.addClassOnOver("close-over");
5919         c.on("click", this.closeClick, this);
5920      }
5921
5922     this.addEvents({
5923          /**
5924          * @event activate
5925          * Fires when this tab becomes the active tab.
5926          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5927          * @param {Roo.TabPanelItem} this
5928          */
5929         "activate": true,
5930         /**
5931          * @event beforeclose
5932          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5933          * @param {Roo.TabPanelItem} this
5934          * @param {Object} e Set cancel to true on this object to cancel the close.
5935          */
5936         "beforeclose": true,
5937         /**
5938          * @event close
5939          * Fires when this tab is closed.
5940          * @param {Roo.TabPanelItem} this
5941          */
5942          "close": true,
5943         /**
5944          * @event deactivate
5945          * Fires when this tab is no longer the active tab.
5946          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5947          * @param {Roo.TabPanelItem} this
5948          */
5949          "deactivate" : true
5950     });
5951     this.hidden = false;
5952
5953     Roo.TabPanelItem.superclass.constructor.call(this);
5954 };
5955
5956 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5957     purgeListeners : function(){
5958        Roo.util.Observable.prototype.purgeListeners.call(this);
5959        this.el.removeAllListeners();
5960     },
5961     /**
5962      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5963      */
5964     show : function(){
5965         this.pnode.addClass("on");
5966         this.showAction();
5967         if(Roo.isOpera){
5968             this.tabPanel.stripWrap.repaint();
5969         }
5970         this.fireEvent("activate", this.tabPanel, this);
5971     },
5972
5973     /**
5974      * Returns true if this tab is the active tab.
5975      * @return {Boolean}
5976      */
5977     isActive : function(){
5978         return this.tabPanel.getActiveTab() == this;
5979     },
5980
5981     /**
5982      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
5983      */
5984     hide : function(){
5985         this.pnode.removeClass("on");
5986         this.hideAction();
5987         this.fireEvent("deactivate", this.tabPanel, this);
5988     },
5989
5990     hideAction : function(){
5991         this.bodyEl.hide();
5992         this.bodyEl.setStyle("position", "absolute");
5993         this.bodyEl.setLeft("-20000px");
5994         this.bodyEl.setTop("-20000px");
5995     },
5996
5997     showAction : function(){
5998         this.bodyEl.setStyle("position", "relative");
5999         this.bodyEl.setTop("");
6000         this.bodyEl.setLeft("");
6001         this.bodyEl.show();
6002     },
6003
6004     /**
6005      * Set the tooltip for the tab.
6006      * @param {String} tooltip The tab's tooltip
6007      */
6008     setTooltip : function(text){
6009         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6010             this.textEl.dom.qtip = text;
6011             this.textEl.dom.removeAttribute('title');
6012         }else{
6013             this.textEl.dom.title = text;
6014         }
6015     },
6016
6017     onTabClick : function(e){
6018         e.preventDefault();
6019         this.tabPanel.activate(this.id);
6020     },
6021
6022     onTabMouseDown : function(e){
6023         e.preventDefault();
6024         this.tabPanel.activate(this.id);
6025     },
6026
6027     getWidth : function(){
6028         return this.inner.getWidth();
6029     },
6030
6031     setWidth : function(width){
6032         var iwidth = width - this.pnode.getPadding("lr");
6033         this.inner.setWidth(iwidth);
6034         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6035         this.pnode.setWidth(width);
6036     },
6037
6038     /**
6039      * Show or hide the tab
6040      * @param {Boolean} hidden True to hide or false to show.
6041      */
6042     setHidden : function(hidden){
6043         this.hidden = hidden;
6044         this.pnode.setStyle("display", hidden ? "none" : "");
6045     },
6046
6047     /**
6048      * Returns true if this tab is "hidden"
6049      * @return {Boolean}
6050      */
6051     isHidden : function(){
6052         return this.hidden;
6053     },
6054
6055     /**
6056      * Returns the text for this tab
6057      * @return {String}
6058      */
6059     getText : function(){
6060         return this.text;
6061     },
6062
6063     autoSize : function(){
6064         //this.el.beginMeasure();
6065         this.textEl.setWidth(1);
6066         /*
6067          *  #2804 [new] Tabs in Roojs
6068          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6069          */
6070         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6071         //this.el.endMeasure();
6072     },
6073
6074     /**
6075      * Sets the text for the tab (Note: this also sets the tooltip text)
6076      * @param {String} text The tab's text and tooltip
6077      */
6078     setText : function(text){
6079         this.text = text;
6080         this.textEl.update(text);
6081         this.setTooltip(text);
6082         if(!this.tabPanel.resizeTabs){
6083             this.autoSize();
6084         }
6085     },
6086     /**
6087      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6088      */
6089     activate : function(){
6090         this.tabPanel.activate(this.id);
6091     },
6092
6093     /**
6094      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6095      */
6096     disable : function(){
6097         if(this.tabPanel.active != this){
6098             this.disabled = true;
6099             this.pnode.addClass("disabled");
6100         }
6101     },
6102
6103     /**
6104      * Enables this TabPanelItem if it was previously disabled.
6105      */
6106     enable : function(){
6107         this.disabled = false;
6108         this.pnode.removeClass("disabled");
6109     },
6110
6111     /**
6112      * Sets the content for this TabPanelItem.
6113      * @param {String} content The content
6114      * @param {Boolean} loadScripts true to look for and load scripts
6115      */
6116     setContent : function(content, loadScripts){
6117         this.bodyEl.update(content, loadScripts);
6118     },
6119
6120     /**
6121      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6122      * @return {Roo.UpdateManager} The UpdateManager
6123      */
6124     getUpdateManager : function(){
6125         return this.bodyEl.getUpdateManager();
6126     },
6127
6128     /**
6129      * Set a URL to be used to load the content for this TabPanelItem.
6130      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6131      * @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)
6132      * @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)
6133      * @return {Roo.UpdateManager} The UpdateManager
6134      */
6135     setUrl : function(url, params, loadOnce){
6136         if(this.refreshDelegate){
6137             this.un('activate', this.refreshDelegate);
6138         }
6139         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6140         this.on("activate", this.refreshDelegate);
6141         return this.bodyEl.getUpdateManager();
6142     },
6143
6144     /** @private */
6145     _handleRefresh : function(url, params, loadOnce){
6146         if(!loadOnce || !this.loaded){
6147             var updater = this.bodyEl.getUpdateManager();
6148             updater.update(url, params, this._setLoaded.createDelegate(this));
6149         }
6150     },
6151
6152     /**
6153      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6154      *   Will fail silently if the setUrl method has not been called.
6155      *   This does not activate the panel, just updates its content.
6156      */
6157     refresh : function(){
6158         if(this.refreshDelegate){
6159            this.loaded = false;
6160            this.refreshDelegate();
6161         }
6162     },
6163
6164     /** @private */
6165     _setLoaded : function(){
6166         this.loaded = true;
6167     },
6168
6169     /** @private */
6170     closeClick : function(e){
6171         var o = {};
6172         e.stopEvent();
6173         this.fireEvent("beforeclose", this, o);
6174         if(o.cancel !== true){
6175             this.tabPanel.removeTab(this.id);
6176         }
6177     },
6178     /**
6179      * The text displayed in the tooltip for the close icon.
6180      * @type String
6181      */
6182     closeText : "Close this tab"
6183 });
6184
6185 /** @private */
6186 Roo.TabPanel.prototype.createStrip = function(container){
6187     var strip = document.createElement("div");
6188     strip.className = "x-tabs-wrap";
6189     container.appendChild(strip);
6190     return strip;
6191 };
6192 /** @private */
6193 Roo.TabPanel.prototype.createStripList = function(strip){
6194     // div wrapper for retard IE
6195     // returns the "tr" element.
6196     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6197         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6198         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6199     return strip.firstChild.firstChild.firstChild.firstChild;
6200 };
6201 /** @private */
6202 Roo.TabPanel.prototype.createBody = function(container){
6203     var body = document.createElement("div");
6204     Roo.id(body, "tab-body");
6205     Roo.fly(body).addClass("x-tabs-body");
6206     container.appendChild(body);
6207     return body;
6208 };
6209 /** @private */
6210 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6211     var body = Roo.getDom(id);
6212     if(!body){
6213         body = document.createElement("div");
6214         body.id = id;
6215     }
6216     Roo.fly(body).addClass("x-tabs-item-body");
6217     bodyEl.insertBefore(body, bodyEl.firstChild);
6218     return body;
6219 };
6220 /** @private */
6221 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6222     var td = document.createElement("td");
6223     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6224     //stripEl.appendChild(td);
6225     if(closable){
6226         td.className = "x-tabs-closable";
6227         if(!this.closeTpl){
6228             this.closeTpl = new Roo.Template(
6229                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6230                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6231                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6232             );
6233         }
6234         var el = this.closeTpl.overwrite(td, {"text": text});
6235         var close = el.getElementsByTagName("div")[0];
6236         var inner = el.getElementsByTagName("em")[0];
6237         return {"el": el, "close": close, "inner": inner};
6238     } else {
6239         if(!this.tabTpl){
6240             this.tabTpl = new Roo.Template(
6241                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6242                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6243             );
6244         }
6245         var el = this.tabTpl.overwrite(td, {"text": text});
6246         var inner = el.getElementsByTagName("em")[0];
6247         return {"el": el, "inner": inner};
6248     }
6249 };/*
6250  * Based on:
6251  * Ext JS Library 1.1.1
6252  * Copyright(c) 2006-2007, Ext JS, LLC.
6253  *
6254  * Originally Released Under LGPL - original licence link has changed is not relivant.
6255  *
6256  * Fork - LGPL
6257  * <script type="text/javascript">
6258  */
6259
6260 /**
6261  * @class Roo.Button
6262  * @extends Roo.util.Observable
6263  * Simple Button class
6264  * @cfg {String} text The button text
6265  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6266  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6267  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6268  * @cfg {Object} scope The scope of the handler
6269  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6270  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6271  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6272  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6273  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6274  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6275    applies if enableToggle = true)
6276  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6277  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6278   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6279  * @constructor
6280  * Create a new button
6281  * @param {Object} config The config object
6282  */
6283 Roo.Button = function(renderTo, config)
6284 {
6285     if (!config) {
6286         config = renderTo;
6287         renderTo = config.renderTo || false;
6288     }
6289     
6290     Roo.apply(this, config);
6291     this.addEvents({
6292         /**
6293              * @event click
6294              * Fires when this button is clicked
6295              * @param {Button} this
6296              * @param {EventObject} e The click event
6297              */
6298             "click" : true,
6299         /**
6300              * @event toggle
6301              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6302              * @param {Button} this
6303              * @param {Boolean} pressed
6304              */
6305             "toggle" : true,
6306         /**
6307              * @event mouseover
6308              * Fires when the mouse hovers over the button
6309              * @param {Button} this
6310              * @param {Event} e The event object
6311              */
6312         'mouseover' : true,
6313         /**
6314              * @event mouseout
6315              * Fires when the mouse exits the button
6316              * @param {Button} this
6317              * @param {Event} e The event object
6318              */
6319         'mouseout': true,
6320          /**
6321              * @event render
6322              * Fires when the button is rendered
6323              * @param {Button} this
6324              */
6325         'render': true
6326     });
6327     if(this.menu){
6328         this.menu = Roo.menu.MenuMgr.get(this.menu);
6329     }
6330     // register listeners first!!  - so render can be captured..
6331     Roo.util.Observable.call(this);
6332     if(renderTo){
6333         this.render(renderTo);
6334     }
6335     
6336   
6337 };
6338
6339 Roo.extend(Roo.Button, Roo.util.Observable, {
6340     /**
6341      * 
6342      */
6343     
6344     /**
6345      * Read-only. True if this button is hidden
6346      * @type Boolean
6347      */
6348     hidden : false,
6349     /**
6350      * Read-only. True if this button is disabled
6351      * @type Boolean
6352      */
6353     disabled : false,
6354     /**
6355      * Read-only. True if this button is pressed (only if enableToggle = true)
6356      * @type Boolean
6357      */
6358     pressed : false,
6359
6360     /**
6361      * @cfg {Number} tabIndex 
6362      * The DOM tabIndex for this button (defaults to undefined)
6363      */
6364     tabIndex : undefined,
6365
6366     /**
6367      * @cfg {Boolean} enableToggle
6368      * True to enable pressed/not pressed toggling (defaults to false)
6369      */
6370     enableToggle: false,
6371     /**
6372      * @cfg {Roo.menu.Menu} menu
6373      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6374      */
6375     menu : undefined,
6376     /**
6377      * @cfg {String} menuAlign
6378      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6379      */
6380     menuAlign : "tl-bl?",
6381
6382     /**
6383      * @cfg {String} iconCls
6384      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6385      */
6386     iconCls : undefined,
6387     /**
6388      * @cfg {String} type
6389      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6390      */
6391     type : 'button',
6392
6393     // private
6394     menuClassTarget: 'tr',
6395
6396     /**
6397      * @cfg {String} clickEvent
6398      * The type of event to map to the button's event handler (defaults to 'click')
6399      */
6400     clickEvent : 'click',
6401
6402     /**
6403      * @cfg {Boolean} handleMouseEvents
6404      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6405      */
6406     handleMouseEvents : true,
6407
6408     /**
6409      * @cfg {String} tooltipType
6410      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6411      */
6412     tooltipType : 'qtip',
6413
6414     /**
6415      * @cfg {String} cls
6416      * A CSS class to apply to the button's main element.
6417      */
6418     
6419     /**
6420      * @cfg {Roo.Template} template (Optional)
6421      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6422      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6423      * require code modifications if required elements (e.g. a button) aren't present.
6424      */
6425
6426     // private
6427     render : function(renderTo){
6428         var btn;
6429         if(this.hideParent){
6430             this.parentEl = Roo.get(renderTo);
6431         }
6432         if(!this.dhconfig){
6433             if(!this.template){
6434                 if(!Roo.Button.buttonTemplate){
6435                     // hideous table template
6436                     Roo.Button.buttonTemplate = new Roo.Template(
6437                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6438                         '<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>',
6439                         "</tr></tbody></table>");
6440                 }
6441                 this.template = Roo.Button.buttonTemplate;
6442             }
6443             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6444             var btnEl = btn.child("button:first");
6445             btnEl.on('focus', this.onFocus, this);
6446             btnEl.on('blur', this.onBlur, this);
6447             if(this.cls){
6448                 btn.addClass(this.cls);
6449             }
6450             if(this.icon){
6451                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6452             }
6453             if(this.iconCls){
6454                 btnEl.addClass(this.iconCls);
6455                 if(!this.cls){
6456                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6457                 }
6458             }
6459             if(this.tabIndex !== undefined){
6460                 btnEl.dom.tabIndex = this.tabIndex;
6461             }
6462             if(this.tooltip){
6463                 if(typeof this.tooltip == 'object'){
6464                     Roo.QuickTips.tips(Roo.apply({
6465                           target: btnEl.id
6466                     }, this.tooltip));
6467                 } else {
6468                     btnEl.dom[this.tooltipType] = this.tooltip;
6469                 }
6470             }
6471         }else{
6472             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6473         }
6474         this.el = btn;
6475         if(this.id){
6476             this.el.dom.id = this.el.id = this.id;
6477         }
6478         if(this.menu){
6479             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6480             this.menu.on("show", this.onMenuShow, this);
6481             this.menu.on("hide", this.onMenuHide, this);
6482         }
6483         btn.addClass("x-btn");
6484         if(Roo.isIE && !Roo.isIE7){
6485             this.autoWidth.defer(1, this);
6486         }else{
6487             this.autoWidth();
6488         }
6489         if(this.handleMouseEvents){
6490             btn.on("mouseover", this.onMouseOver, this);
6491             btn.on("mouseout", this.onMouseOut, this);
6492             btn.on("mousedown", this.onMouseDown, this);
6493         }
6494         btn.on(this.clickEvent, this.onClick, this);
6495         //btn.on("mouseup", this.onMouseUp, this);
6496         if(this.hidden){
6497             this.hide();
6498         }
6499         if(this.disabled){
6500             this.disable();
6501         }
6502         Roo.ButtonToggleMgr.register(this);
6503         if(this.pressed){
6504             this.el.addClass("x-btn-pressed");
6505         }
6506         if(this.repeat){
6507             var repeater = new Roo.util.ClickRepeater(btn,
6508                 typeof this.repeat == "object" ? this.repeat : {}
6509             );
6510             repeater.on("click", this.onClick,  this);
6511         }
6512         
6513         this.fireEvent('render', this);
6514         
6515     },
6516     /**
6517      * Returns the button's underlying element
6518      * @return {Roo.Element} The element
6519      */
6520     getEl : function(){
6521         return this.el;  
6522     },
6523     
6524     /**
6525      * Destroys this Button and removes any listeners.
6526      */
6527     destroy : function(){
6528         Roo.ButtonToggleMgr.unregister(this);
6529         this.el.removeAllListeners();
6530         this.purgeListeners();
6531         this.el.remove();
6532     },
6533
6534     // private
6535     autoWidth : function(){
6536         if(this.el){
6537             this.el.setWidth("auto");
6538             if(Roo.isIE7 && Roo.isStrict){
6539                 var ib = this.el.child('button');
6540                 if(ib && ib.getWidth() > 20){
6541                     ib.clip();
6542                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6543                 }
6544             }
6545             if(this.minWidth){
6546                 if(this.hidden){
6547                     this.el.beginMeasure();
6548                 }
6549                 if(this.el.getWidth() < this.minWidth){
6550                     this.el.setWidth(this.minWidth);
6551                 }
6552                 if(this.hidden){
6553                     this.el.endMeasure();
6554                 }
6555             }
6556         }
6557     },
6558
6559     /**
6560      * Assigns this button's click handler
6561      * @param {Function} handler The function to call when the button is clicked
6562      * @param {Object} scope (optional) Scope for the function passed in
6563      */
6564     setHandler : function(handler, scope){
6565         this.handler = handler;
6566         this.scope = scope;  
6567     },
6568     
6569     /**
6570      * Sets this button's text
6571      * @param {String} text The button text
6572      */
6573     setText : function(text){
6574         this.text = text;
6575         if(this.el){
6576             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6577         }
6578         this.autoWidth();
6579     },
6580     
6581     /**
6582      * Gets the text for this button
6583      * @return {String} The button text
6584      */
6585     getText : function(){
6586         return this.text;  
6587     },
6588     
6589     /**
6590      * Show this button
6591      */
6592     show: function(){
6593         this.hidden = false;
6594         if(this.el){
6595             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6596         }
6597     },
6598     
6599     /**
6600      * Hide this button
6601      */
6602     hide: function(){
6603         this.hidden = true;
6604         if(this.el){
6605             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6606         }
6607     },
6608     
6609     /**
6610      * Convenience function for boolean show/hide
6611      * @param {Boolean} visible True to show, false to hide
6612      */
6613     setVisible: function(visible){
6614         if(visible) {
6615             this.show();
6616         }else{
6617             this.hide();
6618         }
6619     },
6620     
6621     /**
6622      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6623      * @param {Boolean} state (optional) Force a particular state
6624      */
6625     toggle : function(state){
6626         state = state === undefined ? !this.pressed : state;
6627         if(state != this.pressed){
6628             if(state){
6629                 this.el.addClass("x-btn-pressed");
6630                 this.pressed = true;
6631                 this.fireEvent("toggle", this, true);
6632             }else{
6633                 this.el.removeClass("x-btn-pressed");
6634                 this.pressed = false;
6635                 this.fireEvent("toggle", this, false);
6636             }
6637             if(this.toggleHandler){
6638                 this.toggleHandler.call(this.scope || this, this, state);
6639             }
6640         }
6641     },
6642     
6643     /**
6644      * Focus the button
6645      */
6646     focus : function(){
6647         this.el.child('button:first').focus();
6648     },
6649     
6650     /**
6651      * Disable this button
6652      */
6653     disable : function(){
6654         if(this.el){
6655             this.el.addClass("x-btn-disabled");
6656         }
6657         this.disabled = true;
6658     },
6659     
6660     /**
6661      * Enable this button
6662      */
6663     enable : function(){
6664         if(this.el){
6665             this.el.removeClass("x-btn-disabled");
6666         }
6667         this.disabled = false;
6668     },
6669
6670     /**
6671      * Convenience function for boolean enable/disable
6672      * @param {Boolean} enabled True to enable, false to disable
6673      */
6674     setDisabled : function(v){
6675         this[v !== true ? "enable" : "disable"]();
6676     },
6677
6678     // private
6679     onClick : function(e)
6680     {
6681         if(e){
6682             e.preventDefault();
6683         }
6684         if(e.button != 0){
6685             return;
6686         }
6687         if(!this.disabled){
6688             if(this.enableToggle){
6689                 this.toggle();
6690             }
6691             if(this.menu && !this.menu.isVisible()){
6692                 this.menu.show(this.el, this.menuAlign);
6693             }
6694             this.fireEvent("click", this, e);
6695             if(this.handler){
6696                 this.el.removeClass("x-btn-over");
6697                 this.handler.call(this.scope || this, this, e);
6698             }
6699         }
6700     },
6701     // private
6702     onMouseOver : function(e){
6703         if(!this.disabled){
6704             this.el.addClass("x-btn-over");
6705             this.fireEvent('mouseover', this, e);
6706         }
6707     },
6708     // private
6709     onMouseOut : function(e){
6710         if(!e.within(this.el,  true)){
6711             this.el.removeClass("x-btn-over");
6712             this.fireEvent('mouseout', this, e);
6713         }
6714     },
6715     // private
6716     onFocus : function(e){
6717         if(!this.disabled){
6718             this.el.addClass("x-btn-focus");
6719         }
6720     },
6721     // private
6722     onBlur : function(e){
6723         this.el.removeClass("x-btn-focus");
6724     },
6725     // private
6726     onMouseDown : function(e){
6727         if(!this.disabled && e.button == 0){
6728             this.el.addClass("x-btn-click");
6729             Roo.get(document).on('mouseup', this.onMouseUp, this);
6730         }
6731     },
6732     // private
6733     onMouseUp : function(e){
6734         if(e.button == 0){
6735             this.el.removeClass("x-btn-click");
6736             Roo.get(document).un('mouseup', this.onMouseUp, this);
6737         }
6738     },
6739     // private
6740     onMenuShow : function(e){
6741         this.el.addClass("x-btn-menu-active");
6742     },
6743     // private
6744     onMenuHide : function(e){
6745         this.el.removeClass("x-btn-menu-active");
6746     }   
6747 });
6748
6749 // Private utility class used by Button
6750 Roo.ButtonToggleMgr = function(){
6751    var groups = {};
6752    
6753    function toggleGroup(btn, state){
6754        if(state){
6755            var g = groups[btn.toggleGroup];
6756            for(var i = 0, l = g.length; i < l; i++){
6757                if(g[i] != btn){
6758                    g[i].toggle(false);
6759                }
6760            }
6761        }
6762    }
6763    
6764    return {
6765        register : function(btn){
6766            if(!btn.toggleGroup){
6767                return;
6768            }
6769            var g = groups[btn.toggleGroup];
6770            if(!g){
6771                g = groups[btn.toggleGroup] = [];
6772            }
6773            g.push(btn);
6774            btn.on("toggle", toggleGroup);
6775        },
6776        
6777        unregister : function(btn){
6778            if(!btn.toggleGroup){
6779                return;
6780            }
6781            var g = groups[btn.toggleGroup];
6782            if(g){
6783                g.remove(btn);
6784                btn.un("toggle", toggleGroup);
6785            }
6786        }
6787    };
6788 }();/*
6789  * Based on:
6790  * Ext JS Library 1.1.1
6791  * Copyright(c) 2006-2007, Ext JS, LLC.
6792  *
6793  * Originally Released Under LGPL - original licence link has changed is not relivant.
6794  *
6795  * Fork - LGPL
6796  * <script type="text/javascript">
6797  */
6798  
6799 /**
6800  * @class Roo.SplitButton
6801  * @extends Roo.Button
6802  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6803  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6804  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6805  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6806  * @cfg {String} arrowTooltip The title attribute of the arrow
6807  * @constructor
6808  * Create a new menu button
6809  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6810  * @param {Object} config The config object
6811  */
6812 Roo.SplitButton = function(renderTo, config){
6813     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6814     /**
6815      * @event arrowclick
6816      * Fires when this button's arrow is clicked
6817      * @param {SplitButton} this
6818      * @param {EventObject} e The click event
6819      */
6820     this.addEvents({"arrowclick":true});
6821 };
6822
6823 Roo.extend(Roo.SplitButton, Roo.Button, {
6824     render : function(renderTo){
6825         // this is one sweet looking template!
6826         var tpl = new Roo.Template(
6827             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6828             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6829             '<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>',
6830             "</tbody></table></td><td>",
6831             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6832             '<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>',
6833             "</tbody></table></td></tr></table>"
6834         );
6835         var btn = tpl.append(renderTo, [this.text, this.type], true);
6836         var btnEl = btn.child("button");
6837         if(this.cls){
6838             btn.addClass(this.cls);
6839         }
6840         if(this.icon){
6841             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6842         }
6843         if(this.iconCls){
6844             btnEl.addClass(this.iconCls);
6845             if(!this.cls){
6846                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6847             }
6848         }
6849         this.el = btn;
6850         if(this.handleMouseEvents){
6851             btn.on("mouseover", this.onMouseOver, this);
6852             btn.on("mouseout", this.onMouseOut, this);
6853             btn.on("mousedown", this.onMouseDown, this);
6854             btn.on("mouseup", this.onMouseUp, this);
6855         }
6856         btn.on(this.clickEvent, this.onClick, this);
6857         if(this.tooltip){
6858             if(typeof this.tooltip == 'object'){
6859                 Roo.QuickTips.tips(Roo.apply({
6860                       target: btnEl.id
6861                 }, this.tooltip));
6862             } else {
6863                 btnEl.dom[this.tooltipType] = this.tooltip;
6864             }
6865         }
6866         if(this.arrowTooltip){
6867             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6868         }
6869         if(this.hidden){
6870             this.hide();
6871         }
6872         if(this.disabled){
6873             this.disable();
6874         }
6875         if(this.pressed){
6876             this.el.addClass("x-btn-pressed");
6877         }
6878         if(Roo.isIE && !Roo.isIE7){
6879             this.autoWidth.defer(1, this);
6880         }else{
6881             this.autoWidth();
6882         }
6883         if(this.menu){
6884             this.menu.on("show", this.onMenuShow, this);
6885             this.menu.on("hide", this.onMenuHide, this);
6886         }
6887         this.fireEvent('render', this);
6888     },
6889
6890     // private
6891     autoWidth : function(){
6892         if(this.el){
6893             var tbl = this.el.child("table:first");
6894             var tbl2 = this.el.child("table:last");
6895             this.el.setWidth("auto");
6896             tbl.setWidth("auto");
6897             if(Roo.isIE7 && Roo.isStrict){
6898                 var ib = this.el.child('button:first');
6899                 if(ib && ib.getWidth() > 20){
6900                     ib.clip();
6901                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6902                 }
6903             }
6904             if(this.minWidth){
6905                 if(this.hidden){
6906                     this.el.beginMeasure();
6907                 }
6908                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6909                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6910                 }
6911                 if(this.hidden){
6912                     this.el.endMeasure();
6913                 }
6914             }
6915             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6916         } 
6917     },
6918     /**
6919      * Sets this button's click handler
6920      * @param {Function} handler The function to call when the button is clicked
6921      * @param {Object} scope (optional) Scope for the function passed above
6922      */
6923     setHandler : function(handler, scope){
6924         this.handler = handler;
6925         this.scope = scope;  
6926     },
6927     
6928     /**
6929      * Sets this button's arrow click handler
6930      * @param {Function} handler The function to call when the arrow is clicked
6931      * @param {Object} scope (optional) Scope for the function passed above
6932      */
6933     setArrowHandler : function(handler, scope){
6934         this.arrowHandler = handler;
6935         this.scope = scope;  
6936     },
6937     
6938     /**
6939      * Focus the button
6940      */
6941     focus : function(){
6942         if(this.el){
6943             this.el.child("button:first").focus();
6944         }
6945     },
6946
6947     // private
6948     onClick : function(e){
6949         e.preventDefault();
6950         if(!this.disabled){
6951             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6952                 if(this.menu && !this.menu.isVisible()){
6953                     this.menu.show(this.el, this.menuAlign);
6954                 }
6955                 this.fireEvent("arrowclick", this, e);
6956                 if(this.arrowHandler){
6957                     this.arrowHandler.call(this.scope || this, this, e);
6958                 }
6959             }else{
6960                 this.fireEvent("click", this, e);
6961                 if(this.handler){
6962                     this.handler.call(this.scope || this, this, e);
6963                 }
6964             }
6965         }
6966     },
6967     // private
6968     onMouseDown : function(e){
6969         if(!this.disabled){
6970             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6971         }
6972     },
6973     // private
6974     onMouseUp : function(e){
6975         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
6976     }   
6977 });
6978
6979
6980 // backwards compat
6981 Roo.MenuButton = Roo.SplitButton;/*
6982  * Based on:
6983  * Ext JS Library 1.1.1
6984  * Copyright(c) 2006-2007, Ext JS, LLC.
6985  *
6986  * Originally Released Under LGPL - original licence link has changed is not relivant.
6987  *
6988  * Fork - LGPL
6989  * <script type="text/javascript">
6990  */
6991
6992 /**
6993  * @class Roo.Toolbar
6994  * @children   Roo.Toolbar.Item Roo.form.Field
6995  * Basic Toolbar class.
6996  * @constructor
6997  * Creates a new Toolbar
6998  * @param {Object} container The config object
6999  */ 
7000 Roo.Toolbar = function(container, buttons, config)
7001 {
7002     /// old consturctor format still supported..
7003     if(container instanceof Array){ // omit the container for later rendering
7004         buttons = container;
7005         config = buttons;
7006         container = null;
7007     }
7008     if (typeof(container) == 'object' && container.xtype) {
7009         config = container;
7010         container = config.container;
7011         buttons = config.buttons || []; // not really - use items!!
7012     }
7013     var xitems = [];
7014     if (config && config.items) {
7015         xitems = config.items;
7016         delete config.items;
7017     }
7018     Roo.apply(this, config);
7019     this.buttons = buttons;
7020     
7021     if(container){
7022         this.render(container);
7023     }
7024     this.xitems = xitems;
7025     Roo.each(xitems, function(b) {
7026         this.add(b);
7027     }, this);
7028     
7029 };
7030
7031 Roo.Toolbar.prototype = {
7032     /**
7033      * @cfg {Array} items
7034      * array of button configs or elements to add (will be converted to a MixedCollection)
7035      */
7036     items: false,
7037     /**
7038      * @cfg {String/HTMLElement/Element} container
7039      * The id or element that will contain the toolbar
7040      */
7041     // private
7042     render : function(ct){
7043         this.el = Roo.get(ct);
7044         if(this.cls){
7045             this.el.addClass(this.cls);
7046         }
7047         // using a table allows for vertical alignment
7048         // 100% width is needed by Safari...
7049         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7050         this.tr = this.el.child("tr", true);
7051         var autoId = 0;
7052         this.items = new Roo.util.MixedCollection(false, function(o){
7053             return o.id || ("item" + (++autoId));
7054         });
7055         if(this.buttons){
7056             this.add.apply(this, this.buttons);
7057             delete this.buttons;
7058         }
7059     },
7060
7061     /**
7062      * Adds element(s) to the toolbar -- this function takes a variable number of 
7063      * arguments of mixed type and adds them to the toolbar.
7064      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7065      * <ul>
7066      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7067      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7068      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7069      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7070      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7071      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7072      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7073      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7074      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7075      * </ul>
7076      * @param {Mixed} arg2
7077      * @param {Mixed} etc.
7078      */
7079     add : function(){
7080         var a = arguments, l = a.length;
7081         for(var i = 0; i < l; i++){
7082             this._add(a[i]);
7083         }
7084     },
7085     // private..
7086     _add : function(el) {
7087         
7088         if (el.xtype) {
7089             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7090         }
7091         
7092         if (el.applyTo){ // some kind of form field
7093             return this.addField(el);
7094         } 
7095         if (el.render){ // some kind of Toolbar.Item
7096             return this.addItem(el);
7097         }
7098         if (typeof el == "string"){ // string
7099             if(el == "separator" || el == "-"){
7100                 return this.addSeparator();
7101             }
7102             if (el == " "){
7103                 return this.addSpacer();
7104             }
7105             if(el == "->"){
7106                 return this.addFill();
7107             }
7108             return this.addText(el);
7109             
7110         }
7111         if(el.tagName){ // element
7112             return this.addElement(el);
7113         }
7114         if(typeof el == "object"){ // must be button config?
7115             return this.addButton(el);
7116         }
7117         // and now what?!?!
7118         return false;
7119         
7120     },
7121     
7122     /**
7123      * Add an Xtype element
7124      * @param {Object} xtype Xtype Object
7125      * @return {Object} created Object
7126      */
7127     addxtype : function(e){
7128         return this.add(e);  
7129     },
7130     
7131     /**
7132      * Returns the Element for this toolbar.
7133      * @return {Roo.Element}
7134      */
7135     getEl : function(){
7136         return this.el;  
7137     },
7138     
7139     /**
7140      * Adds a separator
7141      * @return {Roo.Toolbar.Item} The separator item
7142      */
7143     addSeparator : function(){
7144         return this.addItem(new Roo.Toolbar.Separator());
7145     },
7146
7147     /**
7148      * Adds a spacer element
7149      * @return {Roo.Toolbar.Spacer} The spacer item
7150      */
7151     addSpacer : function(){
7152         return this.addItem(new Roo.Toolbar.Spacer());
7153     },
7154
7155     /**
7156      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7157      * @return {Roo.Toolbar.Fill} The fill item
7158      */
7159     addFill : function(){
7160         return this.addItem(new Roo.Toolbar.Fill());
7161     },
7162
7163     /**
7164      * Adds any standard HTML element to the toolbar
7165      * @param {String/HTMLElement/Element} el The element or id of the element to add
7166      * @return {Roo.Toolbar.Item} The element's item
7167      */
7168     addElement : function(el){
7169         return this.addItem(new Roo.Toolbar.Item(el));
7170     },
7171     /**
7172      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7173      * @type Roo.util.MixedCollection  
7174      */
7175     items : false,
7176      
7177     /**
7178      * Adds any Toolbar.Item or subclass
7179      * @param {Roo.Toolbar.Item} item
7180      * @return {Roo.Toolbar.Item} The item
7181      */
7182     addItem : function(item){
7183         var td = this.nextBlock();
7184         item.render(td);
7185         this.items.add(item);
7186         return item;
7187     },
7188     
7189     /**
7190      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7191      * @param {Object/Array} config A button config or array of configs
7192      * @return {Roo.Toolbar.Button/Array}
7193      */
7194     addButton : function(config){
7195         if(config instanceof Array){
7196             var buttons = [];
7197             for(var i = 0, len = config.length; i < len; i++) {
7198                 buttons.push(this.addButton(config[i]));
7199             }
7200             return buttons;
7201         }
7202         var b = config;
7203         if(!(config instanceof Roo.Toolbar.Button)){
7204             b = config.split ?
7205                 new Roo.Toolbar.SplitButton(config) :
7206                 new Roo.Toolbar.Button(config);
7207         }
7208         var td = this.nextBlock();
7209         b.render(td);
7210         this.items.add(b);
7211         return b;
7212     },
7213     
7214     /**
7215      * Adds text to the toolbar
7216      * @param {String} text The text to add
7217      * @return {Roo.Toolbar.Item} The element's item
7218      */
7219     addText : function(text){
7220         return this.addItem(new Roo.Toolbar.TextItem(text));
7221     },
7222     
7223     /**
7224      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7225      * @param {Number} index The index where the item is to be inserted
7226      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7227      * @return {Roo.Toolbar.Button/Item}
7228      */
7229     insertButton : function(index, item){
7230         if(item instanceof Array){
7231             var buttons = [];
7232             for(var i = 0, len = item.length; i < len; i++) {
7233                buttons.push(this.insertButton(index + i, item[i]));
7234             }
7235             return buttons;
7236         }
7237         if (!(item instanceof Roo.Toolbar.Button)){
7238            item = new Roo.Toolbar.Button(item);
7239         }
7240         var td = document.createElement("td");
7241         this.tr.insertBefore(td, this.tr.childNodes[index]);
7242         item.render(td);
7243         this.items.insert(index, item);
7244         return item;
7245     },
7246     
7247     /**
7248      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7249      * @param {Object} config
7250      * @return {Roo.Toolbar.Item} The element's item
7251      */
7252     addDom : function(config, returnEl){
7253         var td = this.nextBlock();
7254         Roo.DomHelper.overwrite(td, config);
7255         var ti = new Roo.Toolbar.Item(td.firstChild);
7256         ti.render(td);
7257         this.items.add(ti);
7258         return ti;
7259     },
7260
7261     /**
7262      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7263      * @type Roo.util.MixedCollection  
7264      */
7265     fields : false,
7266     
7267     /**
7268      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7269      * Note: the field should not have been rendered yet. For a field that has already been
7270      * rendered, use {@link #addElement}.
7271      * @param {Roo.form.Field} field
7272      * @return {Roo.ToolbarItem}
7273      */
7274      
7275       
7276     addField : function(field) {
7277         if (!this.fields) {
7278             var autoId = 0;
7279             this.fields = new Roo.util.MixedCollection(false, function(o){
7280                 return o.id || ("item" + (++autoId));
7281             });
7282
7283         }
7284         
7285         var td = this.nextBlock();
7286         field.render(td);
7287         var ti = new Roo.Toolbar.Item(td.firstChild);
7288         ti.render(td);
7289         this.items.add(ti);
7290         this.fields.add(field);
7291         return ti;
7292     },
7293     /**
7294      * Hide the toolbar
7295      * @method hide
7296      */
7297      
7298       
7299     hide : function()
7300     {
7301         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7302         this.el.child('div').hide();
7303     },
7304     /**
7305      * Show the toolbar
7306      * @method show
7307      */
7308     show : function()
7309     {
7310         this.el.child('div').show();
7311     },
7312       
7313     // private
7314     nextBlock : function(){
7315         var td = document.createElement("td");
7316         this.tr.appendChild(td);
7317         return td;
7318     },
7319
7320     // private
7321     destroy : function(){
7322         if(this.items){ // rendered?
7323             Roo.destroy.apply(Roo, this.items.items);
7324         }
7325         if(this.fields){ // rendered?
7326             Roo.destroy.apply(Roo, this.fields.items);
7327         }
7328         Roo.Element.uncache(this.el, this.tr);
7329     }
7330 };
7331
7332 /**
7333  * @class Roo.Toolbar.Item
7334  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7335  * @constructor
7336  * Creates a new Item
7337  * @param {HTMLElement} el 
7338  */
7339 Roo.Toolbar.Item = function(el){
7340     var cfg = {};
7341     if (typeof (el.xtype) != 'undefined') {
7342         cfg = el;
7343         el = cfg.el;
7344     }
7345     
7346     this.el = Roo.getDom(el);
7347     this.id = Roo.id(this.el);
7348     this.hidden = false;
7349     
7350     this.addEvents({
7351          /**
7352              * @event render
7353              * Fires when the button is rendered
7354              * @param {Button} this
7355              */
7356         'render': true
7357     });
7358     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7359 };
7360 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7361 //Roo.Toolbar.Item.prototype = {
7362     
7363     /**
7364      * Get this item's HTML Element
7365      * @return {HTMLElement}
7366      */
7367     getEl : function(){
7368        return this.el;  
7369     },
7370
7371     // private
7372     render : function(td){
7373         
7374          this.td = td;
7375         td.appendChild(this.el);
7376         
7377         this.fireEvent('render', this);
7378     },
7379     
7380     /**
7381      * Removes and destroys this item.
7382      */
7383     destroy : function(){
7384         this.td.parentNode.removeChild(this.td);
7385     },
7386     
7387     /**
7388      * Shows this item.
7389      */
7390     show: function(){
7391         this.hidden = false;
7392         this.td.style.display = "";
7393     },
7394     
7395     /**
7396      * Hides this item.
7397      */
7398     hide: function(){
7399         this.hidden = true;
7400         this.td.style.display = "none";
7401     },
7402     
7403     /**
7404      * Convenience function for boolean show/hide.
7405      * @param {Boolean} visible true to show/false to hide
7406      */
7407     setVisible: function(visible){
7408         if(visible) {
7409             this.show();
7410         }else{
7411             this.hide();
7412         }
7413     },
7414     
7415     /**
7416      * Try to focus this item.
7417      */
7418     focus : function(){
7419         Roo.fly(this.el).focus();
7420     },
7421     
7422     /**
7423      * Disables this item.
7424      */
7425     disable : function(){
7426         Roo.fly(this.td).addClass("x-item-disabled");
7427         this.disabled = true;
7428         this.el.disabled = true;
7429     },
7430     
7431     /**
7432      * Enables this item.
7433      */
7434     enable : function(){
7435         Roo.fly(this.td).removeClass("x-item-disabled");
7436         this.disabled = false;
7437         this.el.disabled = false;
7438     }
7439 });
7440
7441
7442 /**
7443  * @class Roo.Toolbar.Separator
7444  * @extends Roo.Toolbar.Item
7445  * A simple toolbar separator class
7446  * @constructor
7447  * Creates a new Separator
7448  */
7449 Roo.Toolbar.Separator = function(cfg){
7450     
7451     var s = document.createElement("span");
7452     s.className = "ytb-sep";
7453     if (cfg) {
7454         cfg.el = s;
7455     }
7456     
7457     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7458 };
7459 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7460     enable:Roo.emptyFn,
7461     disable:Roo.emptyFn,
7462     focus:Roo.emptyFn
7463 });
7464
7465 /**
7466  * @class Roo.Toolbar.Spacer
7467  * @extends Roo.Toolbar.Item
7468  * A simple element that adds extra horizontal space to a toolbar.
7469  * @constructor
7470  * Creates a new Spacer
7471  */
7472 Roo.Toolbar.Spacer = function(cfg){
7473     var s = document.createElement("div");
7474     s.className = "ytb-spacer";
7475     if (cfg) {
7476         cfg.el = s;
7477     }
7478     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7479 };
7480 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7481     enable:Roo.emptyFn,
7482     disable:Roo.emptyFn,
7483     focus:Roo.emptyFn
7484 });
7485
7486 /**
7487  * @class Roo.Toolbar.Fill
7488  * @extends Roo.Toolbar.Spacer
7489  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7490  * @constructor
7491  * Creates a new Spacer
7492  */
7493 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7494     // private
7495     render : function(td){
7496         td.style.width = '100%';
7497         Roo.Toolbar.Fill.superclass.render.call(this, td);
7498     }
7499 });
7500
7501 /**
7502  * @class Roo.Toolbar.TextItem
7503  * @extends Roo.Toolbar.Item
7504  * A simple class that renders text directly into a toolbar.
7505  * @constructor
7506  * Creates a new TextItem
7507  * @cfg {string} text 
7508  */
7509 Roo.Toolbar.TextItem = function(cfg){
7510     var  text = cfg || "";
7511     if (typeof(cfg) == 'object') {
7512         text = cfg.text || "";
7513     }  else {
7514         cfg = null;
7515     }
7516     var s = document.createElement("span");
7517     s.className = "ytb-text";
7518     s.innerHTML = text;
7519     if (cfg) {
7520         cfg.el  = s;
7521     }
7522     
7523     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7524 };
7525 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7526     
7527      
7528     enable:Roo.emptyFn,
7529     disable:Roo.emptyFn,
7530     focus:Roo.emptyFn
7531 });
7532
7533 /**
7534  * @class Roo.Toolbar.Button
7535  * @extends Roo.Button
7536  * A button that renders into a toolbar.
7537  * @constructor
7538  * Creates a new Button
7539  * @param {Object} config A standard {@link Roo.Button} config object
7540  */
7541 Roo.Toolbar.Button = function(config){
7542     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7543 };
7544 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7545 {
7546     
7547     
7548     render : function(td){
7549         this.td = td;
7550         Roo.Toolbar.Button.superclass.render.call(this, td);
7551     },
7552     
7553     /**
7554      * Removes and destroys this button
7555      */
7556     destroy : function(){
7557         Roo.Toolbar.Button.superclass.destroy.call(this);
7558         this.td.parentNode.removeChild(this.td);
7559     },
7560     
7561     /**
7562      * Shows this button
7563      */
7564     show: function(){
7565         this.hidden = false;
7566         this.td.style.display = "";
7567     },
7568     
7569     /**
7570      * Hides this button
7571      */
7572     hide: function(){
7573         this.hidden = true;
7574         this.td.style.display = "none";
7575     },
7576
7577     /**
7578      * Disables this item
7579      */
7580     disable : function(){
7581         Roo.fly(this.td).addClass("x-item-disabled");
7582         this.disabled = true;
7583     },
7584
7585     /**
7586      * Enables this item
7587      */
7588     enable : function(){
7589         Roo.fly(this.td).removeClass("x-item-disabled");
7590         this.disabled = false;
7591     }
7592 });
7593 // backwards compat
7594 Roo.ToolbarButton = Roo.Toolbar.Button;
7595
7596 /**
7597  * @class Roo.Toolbar.SplitButton
7598  * @extends Roo.SplitButton
7599  * A menu button that renders into a toolbar.
7600  * @constructor
7601  * Creates a new SplitButton
7602  * @param {Object} config A standard {@link Roo.SplitButton} config object
7603  */
7604 Roo.Toolbar.SplitButton = function(config){
7605     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7606 };
7607 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7608     render : function(td){
7609         this.td = td;
7610         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7611     },
7612     
7613     /**
7614      * Removes and destroys this button
7615      */
7616     destroy : function(){
7617         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7618         this.td.parentNode.removeChild(this.td);
7619     },
7620     
7621     /**
7622      * Shows this button
7623      */
7624     show: function(){
7625         this.hidden = false;
7626         this.td.style.display = "";
7627     },
7628     
7629     /**
7630      * Hides this button
7631      */
7632     hide: function(){
7633         this.hidden = true;
7634         this.td.style.display = "none";
7635     }
7636 });
7637
7638 // backwards compat
7639 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7640  * Based on:
7641  * Ext JS Library 1.1.1
7642  * Copyright(c) 2006-2007, Ext JS, LLC.
7643  *
7644  * Originally Released Under LGPL - original licence link has changed is not relivant.
7645  *
7646  * Fork - LGPL
7647  * <script type="text/javascript">
7648  */
7649  
7650 /**
7651  * @class Roo.PagingToolbar
7652  * @extends Roo.Toolbar
7653  * @children   Roo.Toolbar.Item Roo.form.Field
7654  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7655  * @constructor
7656  * Create a new PagingToolbar
7657  * @param {Object} config The config object
7658  */
7659 Roo.PagingToolbar = function(el, ds, config)
7660 {
7661     // old args format still supported... - xtype is prefered..
7662     if (typeof(el) == 'object' && el.xtype) {
7663         // created from xtype...
7664         config = el;
7665         ds = el.dataSource;
7666         el = config.container;
7667     }
7668     var items = [];
7669     if (config.items) {
7670         items = config.items;
7671         config.items = [];
7672     }
7673     
7674     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7675     this.ds = ds;
7676     this.cursor = 0;
7677     this.renderButtons(this.el);
7678     this.bind(ds);
7679     
7680     // supprot items array.
7681    
7682     Roo.each(items, function(e) {
7683         this.add(Roo.factory(e));
7684     },this);
7685     
7686 };
7687
7688 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7689     /**
7690      * @cfg {Roo.data.Store} dataSource
7691      * The underlying data store providing the paged data
7692      */
7693     /**
7694      * @cfg {String/HTMLElement/Element} container
7695      * container The id or element that will contain the toolbar
7696      */
7697     /**
7698      * @cfg {Boolean} displayInfo
7699      * True to display the displayMsg (defaults to false)
7700      */
7701     /**
7702      * @cfg {Number} pageSize
7703      * The number of records to display per page (defaults to 20)
7704      */
7705     pageSize: 20,
7706     /**
7707      * @cfg {String} displayMsg
7708      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7709      */
7710     displayMsg : 'Displaying {0} - {1} of {2}',
7711     /**
7712      * @cfg {String} emptyMsg
7713      * The message to display when no records are found (defaults to "No data to display")
7714      */
7715     emptyMsg : 'No data to display',
7716     /**
7717      * Customizable piece of the default paging text (defaults to "Page")
7718      * @type String
7719      */
7720     beforePageText : "Page",
7721     /**
7722      * Customizable piece of the default paging text (defaults to "of %0")
7723      * @type String
7724      */
7725     afterPageText : "of {0}",
7726     /**
7727      * Customizable piece of the default paging text (defaults to "First Page")
7728      * @type String
7729      */
7730     firstText : "First Page",
7731     /**
7732      * Customizable piece of the default paging text (defaults to "Previous Page")
7733      * @type String
7734      */
7735     prevText : "Previous Page",
7736     /**
7737      * Customizable piece of the default paging text (defaults to "Next Page")
7738      * @type String
7739      */
7740     nextText : "Next Page",
7741     /**
7742      * Customizable piece of the default paging text (defaults to "Last Page")
7743      * @type String
7744      */
7745     lastText : "Last Page",
7746     /**
7747      * Customizable piece of the default paging text (defaults to "Refresh")
7748      * @type String
7749      */
7750     refreshText : "Refresh",
7751
7752     // private
7753     renderButtons : function(el){
7754         Roo.PagingToolbar.superclass.render.call(this, el);
7755         this.first = this.addButton({
7756             tooltip: this.firstText,
7757             cls: "x-btn-icon x-grid-page-first",
7758             disabled: true,
7759             handler: this.onClick.createDelegate(this, ["first"])
7760         });
7761         this.prev = this.addButton({
7762             tooltip: this.prevText,
7763             cls: "x-btn-icon x-grid-page-prev",
7764             disabled: true,
7765             handler: this.onClick.createDelegate(this, ["prev"])
7766         });
7767         //this.addSeparator();
7768         this.add(this.beforePageText);
7769         this.field = Roo.get(this.addDom({
7770            tag: "input",
7771            type: "text",
7772            size: "3",
7773            value: "1",
7774            cls: "x-grid-page-number"
7775         }).el);
7776         this.field.on("keydown", this.onPagingKeydown, this);
7777         this.field.on("focus", function(){this.dom.select();});
7778         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7779         this.field.setHeight(18);
7780         //this.addSeparator();
7781         this.next = this.addButton({
7782             tooltip: this.nextText,
7783             cls: "x-btn-icon x-grid-page-next",
7784             disabled: true,
7785             handler: this.onClick.createDelegate(this, ["next"])
7786         });
7787         this.last = this.addButton({
7788             tooltip: this.lastText,
7789             cls: "x-btn-icon x-grid-page-last",
7790             disabled: true,
7791             handler: this.onClick.createDelegate(this, ["last"])
7792         });
7793         //this.addSeparator();
7794         this.loading = this.addButton({
7795             tooltip: this.refreshText,
7796             cls: "x-btn-icon x-grid-loading",
7797             handler: this.onClick.createDelegate(this, ["refresh"])
7798         });
7799
7800         if(this.displayInfo){
7801             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7802         }
7803     },
7804
7805     // private
7806     updateInfo : function(){
7807         if(this.displayEl){
7808             var count = this.ds.getCount();
7809             var msg = count == 0 ?
7810                 this.emptyMsg :
7811                 String.format(
7812                     this.displayMsg,
7813                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7814                 );
7815             this.displayEl.update(msg);
7816         }
7817     },
7818
7819     // private
7820     onLoad : function(ds, r, o){
7821        this.cursor = o.params ? o.params.start : 0;
7822        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7823
7824        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7825        this.field.dom.value = ap;
7826        this.first.setDisabled(ap == 1);
7827        this.prev.setDisabled(ap == 1);
7828        this.next.setDisabled(ap == ps);
7829        this.last.setDisabled(ap == ps);
7830        this.loading.enable();
7831        this.updateInfo();
7832     },
7833
7834     // private
7835     getPageData : function(){
7836         var total = this.ds.getTotalCount();
7837         return {
7838             total : total,
7839             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7840             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7841         };
7842     },
7843
7844     // private
7845     onLoadError : function(){
7846         this.loading.enable();
7847     },
7848
7849     // private
7850     onPagingKeydown : function(e){
7851         var k = e.getKey();
7852         var d = this.getPageData();
7853         if(k == e.RETURN){
7854             var v = this.field.dom.value, pageNum;
7855             if(!v || isNaN(pageNum = parseInt(v, 10))){
7856                 this.field.dom.value = d.activePage;
7857                 return;
7858             }
7859             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7860             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7861             e.stopEvent();
7862         }
7863         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))
7864         {
7865           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7866           this.field.dom.value = pageNum;
7867           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7868           e.stopEvent();
7869         }
7870         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7871         {
7872           var v = this.field.dom.value, pageNum; 
7873           var increment = (e.shiftKey) ? 10 : 1;
7874           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7875             increment *= -1;
7876           }
7877           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7878             this.field.dom.value = d.activePage;
7879             return;
7880           }
7881           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7882           {
7883             this.field.dom.value = parseInt(v, 10) + increment;
7884             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7885             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7886           }
7887           e.stopEvent();
7888         }
7889     },
7890
7891     // private
7892     beforeLoad : function(){
7893         if(this.loading){
7894             this.loading.disable();
7895         }
7896     },
7897
7898     // private
7899     onClick : function(which){
7900         var ds = this.ds;
7901         switch(which){
7902             case "first":
7903                 ds.load({params:{start: 0, limit: this.pageSize}});
7904             break;
7905             case "prev":
7906                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7907             break;
7908             case "next":
7909                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7910             break;
7911             case "last":
7912                 var total = ds.getTotalCount();
7913                 var extra = total % this.pageSize;
7914                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7915                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7916             break;
7917             case "refresh":
7918                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7919             break;
7920         }
7921     },
7922
7923     /**
7924      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7925      * @param {Roo.data.Store} store The data store to unbind
7926      */
7927     unbind : function(ds){
7928         ds.un("beforeload", this.beforeLoad, this);
7929         ds.un("load", this.onLoad, this);
7930         ds.un("loadexception", this.onLoadError, this);
7931         ds.un("remove", this.updateInfo, this);
7932         ds.un("add", this.updateInfo, this);
7933         this.ds = undefined;
7934     },
7935
7936     /**
7937      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7938      * @param {Roo.data.Store} store The data store to bind
7939      */
7940     bind : function(ds){
7941         ds.on("beforeload", this.beforeLoad, this);
7942         ds.on("load", this.onLoad, this);
7943         ds.on("loadexception", this.onLoadError, this);
7944         ds.on("remove", this.updateInfo, this);
7945         ds.on("add", this.updateInfo, this);
7946         this.ds = ds;
7947     }
7948 });/*
7949  * Based on:
7950  * Ext JS Library 1.1.1
7951  * Copyright(c) 2006-2007, Ext JS, LLC.
7952  *
7953  * Originally Released Under LGPL - original licence link has changed is not relivant.
7954  *
7955  * Fork - LGPL
7956  * <script type="text/javascript">
7957  */
7958
7959 /**
7960  * @class Roo.Resizable
7961  * @extends Roo.util.Observable
7962  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
7963  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
7964  * 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
7965  * the element will be wrapped for you automatically.</p>
7966  * <p>Here is the list of valid resize handles:</p>
7967  * <pre>
7968 Value   Description
7969 ------  -------------------
7970  'n'     north
7971  's'     south
7972  'e'     east
7973  'w'     west
7974  'nw'    northwest
7975  'sw'    southwest
7976  'se'    southeast
7977  'ne'    northeast
7978  'hd'    horizontal drag
7979  'all'   all
7980 </pre>
7981  * <p>Here's an example showing the creation of a typical Resizable:</p>
7982  * <pre><code>
7983 var resizer = new Roo.Resizable("element-id", {
7984     handles: 'all',
7985     minWidth: 200,
7986     minHeight: 100,
7987     maxWidth: 500,
7988     maxHeight: 400,
7989     pinned: true
7990 });
7991 resizer.on("resize", myHandler);
7992 </code></pre>
7993  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
7994  * resizer.east.setDisplayed(false);</p>
7995  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
7996  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
7997  * resize operation's new size (defaults to [0, 0])
7998  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
7999  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8000  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8001  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8002  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8003  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8004  * @cfg {Number} width The width of the element in pixels (defaults to null)
8005  * @cfg {Number} height The height of the element in pixels (defaults to null)
8006  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8007  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8008  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8009  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8010  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8011  * in favor of the handles config option (defaults to false)
8012  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8013  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8014  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8015  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8016  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8017  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8018  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8019  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8020  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8021  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8022  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8023  * @constructor
8024  * Create a new resizable component
8025  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8026  * @param {Object} config configuration options
8027   */
8028 Roo.Resizable = function(el, config)
8029 {
8030     this.el = Roo.get(el);
8031
8032     if(config && config.wrap){
8033         config.resizeChild = this.el;
8034         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8035         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8036         this.el.setStyle("overflow", "hidden");
8037         this.el.setPositioning(config.resizeChild.getPositioning());
8038         config.resizeChild.clearPositioning();
8039         if(!config.width || !config.height){
8040             var csize = config.resizeChild.getSize();
8041             this.el.setSize(csize.width, csize.height);
8042         }
8043         if(config.pinned && !config.adjustments){
8044             config.adjustments = "auto";
8045         }
8046     }
8047
8048     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8049     this.proxy.unselectable();
8050     this.proxy.enableDisplayMode('block');
8051
8052     Roo.apply(this, config);
8053
8054     if(this.pinned){
8055         this.disableTrackOver = true;
8056         this.el.addClass("x-resizable-pinned");
8057     }
8058     // if the element isn't positioned, make it relative
8059     var position = this.el.getStyle("position");
8060     if(position != "absolute" && position != "fixed"){
8061         this.el.setStyle("position", "relative");
8062     }
8063     if(!this.handles){ // no handles passed, must be legacy style
8064         this.handles = 's,e,se';
8065         if(this.multiDirectional){
8066             this.handles += ',n,w';
8067         }
8068     }
8069     if(this.handles == "all"){
8070         this.handles = "n s e w ne nw se sw";
8071     }
8072     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8073     var ps = Roo.Resizable.positions;
8074     for(var i = 0, len = hs.length; i < len; i++){
8075         if(hs[i] && ps[hs[i]]){
8076             var pos = ps[hs[i]];
8077             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8078         }
8079     }
8080     // legacy
8081     this.corner = this.southeast;
8082     
8083     // updateBox = the box can move..
8084     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8085         this.updateBox = true;
8086     }
8087
8088     this.activeHandle = null;
8089
8090     if(this.resizeChild){
8091         if(typeof this.resizeChild == "boolean"){
8092             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8093         }else{
8094             this.resizeChild = Roo.get(this.resizeChild, true);
8095         }
8096     }
8097     
8098     if(this.adjustments == "auto"){
8099         var rc = this.resizeChild;
8100         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8101         if(rc && (hw || hn)){
8102             rc.position("relative");
8103             rc.setLeft(hw ? hw.el.getWidth() : 0);
8104             rc.setTop(hn ? hn.el.getHeight() : 0);
8105         }
8106         this.adjustments = [
8107             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8108             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8109         ];
8110     }
8111
8112     if(this.draggable){
8113         this.dd = this.dynamic ?
8114             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8115         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8116     }
8117
8118     // public events
8119     this.addEvents({
8120         /**
8121          * @event beforeresize
8122          * Fired before resize is allowed. Set enabled to false to cancel resize.
8123          * @param {Roo.Resizable} this
8124          * @param {Roo.EventObject} e The mousedown event
8125          */
8126         "beforeresize" : true,
8127         /**
8128          * @event resizing
8129          * Fired a resizing.
8130          * @param {Roo.Resizable} this
8131          * @param {Number} x The new x position
8132          * @param {Number} y The new y position
8133          * @param {Number} w The new w width
8134          * @param {Number} h The new h hight
8135          * @param {Roo.EventObject} e The mouseup event
8136          */
8137         "resizing" : true,
8138         /**
8139          * @event resize
8140          * Fired after a resize.
8141          * @param {Roo.Resizable} this
8142          * @param {Number} width The new width
8143          * @param {Number} height The new height
8144          * @param {Roo.EventObject} e The mouseup event
8145          */
8146         "resize" : true
8147     });
8148
8149     if(this.width !== null && this.height !== null){
8150         this.resizeTo(this.width, this.height);
8151     }else{
8152         this.updateChildSize();
8153     }
8154     if(Roo.isIE){
8155         this.el.dom.style.zoom = 1;
8156     }
8157     Roo.Resizable.superclass.constructor.call(this);
8158 };
8159
8160 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8161         resizeChild : false,
8162         adjustments : [0, 0],
8163         minWidth : 5,
8164         minHeight : 5,
8165         maxWidth : 10000,
8166         maxHeight : 10000,
8167         enabled : true,
8168         animate : false,
8169         duration : .35,
8170         dynamic : false,
8171         handles : false,
8172         multiDirectional : false,
8173         disableTrackOver : false,
8174         easing : 'easeOutStrong',
8175         widthIncrement : 0,
8176         heightIncrement : 0,
8177         pinned : false,
8178         width : null,
8179         height : null,
8180         preserveRatio : false,
8181         transparent: false,
8182         minX: 0,
8183         minY: 0,
8184         draggable: false,
8185
8186         /**
8187          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8188          */
8189         constrainTo: undefined,
8190         /**
8191          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8192          */
8193         resizeRegion: undefined,
8194
8195
8196     /**
8197      * Perform a manual resize
8198      * @param {Number} width
8199      * @param {Number} height
8200      */
8201     resizeTo : function(width, height){
8202         this.el.setSize(width, height);
8203         this.updateChildSize();
8204         this.fireEvent("resize", this, width, height, null);
8205     },
8206
8207     // private
8208     startSizing : function(e, handle){
8209         this.fireEvent("beforeresize", this, e);
8210         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8211
8212             if(!this.overlay){
8213                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8214                 this.overlay.unselectable();
8215                 this.overlay.enableDisplayMode("block");
8216                 this.overlay.on("mousemove", this.onMouseMove, this);
8217                 this.overlay.on("mouseup", this.onMouseUp, this);
8218             }
8219             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8220
8221             this.resizing = true;
8222             this.startBox = this.el.getBox();
8223             this.startPoint = e.getXY();
8224             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8225                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8226
8227             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8228             this.overlay.show();
8229
8230             if(this.constrainTo) {
8231                 var ct = Roo.get(this.constrainTo);
8232                 this.resizeRegion = ct.getRegion().adjust(
8233                     ct.getFrameWidth('t'),
8234                     ct.getFrameWidth('l'),
8235                     -ct.getFrameWidth('b'),
8236                     -ct.getFrameWidth('r')
8237                 );
8238             }
8239
8240             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8241             this.proxy.show();
8242             this.proxy.setBox(this.startBox);
8243             if(!this.dynamic){
8244                 this.proxy.setStyle('visibility', 'visible');
8245             }
8246         }
8247     },
8248
8249     // private
8250     onMouseDown : function(handle, e){
8251         if(this.enabled){
8252             e.stopEvent();
8253             this.activeHandle = handle;
8254             this.startSizing(e, handle);
8255         }
8256     },
8257
8258     // private
8259     onMouseUp : function(e){
8260         var size = this.resizeElement();
8261         this.resizing = false;
8262         this.handleOut();
8263         this.overlay.hide();
8264         this.proxy.hide();
8265         this.fireEvent("resize", this, size.width, size.height, e);
8266     },
8267
8268     // private
8269     updateChildSize : function(){
8270         
8271         if(this.resizeChild){
8272             var el = this.el;
8273             var child = this.resizeChild;
8274             var adj = this.adjustments;
8275             if(el.dom.offsetWidth){
8276                 var b = el.getSize(true);
8277                 child.setSize(b.width+adj[0], b.height+adj[1]);
8278             }
8279             // Second call here for IE
8280             // The first call enables instant resizing and
8281             // the second call corrects scroll bars if they
8282             // exist
8283             if(Roo.isIE){
8284                 setTimeout(function(){
8285                     if(el.dom.offsetWidth){
8286                         var b = el.getSize(true);
8287                         child.setSize(b.width+adj[0], b.height+adj[1]);
8288                     }
8289                 }, 10);
8290             }
8291         }
8292     },
8293
8294     // private
8295     snap : function(value, inc, min){
8296         if(!inc || !value) {
8297             return value;
8298         }
8299         var newValue = value;
8300         var m = value % inc;
8301         if(m > 0){
8302             if(m > (inc/2)){
8303                 newValue = value + (inc-m);
8304             }else{
8305                 newValue = value - m;
8306             }
8307         }
8308         return Math.max(min, newValue);
8309     },
8310
8311     // private
8312     resizeElement : function(){
8313         var box = this.proxy.getBox();
8314         if(this.updateBox){
8315             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8316         }else{
8317             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8318         }
8319         this.updateChildSize();
8320         if(!this.dynamic){
8321             this.proxy.hide();
8322         }
8323         return box;
8324     },
8325
8326     // private
8327     constrain : function(v, diff, m, mx){
8328         if(v - diff < m){
8329             diff = v - m;
8330         }else if(v - diff > mx){
8331             diff = mx - v;
8332         }
8333         return diff;
8334     },
8335
8336     // private
8337     onMouseMove : function(e){
8338         
8339         if(this.enabled){
8340             try{// try catch so if something goes wrong the user doesn't get hung
8341
8342             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8343                 return;
8344             }
8345
8346             //var curXY = this.startPoint;
8347             var curSize = this.curSize || this.startBox;
8348             var x = this.startBox.x, y = this.startBox.y;
8349             var ox = x, oy = y;
8350             var w = curSize.width, h = curSize.height;
8351             var ow = w, oh = h;
8352             var mw = this.minWidth, mh = this.minHeight;
8353             var mxw = this.maxWidth, mxh = this.maxHeight;
8354             var wi = this.widthIncrement;
8355             var hi = this.heightIncrement;
8356
8357             var eventXY = e.getXY();
8358             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8359             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8360
8361             var pos = this.activeHandle.position;
8362
8363             switch(pos){
8364                 case "east":
8365                     w += diffX;
8366                     w = Math.min(Math.max(mw, w), mxw);
8367                     break;
8368              
8369                 case "south":
8370                     h += diffY;
8371                     h = Math.min(Math.max(mh, h), mxh);
8372                     break;
8373                 case "southeast":
8374                     w += diffX;
8375                     h += diffY;
8376                     w = Math.min(Math.max(mw, w), mxw);
8377                     h = Math.min(Math.max(mh, h), mxh);
8378                     break;
8379                 case "north":
8380                     diffY = this.constrain(h, diffY, mh, mxh);
8381                     y += diffY;
8382                     h -= diffY;
8383                     break;
8384                 case "hdrag":
8385                     
8386                     if (wi) {
8387                         var adiffX = Math.abs(diffX);
8388                         var sub = (adiffX % wi); // how much 
8389                         if (sub > (wi/2)) { // far enough to snap
8390                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8391                         } else {
8392                             // remove difference.. 
8393                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8394                         }
8395                     }
8396                     x += diffX;
8397                     x = Math.max(this.minX, x);
8398                     break;
8399                 case "west":
8400                     diffX = this.constrain(w, diffX, mw, mxw);
8401                     x += diffX;
8402                     w -= diffX;
8403                     break;
8404                 case "northeast":
8405                     w += diffX;
8406                     w = Math.min(Math.max(mw, w), mxw);
8407                     diffY = this.constrain(h, diffY, mh, mxh);
8408                     y += diffY;
8409                     h -= diffY;
8410                     break;
8411                 case "northwest":
8412                     diffX = this.constrain(w, diffX, mw, mxw);
8413                     diffY = this.constrain(h, diffY, mh, mxh);
8414                     y += diffY;
8415                     h -= diffY;
8416                     x += diffX;
8417                     w -= diffX;
8418                     break;
8419                case "southwest":
8420                     diffX = this.constrain(w, diffX, mw, mxw);
8421                     h += diffY;
8422                     h = Math.min(Math.max(mh, h), mxh);
8423                     x += diffX;
8424                     w -= diffX;
8425                     break;
8426             }
8427
8428             var sw = this.snap(w, wi, mw);
8429             var sh = this.snap(h, hi, mh);
8430             if(sw != w || sh != h){
8431                 switch(pos){
8432                     case "northeast":
8433                         y -= sh - h;
8434                     break;
8435                     case "north":
8436                         y -= sh - h;
8437                         break;
8438                     case "southwest":
8439                         x -= sw - w;
8440                     break;
8441                     case "west":
8442                         x -= sw - w;
8443                         break;
8444                     case "northwest":
8445                         x -= sw - w;
8446                         y -= sh - h;
8447                     break;
8448                 }
8449                 w = sw;
8450                 h = sh;
8451             }
8452
8453             if(this.preserveRatio){
8454                 switch(pos){
8455                     case "southeast":
8456                     case "east":
8457                         h = oh * (w/ow);
8458                         h = Math.min(Math.max(mh, h), mxh);
8459                         w = ow * (h/oh);
8460                        break;
8461                     case "south":
8462                         w = ow * (h/oh);
8463                         w = Math.min(Math.max(mw, w), mxw);
8464                         h = oh * (w/ow);
8465                         break;
8466                     case "northeast":
8467                         w = ow * (h/oh);
8468                         w = Math.min(Math.max(mw, w), mxw);
8469                         h = oh * (w/ow);
8470                     break;
8471                     case "north":
8472                         var tw = w;
8473                         w = ow * (h/oh);
8474                         w = Math.min(Math.max(mw, w), mxw);
8475                         h = oh * (w/ow);
8476                         x += (tw - w) / 2;
8477                         break;
8478                     case "southwest":
8479                         h = oh * (w/ow);
8480                         h = Math.min(Math.max(mh, h), mxh);
8481                         var tw = w;
8482                         w = ow * (h/oh);
8483                         x += tw - w;
8484                         break;
8485                     case "west":
8486                         var th = h;
8487                         h = oh * (w/ow);
8488                         h = Math.min(Math.max(mh, h), mxh);
8489                         y += (th - h) / 2;
8490                         var tw = w;
8491                         w = ow * (h/oh);
8492                         x += tw - w;
8493                        break;
8494                     case "northwest":
8495                         var tw = w;
8496                         var th = h;
8497                         h = oh * (w/ow);
8498                         h = Math.min(Math.max(mh, h), mxh);
8499                         w = ow * (h/oh);
8500                         y += th - h;
8501                         x += tw - w;
8502                        break;
8503
8504                 }
8505             }
8506             if (pos == 'hdrag') {
8507                 w = ow;
8508             }
8509             this.proxy.setBounds(x, y, w, h);
8510             if(this.dynamic){
8511                 this.resizeElement();
8512             }
8513             }catch(e){}
8514         }
8515         this.fireEvent("resizing", this, x, y, w, h, e);
8516     },
8517
8518     // private
8519     handleOver : function(){
8520         if(this.enabled){
8521             this.el.addClass("x-resizable-over");
8522         }
8523     },
8524
8525     // private
8526     handleOut : function(){
8527         if(!this.resizing){
8528             this.el.removeClass("x-resizable-over");
8529         }
8530     },
8531
8532     /**
8533      * Returns the element this component is bound to.
8534      * @return {Roo.Element}
8535      */
8536     getEl : function(){
8537         return this.el;
8538     },
8539
8540     /**
8541      * Returns the resizeChild element (or null).
8542      * @return {Roo.Element}
8543      */
8544     getResizeChild : function(){
8545         return this.resizeChild;
8546     },
8547     groupHandler : function()
8548     {
8549         
8550     },
8551     /**
8552      * Destroys this resizable. If the element was wrapped and
8553      * removeEl is not true then the element remains.
8554      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8555      */
8556     destroy : function(removeEl){
8557         this.proxy.remove();
8558         if(this.overlay){
8559             this.overlay.removeAllListeners();
8560             this.overlay.remove();
8561         }
8562         var ps = Roo.Resizable.positions;
8563         for(var k in ps){
8564             if(typeof ps[k] != "function" && this[ps[k]]){
8565                 var h = this[ps[k]];
8566                 h.el.removeAllListeners();
8567                 h.el.remove();
8568             }
8569         }
8570         if(removeEl){
8571             this.el.update("");
8572             this.el.remove();
8573         }
8574     }
8575 });
8576
8577 // private
8578 // hash to map config positions to true positions
8579 Roo.Resizable.positions = {
8580     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8581     hd: "hdrag"
8582 };
8583
8584 // private
8585 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8586     if(!this.tpl){
8587         // only initialize the template if resizable is used
8588         var tpl = Roo.DomHelper.createTemplate(
8589             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8590         );
8591         tpl.compile();
8592         Roo.Resizable.Handle.prototype.tpl = tpl;
8593     }
8594     this.position = pos;
8595     this.rz = rz;
8596     // show north drag fro topdra
8597     var handlepos = pos == 'hdrag' ? 'north' : pos;
8598     
8599     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8600     if (pos == 'hdrag') {
8601         this.el.setStyle('cursor', 'pointer');
8602     }
8603     this.el.unselectable();
8604     if(transparent){
8605         this.el.setOpacity(0);
8606     }
8607     this.el.on("mousedown", this.onMouseDown, this);
8608     if(!disableTrackOver){
8609         this.el.on("mouseover", this.onMouseOver, this);
8610         this.el.on("mouseout", this.onMouseOut, this);
8611     }
8612 };
8613
8614 // private
8615 Roo.Resizable.Handle.prototype = {
8616     afterResize : function(rz){
8617         Roo.log('after?');
8618         // do nothing
8619     },
8620     // private
8621     onMouseDown : function(e){
8622         this.rz.onMouseDown(this, e);
8623     },
8624     // private
8625     onMouseOver : function(e){
8626         this.rz.handleOver(this, e);
8627     },
8628     // private
8629     onMouseOut : function(e){
8630         this.rz.handleOut(this, e);
8631     }
8632 };/*
8633  * Based on:
8634  * Ext JS Library 1.1.1
8635  * Copyright(c) 2006-2007, Ext JS, LLC.
8636  *
8637  * Originally Released Under LGPL - original licence link has changed is not relivant.
8638  *
8639  * Fork - LGPL
8640  * <script type="text/javascript">
8641  */
8642
8643 /**
8644  * @class Roo.Editor
8645  * @extends Roo.Component
8646  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8647  * @constructor
8648  * Create a new Editor
8649  * @param {Roo.form.Field} field The Field object (or descendant)
8650  * @param {Object} config The config object
8651  */
8652 Roo.Editor = function(field, config){
8653     Roo.Editor.superclass.constructor.call(this, config);
8654     this.field = field;
8655     this.addEvents({
8656         /**
8657              * @event beforestartedit
8658              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8659              * false from the handler of this event.
8660              * @param {Editor} this
8661              * @param {Roo.Element} boundEl The underlying element bound to this editor
8662              * @param {Mixed} value The field value being set
8663              */
8664         "beforestartedit" : true,
8665         /**
8666              * @event startedit
8667              * Fires when this editor is displayed
8668              * @param {Roo.Element} boundEl The underlying element bound to this editor
8669              * @param {Mixed} value The starting field value
8670              */
8671         "startedit" : true,
8672         /**
8673              * @event beforecomplete
8674              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8675              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8676              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8677              * event will not fire since no edit actually occurred.
8678              * @param {Editor} this
8679              * @param {Mixed} value The current field value
8680              * @param {Mixed} startValue The original field value
8681              */
8682         "beforecomplete" : true,
8683         /**
8684              * @event complete
8685              * Fires after editing is complete and any changed value has been written to the underlying field.
8686              * @param {Editor} this
8687              * @param {Mixed} value The current field value
8688              * @param {Mixed} startValue The original field value
8689              */
8690         "complete" : true,
8691         /**
8692          * @event specialkey
8693          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8694          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8695          * @param {Roo.form.Field} this
8696          * @param {Roo.EventObject} e The event object
8697          */
8698         "specialkey" : true
8699     });
8700 };
8701
8702 Roo.extend(Roo.Editor, Roo.Component, {
8703     /**
8704      * @cfg {Boolean/String} autosize
8705      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8706      * or "height" to adopt the height only (defaults to false)
8707      */
8708     /**
8709      * @cfg {Boolean} revertInvalid
8710      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8711      * validation fails (defaults to true)
8712      */
8713     /**
8714      * @cfg {Boolean} ignoreNoChange
8715      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8716      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8717      * will never be ignored.
8718      */
8719     /**
8720      * @cfg {Boolean} hideEl
8721      * False to keep the bound element visible while the editor is displayed (defaults to true)
8722      */
8723     /**
8724      * @cfg {Mixed} value
8725      * The data value of the underlying field (defaults to "")
8726      */
8727     value : "",
8728     /**
8729      * @cfg {String} alignment
8730      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8731      */
8732     alignment: "c-c?",
8733     /**
8734      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8735      * for bottom-right shadow (defaults to "frame")
8736      */
8737     shadow : "frame",
8738     /**
8739      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8740      */
8741     constrain : false,
8742     /**
8743      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8744      */
8745     completeOnEnter : false,
8746     /**
8747      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8748      */
8749     cancelOnEsc : false,
8750     /**
8751      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8752      */
8753     updateEl : false,
8754
8755     // private
8756     onRender : function(ct, position){
8757         this.el = new Roo.Layer({
8758             shadow: this.shadow,
8759             cls: "x-editor",
8760             parentEl : ct,
8761             shim : this.shim,
8762             shadowOffset:4,
8763             id: this.id,
8764             constrain: this.constrain
8765         });
8766         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8767         if(this.field.msgTarget != 'title'){
8768             this.field.msgTarget = 'qtip';
8769         }
8770         this.field.render(this.el);
8771         if(Roo.isGecko){
8772             this.field.el.dom.setAttribute('autocomplete', 'off');
8773         }
8774         this.field.on("specialkey", this.onSpecialKey, this);
8775         if(this.swallowKeys){
8776             this.field.el.swallowEvent(['keydown','keypress']);
8777         }
8778         this.field.show();
8779         this.field.on("blur", this.onBlur, this);
8780         if(this.field.grow){
8781             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8782         }
8783     },
8784
8785     onSpecialKey : function(field, e)
8786     {
8787         //Roo.log('editor onSpecialKey');
8788         if(this.completeOnEnter && e.getKey() == e.ENTER){
8789             e.stopEvent();
8790             this.completeEdit();
8791             return;
8792         }
8793         // do not fire special key otherwise it might hide close the editor...
8794         if(e.getKey() == e.ENTER){    
8795             return;
8796         }
8797         if(this.cancelOnEsc && e.getKey() == e.ESC){
8798             this.cancelEdit();
8799             return;
8800         } 
8801         this.fireEvent('specialkey', field, e);
8802     
8803     },
8804
8805     /**
8806      * Starts the editing process and shows the editor.
8807      * @param {String/HTMLElement/Element} el The element to edit
8808      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8809       * to the innerHTML of el.
8810      */
8811     startEdit : function(el, value){
8812         if(this.editing){
8813             this.completeEdit();
8814         }
8815         this.boundEl = Roo.get(el);
8816         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8817         if(!this.rendered){
8818             this.render(this.parentEl || document.body);
8819         }
8820         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8821             return;
8822         }
8823         this.startValue = v;
8824         this.field.setValue(v);
8825         if(this.autoSize){
8826             var sz = this.boundEl.getSize();
8827             switch(this.autoSize){
8828                 case "width":
8829                 this.setSize(sz.width,  "");
8830                 break;
8831                 case "height":
8832                 this.setSize("",  sz.height);
8833                 break;
8834                 default:
8835                 this.setSize(sz.width,  sz.height);
8836             }
8837         }
8838         this.el.alignTo(this.boundEl, this.alignment);
8839         this.editing = true;
8840         if(Roo.QuickTips){
8841             Roo.QuickTips.disable();
8842         }
8843         this.show();
8844     },
8845
8846     /**
8847      * Sets the height and width of this editor.
8848      * @param {Number} width The new width
8849      * @param {Number} height The new height
8850      */
8851     setSize : function(w, h){
8852         this.field.setSize(w, h);
8853         if(this.el){
8854             this.el.sync();
8855         }
8856     },
8857
8858     /**
8859      * Realigns the editor to the bound field based on the current alignment config value.
8860      */
8861     realign : function(){
8862         this.el.alignTo(this.boundEl, this.alignment);
8863     },
8864
8865     /**
8866      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8867      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8868      */
8869     completeEdit : function(remainVisible){
8870         if(!this.editing){
8871             return;
8872         }
8873         var v = this.getValue();
8874         if(this.revertInvalid !== false && !this.field.isValid()){
8875             v = this.startValue;
8876             this.cancelEdit(true);
8877         }
8878         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8879             this.editing = false;
8880             this.hide();
8881             return;
8882         }
8883         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8884             this.editing = false;
8885             if(this.updateEl && this.boundEl){
8886                 this.boundEl.update(v);
8887             }
8888             if(remainVisible !== true){
8889                 this.hide();
8890             }
8891             this.fireEvent("complete", this, v, this.startValue);
8892         }
8893     },
8894
8895     // private
8896     onShow : function(){
8897         this.el.show();
8898         if(this.hideEl !== false){
8899             this.boundEl.hide();
8900         }
8901         this.field.show();
8902         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8903             this.fixIEFocus = true;
8904             this.deferredFocus.defer(50, this);
8905         }else{
8906             this.field.focus();
8907         }
8908         this.fireEvent("startedit", this.boundEl, this.startValue);
8909     },
8910
8911     deferredFocus : function(){
8912         if(this.editing){
8913             this.field.focus();
8914         }
8915     },
8916
8917     /**
8918      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8919      * reverted to the original starting value.
8920      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8921      * cancel (defaults to false)
8922      */
8923     cancelEdit : function(remainVisible){
8924         if(this.editing){
8925             this.setValue(this.startValue);
8926             if(remainVisible !== true){
8927                 this.hide();
8928             }
8929         }
8930     },
8931
8932     // private
8933     onBlur : function(){
8934         if(this.allowBlur !== true && this.editing){
8935             this.completeEdit();
8936         }
8937     },
8938
8939     // private
8940     onHide : function(){
8941         if(this.editing){
8942             this.completeEdit();
8943             return;
8944         }
8945         this.field.blur();
8946         if(this.field.collapse){
8947             this.field.collapse();
8948         }
8949         this.el.hide();
8950         if(this.hideEl !== false){
8951             this.boundEl.show();
8952         }
8953         if(Roo.QuickTips){
8954             Roo.QuickTips.enable();
8955         }
8956     },
8957
8958     /**
8959      * Sets the data value of the editor
8960      * @param {Mixed} value Any valid value supported by the underlying field
8961      */
8962     setValue : function(v){
8963         this.field.setValue(v);
8964     },
8965
8966     /**
8967      * Gets the data value of the editor
8968      * @return {Mixed} The data value
8969      */
8970     getValue : function(){
8971         return this.field.getValue();
8972     }
8973 });/*
8974  * Based on:
8975  * Ext JS Library 1.1.1
8976  * Copyright(c) 2006-2007, Ext JS, LLC.
8977  *
8978  * Originally Released Under LGPL - original licence link has changed is not relivant.
8979  *
8980  * Fork - LGPL
8981  * <script type="text/javascript">
8982  */
8983  
8984 /**
8985  * @class Roo.BasicDialog
8986  * @extends Roo.util.Observable
8987  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
8988  * <pre><code>
8989 var dlg = new Roo.BasicDialog("my-dlg", {
8990     height: 200,
8991     width: 300,
8992     minHeight: 100,
8993     minWidth: 150,
8994     modal: true,
8995     proxyDrag: true,
8996     shadow: true
8997 });
8998 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
8999 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9000 dlg.addButton('Cancel', dlg.hide, dlg);
9001 dlg.show();
9002 </code></pre>
9003   <b>A Dialog should always be a direct child of the body element.</b>
9004  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9005  * @cfg {String} title Default text to display in the title bar (defaults to null)
9006  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9007  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9008  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9009  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9010  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9011  * (defaults to null with no animation)
9012  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9013  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9014  * property for valid values (defaults to 'all')
9015  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9016  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9017  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9018  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9019  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9020  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9021  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9022  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9023  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9024  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9025  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9026  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9027  * draggable = true (defaults to false)
9028  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9029  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9030  * shadow (defaults to false)
9031  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9032  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9033  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9034  * @cfg {Array} buttons Array of buttons
9035  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9036  * @constructor
9037  * Create a new BasicDialog.
9038  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9039  * @param {Object} config Configuration options
9040  */
9041 Roo.BasicDialog = function(el, config){
9042     this.el = Roo.get(el);
9043     var dh = Roo.DomHelper;
9044     if(!this.el && config && config.autoCreate){
9045         if(typeof config.autoCreate == "object"){
9046             if(!config.autoCreate.id){
9047                 config.autoCreate.id = el;
9048             }
9049             this.el = dh.append(document.body,
9050                         config.autoCreate, true);
9051         }else{
9052             this.el = dh.append(document.body,
9053                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9054         }
9055     }
9056     el = this.el;
9057     el.setDisplayed(true);
9058     el.hide = this.hideAction;
9059     this.id = el.id;
9060     el.addClass("x-dlg");
9061
9062     Roo.apply(this, config);
9063
9064     this.proxy = el.createProxy("x-dlg-proxy");
9065     this.proxy.hide = this.hideAction;
9066     this.proxy.setOpacity(.5);
9067     this.proxy.hide();
9068
9069     if(config.width){
9070         el.setWidth(config.width);
9071     }
9072     if(config.height){
9073         el.setHeight(config.height);
9074     }
9075     this.size = el.getSize();
9076     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9077         this.xy = [config.x,config.y];
9078     }else{
9079         this.xy = el.getCenterXY(true);
9080     }
9081     /** The header element @type Roo.Element */
9082     this.header = el.child("> .x-dlg-hd");
9083     /** The body element @type Roo.Element */
9084     this.body = el.child("> .x-dlg-bd");
9085     /** The footer element @type Roo.Element */
9086     this.footer = el.child("> .x-dlg-ft");
9087
9088     if(!this.header){
9089         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9090     }
9091     if(!this.body){
9092         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9093     }
9094
9095     this.header.unselectable();
9096     if(this.title){
9097         this.header.update(this.title);
9098     }
9099     // this element allows the dialog to be focused for keyboard event
9100     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9101     this.focusEl.swallowEvent("click", true);
9102
9103     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9104
9105     // wrap the body and footer for special rendering
9106     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9107     if(this.footer){
9108         this.bwrap.dom.appendChild(this.footer.dom);
9109     }
9110
9111     this.bg = this.el.createChild({
9112         tag: "div", cls:"x-dlg-bg",
9113         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9114     });
9115     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9116
9117
9118     if(this.autoScroll !== false && !this.autoTabs){
9119         this.body.setStyle("overflow", "auto");
9120     }
9121
9122     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9123
9124     if(this.closable !== false){
9125         this.el.addClass("x-dlg-closable");
9126         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9127         this.close.on("click", this.closeClick, this);
9128         this.close.addClassOnOver("x-dlg-close-over");
9129     }
9130     if(this.collapsible !== false){
9131         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9132         this.collapseBtn.on("click", this.collapseClick, this);
9133         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9134         this.header.on("dblclick", this.collapseClick, this);
9135     }
9136     if(this.resizable !== false){
9137         this.el.addClass("x-dlg-resizable");
9138         this.resizer = new Roo.Resizable(el, {
9139             minWidth: this.minWidth || 80,
9140             minHeight:this.minHeight || 80,
9141             handles: this.resizeHandles || "all",
9142             pinned: true
9143         });
9144         this.resizer.on("beforeresize", this.beforeResize, this);
9145         this.resizer.on("resize", this.onResize, this);
9146     }
9147     if(this.draggable !== false){
9148         el.addClass("x-dlg-draggable");
9149         if (!this.proxyDrag) {
9150             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9151         }
9152         else {
9153             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9154         }
9155         dd.setHandleElId(this.header.id);
9156         dd.endDrag = this.endMove.createDelegate(this);
9157         dd.startDrag = this.startMove.createDelegate(this);
9158         dd.onDrag = this.onDrag.createDelegate(this);
9159         dd.scroll = false;
9160         this.dd = dd;
9161     }
9162     if(this.modal){
9163         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9164         this.mask.enableDisplayMode("block");
9165         this.mask.hide();
9166         this.el.addClass("x-dlg-modal");
9167     }
9168     if(this.shadow){
9169         this.shadow = new Roo.Shadow({
9170             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9171             offset : this.shadowOffset
9172         });
9173     }else{
9174         this.shadowOffset = 0;
9175     }
9176     if(Roo.useShims && this.shim !== false){
9177         this.shim = this.el.createShim();
9178         this.shim.hide = this.hideAction;
9179         this.shim.hide();
9180     }else{
9181         this.shim = false;
9182     }
9183     if(this.autoTabs){
9184         this.initTabs();
9185     }
9186     if (this.buttons) { 
9187         var bts= this.buttons;
9188         this.buttons = [];
9189         Roo.each(bts, function(b) {
9190             this.addButton(b);
9191         }, this);
9192     }
9193     
9194     
9195     this.addEvents({
9196         /**
9197          * @event keydown
9198          * Fires when a key is pressed
9199          * @param {Roo.BasicDialog} this
9200          * @param {Roo.EventObject} e
9201          */
9202         "keydown" : true,
9203         /**
9204          * @event move
9205          * Fires when this dialog is moved by the user.
9206          * @param {Roo.BasicDialog} this
9207          * @param {Number} x The new page X
9208          * @param {Number} y The new page Y
9209          */
9210         "move" : true,
9211         /**
9212          * @event resize
9213          * Fires when this dialog is resized by the user.
9214          * @param {Roo.BasicDialog} this
9215          * @param {Number} width The new width
9216          * @param {Number} height The new height
9217          */
9218         "resize" : true,
9219         /**
9220          * @event beforehide
9221          * Fires before this dialog is hidden.
9222          * @param {Roo.BasicDialog} this
9223          */
9224         "beforehide" : true,
9225         /**
9226          * @event hide
9227          * Fires when this dialog is hidden.
9228          * @param {Roo.BasicDialog} this
9229          */
9230         "hide" : true,
9231         /**
9232          * @event beforeshow
9233          * Fires before this dialog is shown.
9234          * @param {Roo.BasicDialog} this
9235          */
9236         "beforeshow" : true,
9237         /**
9238          * @event show
9239          * Fires when this dialog is shown.
9240          * @param {Roo.BasicDialog} this
9241          */
9242         "show" : true
9243     });
9244     el.on("keydown", this.onKeyDown, this);
9245     el.on("mousedown", this.toFront, this);
9246     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9247     this.el.hide();
9248     Roo.DialogManager.register(this);
9249     Roo.BasicDialog.superclass.constructor.call(this);
9250 };
9251
9252 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9253     shadowOffset: Roo.isIE ? 6 : 5,
9254     minHeight: 80,
9255     minWidth: 200,
9256     minButtonWidth: 75,
9257     defaultButton: null,
9258     buttonAlign: "right",
9259     tabTag: 'div',
9260     firstShow: true,
9261
9262     /**
9263      * Sets the dialog title text
9264      * @param {String} text The title text to display
9265      * @return {Roo.BasicDialog} this
9266      */
9267     setTitle : function(text){
9268         this.header.update(text);
9269         return this;
9270     },
9271
9272     // private
9273     closeClick : function(){
9274         this.hide();
9275     },
9276
9277     // private
9278     collapseClick : function(){
9279         this[this.collapsed ? "expand" : "collapse"]();
9280     },
9281
9282     /**
9283      * Collapses the dialog to its minimized state (only the title bar is visible).
9284      * Equivalent to the user clicking the collapse dialog button.
9285      */
9286     collapse : function(){
9287         if(!this.collapsed){
9288             this.collapsed = true;
9289             this.el.addClass("x-dlg-collapsed");
9290             this.restoreHeight = this.el.getHeight();
9291             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9292         }
9293     },
9294
9295     /**
9296      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9297      * clicking the expand dialog button.
9298      */
9299     expand : function(){
9300         if(this.collapsed){
9301             this.collapsed = false;
9302             this.el.removeClass("x-dlg-collapsed");
9303             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9304         }
9305     },
9306
9307     /**
9308      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9309      * @return {Roo.TabPanel} The tabs component
9310      */
9311     initTabs : function(){
9312         var tabs = this.getTabs();
9313         while(tabs.getTab(0)){
9314             tabs.removeTab(0);
9315         }
9316         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9317             var dom = el.dom;
9318             tabs.addTab(Roo.id(dom), dom.title);
9319             dom.title = "";
9320         });
9321         tabs.activate(0);
9322         return tabs;
9323     },
9324
9325     // private
9326     beforeResize : function(){
9327         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9328     },
9329
9330     // private
9331     onResize : function(){
9332         this.refreshSize();
9333         this.syncBodyHeight();
9334         this.adjustAssets();
9335         this.focus();
9336         this.fireEvent("resize", this, this.size.width, this.size.height);
9337     },
9338
9339     // private
9340     onKeyDown : function(e){
9341         if(this.isVisible()){
9342             this.fireEvent("keydown", this, e);
9343         }
9344     },
9345
9346     /**
9347      * Resizes the dialog.
9348      * @param {Number} width
9349      * @param {Number} height
9350      * @return {Roo.BasicDialog} this
9351      */
9352     resizeTo : function(width, height){
9353         this.el.setSize(width, height);
9354         this.size = {width: width, height: height};
9355         this.syncBodyHeight();
9356         if(this.fixedcenter){
9357             this.center();
9358         }
9359         if(this.isVisible()){
9360             this.constrainXY();
9361             this.adjustAssets();
9362         }
9363         this.fireEvent("resize", this, width, height);
9364         return this;
9365     },
9366
9367
9368     /**
9369      * Resizes the dialog to fit the specified content size.
9370      * @param {Number} width
9371      * @param {Number} height
9372      * @return {Roo.BasicDialog} this
9373      */
9374     setContentSize : function(w, h){
9375         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9376         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9377         //if(!this.el.isBorderBox()){
9378             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9379             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9380         //}
9381         if(this.tabs){
9382             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9383             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9384         }
9385         this.resizeTo(w, h);
9386         return this;
9387     },
9388
9389     /**
9390      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9391      * executed in response to a particular key being pressed while the dialog is active.
9392      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9393      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9394      * @param {Function} fn The function to call
9395      * @param {Object} scope (optional) The scope of the function
9396      * @return {Roo.BasicDialog} this
9397      */
9398     addKeyListener : function(key, fn, scope){
9399         var keyCode, shift, ctrl, alt;
9400         if(typeof key == "object" && !(key instanceof Array)){
9401             keyCode = key["key"];
9402             shift = key["shift"];
9403             ctrl = key["ctrl"];
9404             alt = key["alt"];
9405         }else{
9406             keyCode = key;
9407         }
9408         var handler = function(dlg, e){
9409             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9410                 var k = e.getKey();
9411                 if(keyCode instanceof Array){
9412                     for(var i = 0, len = keyCode.length; i < len; i++){
9413                         if(keyCode[i] == k){
9414                           fn.call(scope || window, dlg, k, e);
9415                           return;
9416                         }
9417                     }
9418                 }else{
9419                     if(k == keyCode){
9420                         fn.call(scope || window, dlg, k, e);
9421                     }
9422                 }
9423             }
9424         };
9425         this.on("keydown", handler);
9426         return this;
9427     },
9428
9429     /**
9430      * Returns the TabPanel component (creates it if it doesn't exist).
9431      * Note: If you wish to simply check for the existence of tabs without creating them,
9432      * check for a null 'tabs' property.
9433      * @return {Roo.TabPanel} The tabs component
9434      */
9435     getTabs : function(){
9436         if(!this.tabs){
9437             this.el.addClass("x-dlg-auto-tabs");
9438             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9439             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9440         }
9441         return this.tabs;
9442     },
9443
9444     /**
9445      * Adds a button to the footer section of the dialog.
9446      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9447      * object or a valid Roo.DomHelper element config
9448      * @param {Function} handler The function called when the button is clicked
9449      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9450      * @return {Roo.Button} The new button
9451      */
9452     addButton : function(config, handler, scope){
9453         var dh = Roo.DomHelper;
9454         if(!this.footer){
9455             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9456         }
9457         if(!this.btnContainer){
9458             var tb = this.footer.createChild({
9459
9460                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9461                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9462             }, null, true);
9463             this.btnContainer = tb.firstChild.firstChild.firstChild;
9464         }
9465         var bconfig = {
9466             handler: handler,
9467             scope: scope,
9468             minWidth: this.minButtonWidth,
9469             hideParent:true
9470         };
9471         if(typeof config == "string"){
9472             bconfig.text = config;
9473         }else{
9474             if(config.tag){
9475                 bconfig.dhconfig = config;
9476             }else{
9477                 Roo.apply(bconfig, config);
9478             }
9479         }
9480         var fc = false;
9481         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9482             bconfig.position = Math.max(0, bconfig.position);
9483             fc = this.btnContainer.childNodes[bconfig.position];
9484         }
9485          
9486         var btn = new Roo.Button(
9487             fc ? 
9488                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9489                 : this.btnContainer.appendChild(document.createElement("td")),
9490             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9491             bconfig
9492         );
9493         this.syncBodyHeight();
9494         if(!this.buttons){
9495             /**
9496              * Array of all the buttons that have been added to this dialog via addButton
9497              * @type Array
9498              */
9499             this.buttons = [];
9500         }
9501         this.buttons.push(btn);
9502         return btn;
9503     },
9504
9505     /**
9506      * Sets the default button to be focused when the dialog is displayed.
9507      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9508      * @return {Roo.BasicDialog} this
9509      */
9510     setDefaultButton : function(btn){
9511         this.defaultButton = btn;
9512         return this;
9513     },
9514
9515     // private
9516     getHeaderFooterHeight : function(safe){
9517         var height = 0;
9518         if(this.header){
9519            height += this.header.getHeight();
9520         }
9521         if(this.footer){
9522            var fm = this.footer.getMargins();
9523             height += (this.footer.getHeight()+fm.top+fm.bottom);
9524         }
9525         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9526         height += this.centerBg.getPadding("tb");
9527         return height;
9528     },
9529
9530     // private
9531     syncBodyHeight : function()
9532     {
9533         var bd = this.body, // the text
9534             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9535             bw = this.bwrap;
9536         var height = this.size.height - this.getHeaderFooterHeight(false);
9537         bd.setHeight(height-bd.getMargins("tb"));
9538         var hh = this.header.getHeight();
9539         var h = this.size.height-hh;
9540         cb.setHeight(h);
9541         
9542         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9543         bw.setHeight(h-cb.getPadding("tb"));
9544         
9545         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9546         bd.setWidth(bw.getWidth(true));
9547         if(this.tabs){
9548             this.tabs.syncHeight();
9549             if(Roo.isIE){
9550                 this.tabs.el.repaint();
9551             }
9552         }
9553     },
9554
9555     /**
9556      * Restores the previous state of the dialog if Roo.state is configured.
9557      * @return {Roo.BasicDialog} this
9558      */
9559     restoreState : function(){
9560         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9561         if(box && box.width){
9562             this.xy = [box.x, box.y];
9563             this.resizeTo(box.width, box.height);
9564         }
9565         return this;
9566     },
9567
9568     // private
9569     beforeShow : function(){
9570         this.expand();
9571         if(this.fixedcenter){
9572             this.xy = this.el.getCenterXY(true);
9573         }
9574         if(this.modal){
9575             Roo.get(document.body).addClass("x-body-masked");
9576             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9577             this.mask.show();
9578         }
9579         this.constrainXY();
9580     },
9581
9582     // private
9583     animShow : function(){
9584         var b = Roo.get(this.animateTarget).getBox();
9585         this.proxy.setSize(b.width, b.height);
9586         this.proxy.setLocation(b.x, b.y);
9587         this.proxy.show();
9588         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9589                     true, .35, this.showEl.createDelegate(this));
9590     },
9591
9592     /**
9593      * Shows the dialog.
9594      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9595      * @return {Roo.BasicDialog} this
9596      */
9597     show : function(animateTarget){
9598         if (this.fireEvent("beforeshow", this) === false){
9599             return;
9600         }
9601         if(this.syncHeightBeforeShow){
9602             this.syncBodyHeight();
9603         }else if(this.firstShow){
9604             this.firstShow = false;
9605             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9606         }
9607         this.animateTarget = animateTarget || this.animateTarget;
9608         if(!this.el.isVisible()){
9609             this.beforeShow();
9610             if(this.animateTarget && Roo.get(this.animateTarget)){
9611                 this.animShow();
9612             }else{
9613                 this.showEl();
9614             }
9615         }
9616         return this;
9617     },
9618
9619     // private
9620     showEl : function(){
9621         this.proxy.hide();
9622         this.el.setXY(this.xy);
9623         this.el.show();
9624         this.adjustAssets(true);
9625         this.toFront();
9626         this.focus();
9627         // IE peekaboo bug - fix found by Dave Fenwick
9628         if(Roo.isIE){
9629             this.el.repaint();
9630         }
9631         this.fireEvent("show", this);
9632     },
9633
9634     /**
9635      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9636      * dialog itself will receive focus.
9637      */
9638     focus : function(){
9639         if(this.defaultButton){
9640             this.defaultButton.focus();
9641         }else{
9642             this.focusEl.focus();
9643         }
9644     },
9645
9646     // private
9647     constrainXY : function(){
9648         if(this.constraintoviewport !== false){
9649             if(!this.viewSize){
9650                 if(this.container){
9651                     var s = this.container.getSize();
9652                     this.viewSize = [s.width, s.height];
9653                 }else{
9654                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9655                 }
9656             }
9657             var s = Roo.get(this.container||document).getScroll();
9658
9659             var x = this.xy[0], y = this.xy[1];
9660             var w = this.size.width, h = this.size.height;
9661             var vw = this.viewSize[0], vh = this.viewSize[1];
9662             // only move it if it needs it
9663             var moved = false;
9664             // first validate right/bottom
9665             if(x + w > vw+s.left){
9666                 x = vw - w;
9667                 moved = true;
9668             }
9669             if(y + h > vh+s.top){
9670                 y = vh - h;
9671                 moved = true;
9672             }
9673             // then make sure top/left isn't negative
9674             if(x < s.left){
9675                 x = s.left;
9676                 moved = true;
9677             }
9678             if(y < s.top){
9679                 y = s.top;
9680                 moved = true;
9681             }
9682             if(moved){
9683                 // cache xy
9684                 this.xy = [x, y];
9685                 if(this.isVisible()){
9686                     this.el.setLocation(x, y);
9687                     this.adjustAssets();
9688                 }
9689             }
9690         }
9691     },
9692
9693     // private
9694     onDrag : function(){
9695         if(!this.proxyDrag){
9696             this.xy = this.el.getXY();
9697             this.adjustAssets();
9698         }
9699     },
9700
9701     // private
9702     adjustAssets : function(doShow){
9703         var x = this.xy[0], y = this.xy[1];
9704         var w = this.size.width, h = this.size.height;
9705         if(doShow === true){
9706             if(this.shadow){
9707                 this.shadow.show(this.el);
9708             }
9709             if(this.shim){
9710                 this.shim.show();
9711             }
9712         }
9713         if(this.shadow && this.shadow.isVisible()){
9714             this.shadow.show(this.el);
9715         }
9716         if(this.shim && this.shim.isVisible()){
9717             this.shim.setBounds(x, y, w, h);
9718         }
9719     },
9720
9721     // private
9722     adjustViewport : function(w, h){
9723         if(!w || !h){
9724             w = Roo.lib.Dom.getViewWidth();
9725             h = Roo.lib.Dom.getViewHeight();
9726         }
9727         // cache the size
9728         this.viewSize = [w, h];
9729         if(this.modal && this.mask.isVisible()){
9730             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9731             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9732         }
9733         if(this.isVisible()){
9734             this.constrainXY();
9735         }
9736     },
9737
9738     /**
9739      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9740      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9741      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9742      */
9743     destroy : function(removeEl){
9744         if(this.isVisible()){
9745             this.animateTarget = null;
9746             this.hide();
9747         }
9748         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9749         if(this.tabs){
9750             this.tabs.destroy(removeEl);
9751         }
9752         Roo.destroy(
9753              this.shim,
9754              this.proxy,
9755              this.resizer,
9756              this.close,
9757              this.mask
9758         );
9759         if(this.dd){
9760             this.dd.unreg();
9761         }
9762         if(this.buttons){
9763            for(var i = 0, len = this.buttons.length; i < len; i++){
9764                this.buttons[i].destroy();
9765            }
9766         }
9767         this.el.removeAllListeners();
9768         if(removeEl === true){
9769             this.el.update("");
9770             this.el.remove();
9771         }
9772         Roo.DialogManager.unregister(this);
9773     },
9774
9775     // private
9776     startMove : function(){
9777         if(this.proxyDrag){
9778             this.proxy.show();
9779         }
9780         if(this.constraintoviewport !== false){
9781             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9782         }
9783     },
9784
9785     // private
9786     endMove : function(){
9787         if(!this.proxyDrag){
9788             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9789         }else{
9790             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9791             this.proxy.hide();
9792         }
9793         this.refreshSize();
9794         this.adjustAssets();
9795         this.focus();
9796         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9797     },
9798
9799     /**
9800      * Brings this dialog to the front of any other visible dialogs
9801      * @return {Roo.BasicDialog} this
9802      */
9803     toFront : function(){
9804         Roo.DialogManager.bringToFront(this);
9805         return this;
9806     },
9807
9808     /**
9809      * Sends this dialog to the back (under) of any other visible dialogs
9810      * @return {Roo.BasicDialog} this
9811      */
9812     toBack : function(){
9813         Roo.DialogManager.sendToBack(this);
9814         return this;
9815     },
9816
9817     /**
9818      * Centers this dialog in the viewport
9819      * @return {Roo.BasicDialog} this
9820      */
9821     center : function(){
9822         var xy = this.el.getCenterXY(true);
9823         this.moveTo(xy[0], xy[1]);
9824         return this;
9825     },
9826
9827     /**
9828      * Moves the dialog's top-left corner to the specified point
9829      * @param {Number} x
9830      * @param {Number} y
9831      * @return {Roo.BasicDialog} this
9832      */
9833     moveTo : function(x, y){
9834         this.xy = [x,y];
9835         if(this.isVisible()){
9836             this.el.setXY(this.xy);
9837             this.adjustAssets();
9838         }
9839         return this;
9840     },
9841
9842     /**
9843      * Aligns the dialog to the specified element
9844      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9845      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9846      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9847      * @return {Roo.BasicDialog} this
9848      */
9849     alignTo : function(element, position, offsets){
9850         this.xy = this.el.getAlignToXY(element, position, offsets);
9851         if(this.isVisible()){
9852             this.el.setXY(this.xy);
9853             this.adjustAssets();
9854         }
9855         return this;
9856     },
9857
9858     /**
9859      * Anchors an element to another element and realigns it when the window is resized.
9860      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9861      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9862      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9863      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9864      * is a number, it is used as the buffer delay (defaults to 50ms).
9865      * @return {Roo.BasicDialog} this
9866      */
9867     anchorTo : function(el, alignment, offsets, monitorScroll){
9868         var action = function(){
9869             this.alignTo(el, alignment, offsets);
9870         };
9871         Roo.EventManager.onWindowResize(action, this);
9872         var tm = typeof monitorScroll;
9873         if(tm != 'undefined'){
9874             Roo.EventManager.on(window, 'scroll', action, this,
9875                 {buffer: tm == 'number' ? monitorScroll : 50});
9876         }
9877         action.call(this);
9878         return this;
9879     },
9880
9881     /**
9882      * Returns true if the dialog is visible
9883      * @return {Boolean}
9884      */
9885     isVisible : function(){
9886         return this.el.isVisible();
9887     },
9888
9889     // private
9890     animHide : function(callback){
9891         var b = Roo.get(this.animateTarget).getBox();
9892         this.proxy.show();
9893         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9894         this.el.hide();
9895         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9896                     this.hideEl.createDelegate(this, [callback]));
9897     },
9898
9899     /**
9900      * Hides the dialog.
9901      * @param {Function} callback (optional) Function to call when the dialog is hidden
9902      * @return {Roo.BasicDialog} this
9903      */
9904     hide : function(callback){
9905         if (this.fireEvent("beforehide", this) === false){
9906             return;
9907         }
9908         if(this.shadow){
9909             this.shadow.hide();
9910         }
9911         if(this.shim) {
9912           this.shim.hide();
9913         }
9914         // sometimes animateTarget seems to get set.. causing problems...
9915         // this just double checks..
9916         if(this.animateTarget && Roo.get(this.animateTarget)) {
9917            this.animHide(callback);
9918         }else{
9919             this.el.hide();
9920             this.hideEl(callback);
9921         }
9922         return this;
9923     },
9924
9925     // private
9926     hideEl : function(callback){
9927         this.proxy.hide();
9928         if(this.modal){
9929             this.mask.hide();
9930             Roo.get(document.body).removeClass("x-body-masked");
9931         }
9932         this.fireEvent("hide", this);
9933         if(typeof callback == "function"){
9934             callback();
9935         }
9936     },
9937
9938     // private
9939     hideAction : function(){
9940         this.setLeft("-10000px");
9941         this.setTop("-10000px");
9942         this.setStyle("visibility", "hidden");
9943     },
9944
9945     // private
9946     refreshSize : function(){
9947         this.size = this.el.getSize();
9948         this.xy = this.el.getXY();
9949         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9950     },
9951
9952     // private
9953     // z-index is managed by the DialogManager and may be overwritten at any time
9954     setZIndex : function(index){
9955         if(this.modal){
9956             this.mask.setStyle("z-index", index);
9957         }
9958         if(this.shim){
9959             this.shim.setStyle("z-index", ++index);
9960         }
9961         if(this.shadow){
9962             this.shadow.setZIndex(++index);
9963         }
9964         this.el.setStyle("z-index", ++index);
9965         if(this.proxy){
9966             this.proxy.setStyle("z-index", ++index);
9967         }
9968         if(this.resizer){
9969             this.resizer.proxy.setStyle("z-index", ++index);
9970         }
9971
9972         this.lastZIndex = index;
9973     },
9974
9975     /**
9976      * Returns the element for this dialog
9977      * @return {Roo.Element} The underlying dialog Element
9978      */
9979     getEl : function(){
9980         return this.el;
9981     }
9982 });
9983
9984 /**
9985  * @class Roo.DialogManager
9986  * Provides global access to BasicDialogs that have been created and
9987  * support for z-indexing (layering) multiple open dialogs.
9988  */
9989 Roo.DialogManager = function(){
9990     var list = {};
9991     var accessList = [];
9992     var front = null;
9993
9994     // private
9995     var sortDialogs = function(d1, d2){
9996         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
9997     };
9998
9999     // private
10000     var orderDialogs = function(){
10001         accessList.sort(sortDialogs);
10002         var seed = Roo.DialogManager.zseed;
10003         for(var i = 0, len = accessList.length; i < len; i++){
10004             var dlg = accessList[i];
10005             if(dlg){
10006                 dlg.setZIndex(seed + (i*10));
10007             }
10008         }
10009     };
10010
10011     return {
10012         /**
10013          * The starting z-index for BasicDialogs (defaults to 9000)
10014          * @type Number The z-index value
10015          */
10016         zseed : 9000,
10017
10018         // private
10019         register : function(dlg){
10020             list[dlg.id] = dlg;
10021             accessList.push(dlg);
10022         },
10023
10024         // private
10025         unregister : function(dlg){
10026             delete list[dlg.id];
10027             var i=0;
10028             var len=0;
10029             if(!accessList.indexOf){
10030                 for(  i = 0, len = accessList.length; i < len; i++){
10031                     if(accessList[i] == dlg){
10032                         accessList.splice(i, 1);
10033                         return;
10034                     }
10035                 }
10036             }else{
10037                  i = accessList.indexOf(dlg);
10038                 if(i != -1){
10039                     accessList.splice(i, 1);
10040                 }
10041             }
10042         },
10043
10044         /**
10045          * Gets a registered dialog by id
10046          * @param {String/Object} id The id of the dialog or a dialog
10047          * @return {Roo.BasicDialog} this
10048          */
10049         get : function(id){
10050             return typeof id == "object" ? id : list[id];
10051         },
10052
10053         /**
10054          * Brings the specified dialog to the front
10055          * @param {String/Object} dlg The id of the dialog or a dialog
10056          * @return {Roo.BasicDialog} this
10057          */
10058         bringToFront : function(dlg){
10059             dlg = this.get(dlg);
10060             if(dlg != front){
10061                 front = dlg;
10062                 dlg._lastAccess = new Date().getTime();
10063                 orderDialogs();
10064             }
10065             return dlg;
10066         },
10067
10068         /**
10069          * Sends the specified dialog to the back
10070          * @param {String/Object} dlg The id of the dialog or a dialog
10071          * @return {Roo.BasicDialog} this
10072          */
10073         sendToBack : function(dlg){
10074             dlg = this.get(dlg);
10075             dlg._lastAccess = -(new Date().getTime());
10076             orderDialogs();
10077             return dlg;
10078         },
10079
10080         /**
10081          * Hides all dialogs
10082          */
10083         hideAll : function(){
10084             for(var id in list){
10085                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10086                     list[id].hide();
10087                 }
10088             }
10089         }
10090     };
10091 }();
10092
10093 /**
10094  * @class Roo.LayoutDialog
10095  * @extends Roo.BasicDialog
10096  * @children Roo.ContentPanel
10097  * Dialog which provides adjustments for working with a layout in a Dialog.
10098  * Add your necessary layout config options to the dialog's config.<br>
10099  * Example usage (including a nested layout):
10100  * <pre><code>
10101 if(!dialog){
10102     dialog = new Roo.LayoutDialog("download-dlg", {
10103         modal: true,
10104         width:600,
10105         height:450,
10106         shadow:true,
10107         minWidth:500,
10108         minHeight:350,
10109         autoTabs:true,
10110         proxyDrag:true,
10111         // layout config merges with the dialog config
10112         center:{
10113             tabPosition: "top",
10114             alwaysShowTabs: true
10115         }
10116     });
10117     dialog.addKeyListener(27, dialog.hide, dialog);
10118     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10119     dialog.addButton("Build It!", this.getDownload, this);
10120
10121     // we can even add nested layouts
10122     var innerLayout = new Roo.BorderLayout("dl-inner", {
10123         east: {
10124             initialSize: 200,
10125             autoScroll:true,
10126             split:true
10127         },
10128         center: {
10129             autoScroll:true
10130         }
10131     });
10132     innerLayout.beginUpdate();
10133     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10134     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10135     innerLayout.endUpdate(true);
10136
10137     var layout = dialog.getLayout();
10138     layout.beginUpdate();
10139     layout.add("center", new Roo.ContentPanel("standard-panel",
10140                         {title: "Download the Source", fitToFrame:true}));
10141     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10142                {title: "Build your own roo.js"}));
10143     layout.getRegion("center").showPanel(sp);
10144     layout.endUpdate();
10145 }
10146 </code></pre>
10147     * @constructor
10148     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10149     * @param {Object} config configuration options
10150   */
10151 Roo.LayoutDialog = function(el, cfg){
10152     
10153     var config=  cfg;
10154     if (typeof(cfg) == 'undefined') {
10155         config = Roo.apply({}, el);
10156         // not sure why we use documentElement here.. - it should always be body.
10157         // IE7 borks horribly if we use documentElement.
10158         // webkit also does not like documentElement - it creates a body element...
10159         el = Roo.get( document.body || document.documentElement ).createChild();
10160         //config.autoCreate = true;
10161     }
10162     
10163     
10164     config.autoTabs = false;
10165     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10166     this.body.setStyle({overflow:"hidden", position:"relative"});
10167     this.layout = new Roo.BorderLayout(this.body.dom, config);
10168     this.layout.monitorWindowResize = false;
10169     this.el.addClass("x-dlg-auto-layout");
10170     // fix case when center region overwrites center function
10171     this.center = Roo.BasicDialog.prototype.center;
10172     this.on("show", this.layout.layout, this.layout, true);
10173     if (config.items) {
10174         var xitems = config.items;
10175         delete config.items;
10176         Roo.each(xitems, this.addxtype, this);
10177     }
10178     
10179     
10180 };
10181 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10182     
10183     
10184     /**
10185      * @cfg {Roo.LayoutRegion} east  
10186      */
10187     /**
10188      * @cfg {Roo.LayoutRegion} west
10189      */
10190     /**
10191      * @cfg {Roo.LayoutRegion} south
10192      */
10193     /**
10194      * @cfg {Roo.LayoutRegion} north
10195      */
10196     /**
10197      * @cfg {Roo.LayoutRegion} center
10198      */
10199     /**
10200      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10201      */
10202     
10203     
10204     /**
10205      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10206      * @deprecated
10207      */
10208     endUpdate : function(){
10209         this.layout.endUpdate();
10210     },
10211
10212     /**
10213      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10214      *  @deprecated
10215      */
10216     beginUpdate : function(){
10217         this.layout.beginUpdate();
10218     },
10219
10220     /**
10221      * Get the BorderLayout for this dialog
10222      * @return {Roo.BorderLayout}
10223      */
10224     getLayout : function(){
10225         return this.layout;
10226     },
10227
10228     showEl : function(){
10229         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10230         if(Roo.isIE7){
10231             this.layout.layout();
10232         }
10233     },
10234
10235     // private
10236     // Use the syncHeightBeforeShow config option to control this automatically
10237     syncBodyHeight : function(){
10238         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10239         if(this.layout){this.layout.layout();}
10240     },
10241     
10242       /**
10243      * Add an xtype element (actually adds to the layout.)
10244      * @return {Object} xdata xtype object data.
10245      */
10246     
10247     addxtype : function(c) {
10248         return this.layout.addxtype(c);
10249     }
10250 });/*
10251  * Based on:
10252  * Ext JS Library 1.1.1
10253  * Copyright(c) 2006-2007, Ext JS, LLC.
10254  *
10255  * Originally Released Under LGPL - original licence link has changed is not relivant.
10256  *
10257  * Fork - LGPL
10258  * <script type="text/javascript">
10259  */
10260  
10261 /**
10262  * @class Roo.MessageBox
10263  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10264  * Example usage:
10265  *<pre><code>
10266 // Basic alert:
10267 Roo.Msg.alert('Status', 'Changes saved successfully.');
10268
10269 // Prompt for user data:
10270 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10271     if (btn == 'ok'){
10272         // process text value...
10273     }
10274 });
10275
10276 // Show a dialog using config options:
10277 Roo.Msg.show({
10278    title:'Save Changes?',
10279    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10280    buttons: Roo.Msg.YESNOCANCEL,
10281    fn: processResult,
10282    animEl: 'elId'
10283 });
10284 </code></pre>
10285  * @singleton
10286  */
10287 Roo.MessageBox = function(){
10288     var dlg, opt, mask, waitTimer;
10289     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10290     var buttons, activeTextEl, bwidth;
10291
10292     // private
10293     var handleButton = function(button){
10294         dlg.hide();
10295         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10296     };
10297
10298     // private
10299     var handleHide = function(){
10300         if(opt && opt.cls){
10301             dlg.el.removeClass(opt.cls);
10302         }
10303         if(waitTimer){
10304             Roo.TaskMgr.stop(waitTimer);
10305             waitTimer = null;
10306         }
10307     };
10308
10309     // private
10310     var updateButtons = function(b){
10311         var width = 0;
10312         if(!b){
10313             buttons["ok"].hide();
10314             buttons["cancel"].hide();
10315             buttons["yes"].hide();
10316             buttons["no"].hide();
10317             dlg.footer.dom.style.display = 'none';
10318             return width;
10319         }
10320         dlg.footer.dom.style.display = '';
10321         for(var k in buttons){
10322             if(typeof buttons[k] != "function"){
10323                 if(b[k]){
10324                     buttons[k].show();
10325                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10326                     width += buttons[k].el.getWidth()+15;
10327                 }else{
10328                     buttons[k].hide();
10329                 }
10330             }
10331         }
10332         return width;
10333     };
10334
10335     // private
10336     var handleEsc = function(d, k, e){
10337         if(opt && opt.closable !== false){
10338             dlg.hide();
10339         }
10340         if(e){
10341             e.stopEvent();
10342         }
10343     };
10344
10345     return {
10346         /**
10347          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10348          * @return {Roo.BasicDialog} The BasicDialog element
10349          */
10350         getDialog : function(){
10351            if(!dlg){
10352                 dlg = new Roo.BasicDialog("x-msg-box", {
10353                     autoCreate : true,
10354                     shadow: true,
10355                     draggable: true,
10356                     resizable:false,
10357                     constraintoviewport:false,
10358                     fixedcenter:true,
10359                     collapsible : false,
10360                     shim:true,
10361                     modal: true,
10362                     width:400, height:100,
10363                     buttonAlign:"center",
10364                     closeClick : function(){
10365                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10366                             handleButton("no");
10367                         }else{
10368                             handleButton("cancel");
10369                         }
10370                     }
10371                 });
10372                 dlg.on("hide", handleHide);
10373                 mask = dlg.mask;
10374                 dlg.addKeyListener(27, handleEsc);
10375                 buttons = {};
10376                 var bt = this.buttonText;
10377                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10378                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10379                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10380                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10381                 bodyEl = dlg.body.createChild({
10382
10383                     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>'
10384                 });
10385                 msgEl = bodyEl.dom.firstChild;
10386                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10387                 textboxEl.enableDisplayMode();
10388                 textboxEl.addKeyListener([10,13], function(){
10389                     if(dlg.isVisible() && opt && opt.buttons){
10390                         if(opt.buttons.ok){
10391                             handleButton("ok");
10392                         }else if(opt.buttons.yes){
10393                             handleButton("yes");
10394                         }
10395                     }
10396                 });
10397                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10398                 textareaEl.enableDisplayMode();
10399                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10400                 progressEl.enableDisplayMode();
10401                 var pf = progressEl.dom.firstChild;
10402                 if (pf) {
10403                     pp = Roo.get(pf.firstChild);
10404                     pp.setHeight(pf.offsetHeight);
10405                 }
10406                 
10407             }
10408             return dlg;
10409         },
10410
10411         /**
10412          * Updates the message box body text
10413          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10414          * the XHTML-compliant non-breaking space character '&amp;#160;')
10415          * @return {Roo.MessageBox} This message box
10416          */
10417         updateText : function(text){
10418             if(!dlg.isVisible() && !opt.width){
10419                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10420             }
10421             msgEl.innerHTML = text || '&#160;';
10422       
10423             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10424             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10425             var w = Math.max(
10426                     Math.min(opt.width || cw , this.maxWidth), 
10427                     Math.max(opt.minWidth || this.minWidth, bwidth)
10428             );
10429             if(opt.prompt){
10430                 activeTextEl.setWidth(w);
10431             }
10432             if(dlg.isVisible()){
10433                 dlg.fixedcenter = false;
10434             }
10435             // to big, make it scroll. = But as usual stupid IE does not support
10436             // !important..
10437             
10438             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10439                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10440                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10441             } else {
10442                 bodyEl.dom.style.height = '';
10443                 bodyEl.dom.style.overflowY = '';
10444             }
10445             if (cw > w) {
10446                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10447             } else {
10448                 bodyEl.dom.style.overflowX = '';
10449             }
10450             
10451             dlg.setContentSize(w, bodyEl.getHeight());
10452             if(dlg.isVisible()){
10453                 dlg.fixedcenter = true;
10454             }
10455             return this;
10456         },
10457
10458         /**
10459          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10460          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10461          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10462          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10463          * @return {Roo.MessageBox} This message box
10464          */
10465         updateProgress : function(value, text){
10466             if(text){
10467                 this.updateText(text);
10468             }
10469             if (pp) { // weird bug on my firefox - for some reason this is not defined
10470                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10471             }
10472             return this;
10473         },        
10474
10475         /**
10476          * Returns true if the message box is currently displayed
10477          * @return {Boolean} True if the message box is visible, else false
10478          */
10479         isVisible : function(){
10480             return dlg && dlg.isVisible();  
10481         },
10482
10483         /**
10484          * Hides the message box if it is displayed
10485          */
10486         hide : function(){
10487             if(this.isVisible()){
10488                 dlg.hide();
10489             }  
10490         },
10491
10492         /**
10493          * Displays a new message box, or reinitializes an existing message box, based on the config options
10494          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10495          * The following config object properties are supported:
10496          * <pre>
10497 Property    Type             Description
10498 ----------  ---------------  ------------------------------------------------------------------------------------
10499 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10500                                    closes (defaults to undefined)
10501 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10502                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10503 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10504                                    progress and wait dialogs will ignore this property and always hide the
10505                                    close button as they can only be closed programmatically.
10506 cls               String           A custom CSS class to apply to the message box element
10507 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10508                                    displayed (defaults to 75)
10509 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10510                                    function will be btn (the name of the button that was clicked, if applicable,
10511                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10512                                    Progress and wait dialogs will ignore this option since they do not respond to
10513                                    user actions and can only be closed programmatically, so any required function
10514                                    should be called by the same code after it closes the dialog.
10515 icon              String           A CSS class that provides a background image to be used as an icon for
10516                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10517 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10518 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10519 modal             Boolean          False to allow user interaction with the page while the message box is
10520                                    displayed (defaults to true)
10521 msg               String           A string that will replace the existing message box body text (defaults
10522                                    to the XHTML-compliant non-breaking space character '&#160;')
10523 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10524 progress          Boolean          True to display a progress bar (defaults to false)
10525 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10526 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10527 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10528 title             String           The title text
10529 value             String           The string value to set into the active textbox element if displayed
10530 wait              Boolean          True to display a progress bar (defaults to false)
10531 width             Number           The width of the dialog in pixels
10532 </pre>
10533          *
10534          * Example usage:
10535          * <pre><code>
10536 Roo.Msg.show({
10537    title: 'Address',
10538    msg: 'Please enter your address:',
10539    width: 300,
10540    buttons: Roo.MessageBox.OKCANCEL,
10541    multiline: true,
10542    fn: saveAddress,
10543    animEl: 'addAddressBtn'
10544 });
10545 </code></pre>
10546          * @param {Object} config Configuration options
10547          * @return {Roo.MessageBox} This message box
10548          */
10549         show : function(options)
10550         {
10551             
10552             // this causes nightmares if you show one dialog after another
10553             // especially on callbacks..
10554              
10555             if(this.isVisible()){
10556                 
10557                 this.hide();
10558                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10559                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10560                 Roo.log("New Dialog Message:" +  options.msg )
10561                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10562                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10563                 
10564             }
10565             var d = this.getDialog();
10566             opt = options;
10567             d.setTitle(opt.title || "&#160;");
10568             d.close.setDisplayed(opt.closable !== false);
10569             activeTextEl = textboxEl;
10570             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10571             if(opt.prompt){
10572                 if(opt.multiline){
10573                     textboxEl.hide();
10574                     textareaEl.show();
10575                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10576                         opt.multiline : this.defaultTextHeight);
10577                     activeTextEl = textareaEl;
10578                 }else{
10579                     textboxEl.show();
10580                     textareaEl.hide();
10581                 }
10582             }else{
10583                 textboxEl.hide();
10584                 textareaEl.hide();
10585             }
10586             progressEl.setDisplayed(opt.progress === true);
10587             this.updateProgress(0);
10588             activeTextEl.dom.value = opt.value || "";
10589             if(opt.prompt){
10590                 dlg.setDefaultButton(activeTextEl);
10591             }else{
10592                 var bs = opt.buttons;
10593                 var db = null;
10594                 if(bs && bs.ok){
10595                     db = buttons["ok"];
10596                 }else if(bs && bs.yes){
10597                     db = buttons["yes"];
10598                 }
10599                 dlg.setDefaultButton(db);
10600             }
10601             bwidth = updateButtons(opt.buttons);
10602             this.updateText(opt.msg);
10603             if(opt.cls){
10604                 d.el.addClass(opt.cls);
10605             }
10606             d.proxyDrag = opt.proxyDrag === true;
10607             d.modal = opt.modal !== false;
10608             d.mask = opt.modal !== false ? mask : false;
10609             if(!d.isVisible()){
10610                 // force it to the end of the z-index stack so it gets a cursor in FF
10611                 document.body.appendChild(dlg.el.dom);
10612                 d.animateTarget = null;
10613                 d.show(options.animEl);
10614             }
10615             return this;
10616         },
10617
10618         /**
10619          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10620          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10621          * and closing the message box when the process is complete.
10622          * @param {String} title The title bar text
10623          * @param {String} msg The message box body text
10624          * @return {Roo.MessageBox} This message box
10625          */
10626         progress : function(title, msg){
10627             this.show({
10628                 title : title,
10629                 msg : msg,
10630                 buttons: false,
10631                 progress:true,
10632                 closable:false,
10633                 minWidth: this.minProgressWidth,
10634                 modal : true
10635             });
10636             return this;
10637         },
10638
10639         /**
10640          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10641          * If a callback function is passed it will be called after the user clicks the button, and the
10642          * id of the button that was clicked will be passed as the only parameter to the callback
10643          * (could also be the top-right close button).
10644          * @param {String} title The title bar text
10645          * @param {String} msg The message box body text
10646          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10647          * @param {Object} scope (optional) The scope of the callback function
10648          * @return {Roo.MessageBox} This message box
10649          */
10650         alert : function(title, msg, fn, scope){
10651             this.show({
10652                 title : title,
10653                 msg : msg,
10654                 buttons: this.OK,
10655                 fn: fn,
10656                 scope : scope,
10657                 modal : true
10658             });
10659             return this;
10660         },
10661
10662         /**
10663          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10664          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10665          * You are responsible for closing the message box when the process is complete.
10666          * @param {String} msg The message box body text
10667          * @param {String} title (optional) The title bar text
10668          * @return {Roo.MessageBox} This message box
10669          */
10670         wait : function(msg, title){
10671             this.show({
10672                 title : title,
10673                 msg : msg,
10674                 buttons: false,
10675                 closable:false,
10676                 progress:true,
10677                 modal:true,
10678                 width:300,
10679                 wait:true
10680             });
10681             waitTimer = Roo.TaskMgr.start({
10682                 run: function(i){
10683                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10684                 },
10685                 interval: 1000
10686             });
10687             return this;
10688         },
10689
10690         /**
10691          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10692          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10693          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10694          * @param {String} title The title bar text
10695          * @param {String} msg The message box body text
10696          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10697          * @param {Object} scope (optional) The scope of the callback function
10698          * @return {Roo.MessageBox} This message box
10699          */
10700         confirm : function(title, msg, fn, scope){
10701             this.show({
10702                 title : title,
10703                 msg : msg,
10704                 buttons: this.YESNO,
10705                 fn: fn,
10706                 scope : scope,
10707                 modal : true
10708             });
10709             return this;
10710         },
10711
10712         /**
10713          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10714          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10715          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10716          * (could also be the top-right close button) and the text that was entered will be passed as the two
10717          * parameters to the callback.
10718          * @param {String} title The title bar text
10719          * @param {String} msg The message box body text
10720          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10721          * @param {Object} scope (optional) The scope of the callback function
10722          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10723          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10724          * @return {Roo.MessageBox} This message box
10725          */
10726         prompt : function(title, msg, fn, scope, multiline){
10727             this.show({
10728                 title : title,
10729                 msg : msg,
10730                 buttons: this.OKCANCEL,
10731                 fn: fn,
10732                 minWidth:250,
10733                 scope : scope,
10734                 prompt:true,
10735                 multiline: multiline,
10736                 modal : true
10737             });
10738             return this;
10739         },
10740
10741         /**
10742          * Button config that displays a single OK button
10743          * @type Object
10744          */
10745         OK : {ok:true},
10746         /**
10747          * Button config that displays Yes and No buttons
10748          * @type Object
10749          */
10750         YESNO : {yes:true, no:true},
10751         /**
10752          * Button config that displays OK and Cancel buttons
10753          * @type Object
10754          */
10755         OKCANCEL : {ok:true, cancel:true},
10756         /**
10757          * Button config that displays Yes, No and Cancel buttons
10758          * @type Object
10759          */
10760         YESNOCANCEL : {yes:true, no:true, cancel:true},
10761
10762         /**
10763          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10764          * @type Number
10765          */
10766         defaultTextHeight : 75,
10767         /**
10768          * The maximum width in pixels of the message box (defaults to 600)
10769          * @type Number
10770          */
10771         maxWidth : 600,
10772         /**
10773          * The minimum width in pixels of the message box (defaults to 100)
10774          * @type Number
10775          */
10776         minWidth : 100,
10777         /**
10778          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10779          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10780          * @type Number
10781          */
10782         minProgressWidth : 250,
10783         /**
10784          * An object containing the default button text strings that can be overriden for localized language support.
10785          * Supported properties are: ok, cancel, yes and no.
10786          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10787          * @type Object
10788          */
10789         buttonText : {
10790             ok : "OK",
10791             cancel : "Cancel",
10792             yes : "Yes",
10793             no : "No"
10794         }
10795     };
10796 }();
10797
10798 /**
10799  * Shorthand for {@link Roo.MessageBox}
10800  */
10801 Roo.Msg = Roo.MessageBox;/*
10802  * Based on:
10803  * Ext JS Library 1.1.1
10804  * Copyright(c) 2006-2007, Ext JS, LLC.
10805  *
10806  * Originally Released Under LGPL - original licence link has changed is not relivant.
10807  *
10808  * Fork - LGPL
10809  * <script type="text/javascript">
10810  */
10811 /**
10812  * @class Roo.QuickTips
10813  * Provides attractive and customizable tooltips for any element.
10814  * @singleton
10815  */
10816 Roo.QuickTips = function(){
10817     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10818     var ce, bd, xy, dd;
10819     var visible = false, disabled = true, inited = false;
10820     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10821     
10822     var onOver = function(e){
10823         if(disabled){
10824             return;
10825         }
10826         var t = e.getTarget();
10827         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10828             return;
10829         }
10830         if(ce && t == ce.el){
10831             clearTimeout(hideProc);
10832             return;
10833         }
10834         if(t && tagEls[t.id]){
10835             tagEls[t.id].el = t;
10836             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10837             return;
10838         }
10839         var ttp, et = Roo.fly(t);
10840         var ns = cfg.namespace;
10841         if(tm.interceptTitles && t.title){
10842             ttp = t.title;
10843             t.qtip = ttp;
10844             t.removeAttribute("title");
10845             e.preventDefault();
10846         }else{
10847             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10848         }
10849         if(ttp){
10850             showProc = show.defer(tm.showDelay, tm, [{
10851                 el: t, 
10852                 text: ttp.replace(/\\n/g,'<br/>'),
10853                 width: et.getAttributeNS(ns, cfg.width),
10854                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10855                 title: et.getAttributeNS(ns, cfg.title),
10856                     cls: et.getAttributeNS(ns, cfg.cls)
10857             }]);
10858         }
10859     };
10860     
10861     var onOut = function(e){
10862         clearTimeout(showProc);
10863         var t = e.getTarget();
10864         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10865             hideProc = setTimeout(hide, tm.hideDelay);
10866         }
10867     };
10868     
10869     var onMove = function(e){
10870         if(disabled){
10871             return;
10872         }
10873         xy = e.getXY();
10874         xy[1] += 18;
10875         if(tm.trackMouse && ce){
10876             el.setXY(xy);
10877         }
10878     };
10879     
10880     var onDown = function(e){
10881         clearTimeout(showProc);
10882         clearTimeout(hideProc);
10883         if(!e.within(el)){
10884             if(tm.hideOnClick){
10885                 hide();
10886                 tm.disable();
10887                 tm.enable.defer(100, tm);
10888             }
10889         }
10890     };
10891     
10892     var getPad = function(){
10893         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10894     };
10895
10896     var show = function(o){
10897         if(disabled){
10898             return;
10899         }
10900         clearTimeout(dismissProc);
10901         ce = o;
10902         if(removeCls){ // in case manually hidden
10903             el.removeClass(removeCls);
10904             removeCls = null;
10905         }
10906         if(ce.cls){
10907             el.addClass(ce.cls);
10908             removeCls = ce.cls;
10909         }
10910         if(ce.title){
10911             tipTitle.update(ce.title);
10912             tipTitle.show();
10913         }else{
10914             tipTitle.update('');
10915             tipTitle.hide();
10916         }
10917         el.dom.style.width  = tm.maxWidth+'px';
10918         //tipBody.dom.style.width = '';
10919         tipBodyText.update(o.text);
10920         var p = getPad(), w = ce.width;
10921         if(!w){
10922             var td = tipBodyText.dom;
10923             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10924             if(aw > tm.maxWidth){
10925                 w = tm.maxWidth;
10926             }else if(aw < tm.minWidth){
10927                 w = tm.minWidth;
10928             }else{
10929                 w = aw;
10930             }
10931         }
10932         //tipBody.setWidth(w);
10933         el.setWidth(parseInt(w, 10) + p);
10934         if(ce.autoHide === false){
10935             close.setDisplayed(true);
10936             if(dd){
10937                 dd.unlock();
10938             }
10939         }else{
10940             close.setDisplayed(false);
10941             if(dd){
10942                 dd.lock();
10943             }
10944         }
10945         if(xy){
10946             el.avoidY = xy[1]-18;
10947             el.setXY(xy);
10948         }
10949         if(tm.animate){
10950             el.setOpacity(.1);
10951             el.setStyle("visibility", "visible");
10952             el.fadeIn({callback: afterShow});
10953         }else{
10954             afterShow();
10955         }
10956     };
10957     
10958     var afterShow = function(){
10959         if(ce){
10960             el.show();
10961             esc.enable();
10962             if(tm.autoDismiss && ce.autoHide !== false){
10963                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10964             }
10965         }
10966     };
10967     
10968     var hide = function(noanim){
10969         clearTimeout(dismissProc);
10970         clearTimeout(hideProc);
10971         ce = null;
10972         if(el.isVisible()){
10973             esc.disable();
10974             if(noanim !== true && tm.animate){
10975                 el.fadeOut({callback: afterHide});
10976             }else{
10977                 afterHide();
10978             } 
10979         }
10980     };
10981     
10982     var afterHide = function(){
10983         el.hide();
10984         if(removeCls){
10985             el.removeClass(removeCls);
10986             removeCls = null;
10987         }
10988     };
10989     
10990     return {
10991         /**
10992         * @cfg {Number} minWidth
10993         * The minimum width of the quick tip (defaults to 40)
10994         */
10995        minWidth : 40,
10996         /**
10997         * @cfg {Number} maxWidth
10998         * The maximum width of the quick tip (defaults to 300)
10999         */
11000        maxWidth : 300,
11001         /**
11002         * @cfg {Boolean} interceptTitles
11003         * True to automatically use the element's DOM title value if available (defaults to false)
11004         */
11005        interceptTitles : false,
11006         /**
11007         * @cfg {Boolean} trackMouse
11008         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11009         */
11010        trackMouse : false,
11011         /**
11012         * @cfg {Boolean} hideOnClick
11013         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11014         */
11015        hideOnClick : true,
11016         /**
11017         * @cfg {Number} showDelay
11018         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11019         */
11020        showDelay : 500,
11021         /**
11022         * @cfg {Number} hideDelay
11023         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11024         */
11025        hideDelay : 200,
11026         /**
11027         * @cfg {Boolean} autoHide
11028         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11029         * Used in conjunction with hideDelay.
11030         */
11031        autoHide : true,
11032         /**
11033         * @cfg {Boolean}
11034         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11035         * (defaults to true).  Used in conjunction with autoDismissDelay.
11036         */
11037        autoDismiss : true,
11038         /**
11039         * @cfg {Number}
11040         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11041         */
11042        autoDismissDelay : 5000,
11043        /**
11044         * @cfg {Boolean} animate
11045         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11046         */
11047        animate : false,
11048
11049        /**
11050         * @cfg {String} title
11051         * Title text to display (defaults to '').  This can be any valid HTML markup.
11052         */
11053         title: '',
11054        /**
11055         * @cfg {String} text
11056         * Body text to display (defaults to '').  This can be any valid HTML markup.
11057         */
11058         text : '',
11059        /**
11060         * @cfg {String} cls
11061         * A CSS class to apply to the base quick tip element (defaults to '').
11062         */
11063         cls : '',
11064        /**
11065         * @cfg {Number} width
11066         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11067         * minWidth or maxWidth.
11068         */
11069         width : null,
11070
11071     /**
11072      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11073      * or display QuickTips in a page.
11074      */
11075        init : function(){
11076           tm = Roo.QuickTips;
11077           cfg = tm.tagConfig;
11078           if(!inited){
11079               if(!Roo.isReady){ // allow calling of init() before onReady
11080                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11081                   return;
11082               }
11083               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11084               el.fxDefaults = {stopFx: true};
11085               // maximum custom styling
11086               //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>');
11087               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>');              
11088               tipTitle = el.child('h3');
11089               tipTitle.enableDisplayMode("block");
11090               tipBody = el.child('div.x-tip-bd');
11091               tipBodyText = el.child('div.x-tip-bd-inner');
11092               //bdLeft = el.child('div.x-tip-bd-left');
11093               //bdRight = el.child('div.x-tip-bd-right');
11094               close = el.child('div.x-tip-close');
11095               close.enableDisplayMode("block");
11096               close.on("click", hide);
11097               var d = Roo.get(document);
11098               d.on("mousedown", onDown);
11099               d.on("mouseover", onOver);
11100               d.on("mouseout", onOut);
11101               d.on("mousemove", onMove);
11102               esc = d.addKeyListener(27, hide);
11103               esc.disable();
11104               if(Roo.dd.DD){
11105                   dd = el.initDD("default", null, {
11106                       onDrag : function(){
11107                           el.sync();  
11108                       }
11109                   });
11110                   dd.setHandleElId(tipTitle.id);
11111                   dd.lock();
11112               }
11113               inited = true;
11114           }
11115           this.enable(); 
11116        },
11117
11118     /**
11119      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11120      * are supported:
11121      * <pre>
11122 Property    Type                   Description
11123 ----------  ---------------------  ------------------------------------------------------------------------
11124 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11125      * </ul>
11126      * @param {Object} config The config object
11127      */
11128        register : function(config){
11129            var cs = config instanceof Array ? config : arguments;
11130            for(var i = 0, len = cs.length; i < len; i++) {
11131                var c = cs[i];
11132                var target = c.target;
11133                if(target){
11134                    if(target instanceof Array){
11135                        for(var j = 0, jlen = target.length; j < jlen; j++){
11136                            tagEls[target[j]] = c;
11137                        }
11138                    }else{
11139                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11140                    }
11141                }
11142            }
11143        },
11144
11145     /**
11146      * Removes this quick tip from its element and destroys it.
11147      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11148      */
11149        unregister : function(el){
11150            delete tagEls[Roo.id(el)];
11151        },
11152
11153     /**
11154      * Enable this quick tip.
11155      */
11156        enable : function(){
11157            if(inited && disabled){
11158                locks.pop();
11159                if(locks.length < 1){
11160                    disabled = false;
11161                }
11162            }
11163        },
11164
11165     /**
11166      * Disable this quick tip.
11167      */
11168        disable : function(){
11169           disabled = true;
11170           clearTimeout(showProc);
11171           clearTimeout(hideProc);
11172           clearTimeout(dismissProc);
11173           if(ce){
11174               hide(true);
11175           }
11176           locks.push(1);
11177        },
11178
11179     /**
11180      * Returns true if the quick tip is enabled, else false.
11181      */
11182        isEnabled : function(){
11183             return !disabled;
11184        },
11185
11186         // private
11187        tagConfig : {
11188            namespace : "roo", // was ext?? this may break..
11189            alt_namespace : "ext",
11190            attribute : "qtip",
11191            width : "width",
11192            target : "target",
11193            title : "qtitle",
11194            hide : "hide",
11195            cls : "qclass"
11196        }
11197    };
11198 }();
11199
11200 // backwards compat
11201 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11202  * Based on:
11203  * Ext JS Library 1.1.1
11204  * Copyright(c) 2006-2007, Ext JS, LLC.
11205  *
11206  * Originally Released Under LGPL - original licence link has changed is not relivant.
11207  *
11208  * Fork - LGPL
11209  * <script type="text/javascript">
11210  */
11211  
11212
11213 /**
11214  * @class Roo.tree.TreePanel
11215  * @extends Roo.data.Tree
11216  * @cfg {Roo.tree.TreeNode} root The root node
11217  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11218  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11219  * @cfg {Boolean} enableDD true to enable drag and drop
11220  * @cfg {Boolean} enableDrag true to enable just drag
11221  * @cfg {Boolean} enableDrop true to enable just drop
11222  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11223  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11224  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11225  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11226  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11227  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11228  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11229  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11230  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11231  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11232  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11233  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11234  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11235  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11236  * @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>
11237  * @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>
11238  * 
11239  * @constructor
11240  * @param {String/HTMLElement/Element} el The container element
11241  * @param {Object} config
11242  */
11243 Roo.tree.TreePanel = function(el, config){
11244     var root = false;
11245     var loader = false;
11246     if (config.root) {
11247         root = config.root;
11248         delete config.root;
11249     }
11250     if (config.loader) {
11251         loader = config.loader;
11252         delete config.loader;
11253     }
11254     
11255     Roo.apply(this, config);
11256     Roo.tree.TreePanel.superclass.constructor.call(this);
11257     this.el = Roo.get(el);
11258     this.el.addClass('x-tree');
11259     //console.log(root);
11260     if (root) {
11261         this.setRootNode( Roo.factory(root, Roo.tree));
11262     }
11263     if (loader) {
11264         this.loader = Roo.factory(loader, Roo.tree);
11265     }
11266    /**
11267     * Read-only. The id of the container element becomes this TreePanel's id.
11268     */
11269     this.id = this.el.id;
11270     this.addEvents({
11271         /**
11272         * @event beforeload
11273         * Fires before a node is loaded, return false to cancel
11274         * @param {Node} node The node being loaded
11275         */
11276         "beforeload" : true,
11277         /**
11278         * @event load
11279         * Fires when a node is loaded
11280         * @param {Node} node The node that was loaded
11281         */
11282         "load" : true,
11283         /**
11284         * @event textchange
11285         * Fires when the text for a node is changed
11286         * @param {Node} node The node
11287         * @param {String} text The new text
11288         * @param {String} oldText The old text
11289         */
11290         "textchange" : true,
11291         /**
11292         * @event beforeexpand
11293         * Fires before a node is expanded, return false to cancel.
11294         * @param {Node} node The node
11295         * @param {Boolean} deep
11296         * @param {Boolean} anim
11297         */
11298         "beforeexpand" : true,
11299         /**
11300         * @event beforecollapse
11301         * Fires before a node is collapsed, return false to cancel.
11302         * @param {Node} node The node
11303         * @param {Boolean} deep
11304         * @param {Boolean} anim
11305         */
11306         "beforecollapse" : true,
11307         /**
11308         * @event expand
11309         * Fires when a node is expanded
11310         * @param {Node} node The node
11311         */
11312         "expand" : true,
11313         /**
11314         * @event disabledchange
11315         * Fires when the disabled status of a node changes
11316         * @param {Node} node The node
11317         * @param {Boolean} disabled
11318         */
11319         "disabledchange" : true,
11320         /**
11321         * @event collapse
11322         * Fires when a node is collapsed
11323         * @param {Node} node The node
11324         */
11325         "collapse" : true,
11326         /**
11327         * @event beforeclick
11328         * Fires before click processing on a node. Return false to cancel the default action.
11329         * @param {Node} node The node
11330         * @param {Roo.EventObject} e The event object
11331         */
11332         "beforeclick":true,
11333         /**
11334         * @event checkchange
11335         * Fires when a node with a checkbox's checked property changes
11336         * @param {Node} this This node
11337         * @param {Boolean} checked
11338         */
11339         "checkchange":true,
11340         /**
11341         * @event click
11342         * Fires when a node is clicked
11343         * @param {Node} node The node
11344         * @param {Roo.EventObject} e The event object
11345         */
11346         "click":true,
11347         /**
11348         * @event dblclick
11349         * Fires when a node is double clicked
11350         * @param {Node} node The node
11351         * @param {Roo.EventObject} e The event object
11352         */
11353         "dblclick":true,
11354         /**
11355         * @event contextmenu
11356         * Fires when a node is right clicked
11357         * @param {Node} node The node
11358         * @param {Roo.EventObject} e The event object
11359         */
11360         "contextmenu":true,
11361         /**
11362         * @event beforechildrenrendered
11363         * Fires right before the child nodes for a node are rendered
11364         * @param {Node} node The node
11365         */
11366         "beforechildrenrendered":true,
11367         /**
11368         * @event startdrag
11369         * Fires when a node starts being dragged
11370         * @param {Roo.tree.TreePanel} this
11371         * @param {Roo.tree.TreeNode} node
11372         * @param {event} e The raw browser event
11373         */ 
11374        "startdrag" : true,
11375        /**
11376         * @event enddrag
11377         * Fires when a drag operation is complete
11378         * @param {Roo.tree.TreePanel} this
11379         * @param {Roo.tree.TreeNode} node
11380         * @param {event} e The raw browser event
11381         */
11382        "enddrag" : true,
11383        /**
11384         * @event dragdrop
11385         * Fires when a dragged node is dropped on a valid DD target
11386         * @param {Roo.tree.TreePanel} this
11387         * @param {Roo.tree.TreeNode} node
11388         * @param {DD} dd The dd it was dropped on
11389         * @param {event} e The raw browser event
11390         */
11391        "dragdrop" : true,
11392        /**
11393         * @event beforenodedrop
11394         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11395         * passed to handlers has the following properties:<br />
11396         * <ul style="padding:5px;padding-left:16px;">
11397         * <li>tree - The TreePanel</li>
11398         * <li>target - The node being targeted for the drop</li>
11399         * <li>data - The drag data from the drag source</li>
11400         * <li>point - The point of the drop - append, above or below</li>
11401         * <li>source - The drag source</li>
11402         * <li>rawEvent - Raw mouse event</li>
11403         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11404         * to be inserted by setting them on this object.</li>
11405         * <li>cancel - Set this to true to cancel the drop.</li>
11406         * </ul>
11407         * @param {Object} dropEvent
11408         */
11409        "beforenodedrop" : true,
11410        /**
11411         * @event nodedrop
11412         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11413         * passed to handlers has the following properties:<br />
11414         * <ul style="padding:5px;padding-left:16px;">
11415         * <li>tree - The TreePanel</li>
11416         * <li>target - The node being targeted for the drop</li>
11417         * <li>data - The drag data from the drag source</li>
11418         * <li>point - The point of the drop - append, above or below</li>
11419         * <li>source - The drag source</li>
11420         * <li>rawEvent - Raw mouse event</li>
11421         * <li>dropNode - Dropped node(s).</li>
11422         * </ul>
11423         * @param {Object} dropEvent
11424         */
11425        "nodedrop" : true,
11426         /**
11427         * @event nodedragover
11428         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11429         * passed to handlers has the following properties:<br />
11430         * <ul style="padding:5px;padding-left:16px;">
11431         * <li>tree - The TreePanel</li>
11432         * <li>target - The node being targeted for the drop</li>
11433         * <li>data - The drag data from the drag source</li>
11434         * <li>point - The point of the drop - append, above or below</li>
11435         * <li>source - The drag source</li>
11436         * <li>rawEvent - Raw mouse event</li>
11437         * <li>dropNode - Drop node(s) provided by the source.</li>
11438         * <li>cancel - Set this to true to signal drop not allowed.</li>
11439         * </ul>
11440         * @param {Object} dragOverEvent
11441         */
11442        "nodedragover" : true,
11443        /**
11444         * @event appendnode
11445         * Fires when append node to the tree
11446         * @param {Roo.tree.TreePanel} this
11447         * @param {Roo.tree.TreeNode} node
11448         * @param {Number} index The index of the newly appended node
11449         */
11450        "appendnode" : true
11451         
11452     });
11453     if(this.singleExpand){
11454        this.on("beforeexpand", this.restrictExpand, this);
11455     }
11456     if (this.editor) {
11457         this.editor.tree = this;
11458         this.editor = Roo.factory(this.editor, Roo.tree);
11459     }
11460     
11461     if (this.selModel) {
11462         this.selModel = Roo.factory(this.selModel, Roo.tree);
11463     }
11464    
11465 };
11466 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11467     rootVisible : true,
11468     animate: Roo.enableFx,
11469     lines : true,
11470     enableDD : false,
11471     hlDrop : Roo.enableFx,
11472   
11473     renderer: false,
11474     
11475     rendererTip: false,
11476     // private
11477     restrictExpand : function(node){
11478         var p = node.parentNode;
11479         if(p){
11480             if(p.expandedChild && p.expandedChild.parentNode == p){
11481                 p.expandedChild.collapse();
11482             }
11483             p.expandedChild = node;
11484         }
11485     },
11486
11487     // private override
11488     setRootNode : function(node){
11489         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11490         if(!this.rootVisible){
11491             node.ui = new Roo.tree.RootTreeNodeUI(node);
11492         }
11493         return node;
11494     },
11495
11496     /**
11497      * Returns the container element for this TreePanel
11498      */
11499     getEl : function(){
11500         return this.el;
11501     },
11502
11503     /**
11504      * Returns the default TreeLoader for this TreePanel
11505      */
11506     getLoader : function(){
11507         return this.loader;
11508     },
11509
11510     /**
11511      * Expand all nodes
11512      */
11513     expandAll : function(){
11514         this.root.expand(true);
11515     },
11516
11517     /**
11518      * Collapse all nodes
11519      */
11520     collapseAll : function(){
11521         this.root.collapse(true);
11522     },
11523
11524     /**
11525      * Returns the selection model used by this TreePanel
11526      */
11527     getSelectionModel : function(){
11528         if(!this.selModel){
11529             this.selModel = new Roo.tree.DefaultSelectionModel();
11530         }
11531         return this.selModel;
11532     },
11533
11534     /**
11535      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11536      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11537      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11538      * @return {Array}
11539      */
11540     getChecked : function(a, startNode){
11541         startNode = startNode || this.root;
11542         var r = [];
11543         var f = function(){
11544             if(this.attributes.checked){
11545                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11546             }
11547         }
11548         startNode.cascade(f);
11549         return r;
11550     },
11551
11552     /**
11553      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11554      * @param {String} path
11555      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11556      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11557      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11558      */
11559     expandPath : function(path, attr, callback){
11560         attr = attr || "id";
11561         var keys = path.split(this.pathSeparator);
11562         var curNode = this.root;
11563         if(curNode.attributes[attr] != keys[1]){ // invalid root
11564             if(callback){
11565                 callback(false, null);
11566             }
11567             return;
11568         }
11569         var index = 1;
11570         var f = function(){
11571             if(++index == keys.length){
11572                 if(callback){
11573                     callback(true, curNode);
11574                 }
11575                 return;
11576             }
11577             var c = curNode.findChild(attr, keys[index]);
11578             if(!c){
11579                 if(callback){
11580                     callback(false, curNode);
11581                 }
11582                 return;
11583             }
11584             curNode = c;
11585             c.expand(false, false, f);
11586         };
11587         curNode.expand(false, false, f);
11588     },
11589
11590     /**
11591      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11592      * @param {String} path
11593      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11594      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11595      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11596      */
11597     selectPath : function(path, attr, callback){
11598         attr = attr || "id";
11599         var keys = path.split(this.pathSeparator);
11600         var v = keys.pop();
11601         if(keys.length > 0){
11602             var f = function(success, node){
11603                 if(success && node){
11604                     var n = node.findChild(attr, v);
11605                     if(n){
11606                         n.select();
11607                         if(callback){
11608                             callback(true, n);
11609                         }
11610                     }else if(callback){
11611                         callback(false, n);
11612                     }
11613                 }else{
11614                     if(callback){
11615                         callback(false, n);
11616                     }
11617                 }
11618             };
11619             this.expandPath(keys.join(this.pathSeparator), attr, f);
11620         }else{
11621             this.root.select();
11622             if(callback){
11623                 callback(true, this.root);
11624             }
11625         }
11626     },
11627
11628     getTreeEl : function(){
11629         return this.el;
11630     },
11631
11632     /**
11633      * Trigger rendering of this TreePanel
11634      */
11635     render : function(){
11636         if (this.innerCt) {
11637             return this; // stop it rendering more than once!!
11638         }
11639         
11640         this.innerCt = this.el.createChild({tag:"ul",
11641                cls:"x-tree-root-ct " +
11642                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11643
11644         if(this.containerScroll){
11645             Roo.dd.ScrollManager.register(this.el);
11646         }
11647         if((this.enableDD || this.enableDrop) && !this.dropZone){
11648            /**
11649             * The dropZone used by this tree if drop is enabled
11650             * @type Roo.tree.TreeDropZone
11651             */
11652              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11653                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11654            });
11655         }
11656         if((this.enableDD || this.enableDrag) && !this.dragZone){
11657            /**
11658             * The dragZone used by this tree if drag is enabled
11659             * @type Roo.tree.TreeDragZone
11660             */
11661             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11662                ddGroup: this.ddGroup || "TreeDD",
11663                scroll: this.ddScroll
11664            });
11665         }
11666         this.getSelectionModel().init(this);
11667         if (!this.root) {
11668             Roo.log("ROOT not set in tree");
11669             return this;
11670         }
11671         this.root.render();
11672         if(!this.rootVisible){
11673             this.root.renderChildren();
11674         }
11675         return this;
11676     }
11677 });/*
11678  * Based on:
11679  * Ext JS Library 1.1.1
11680  * Copyright(c) 2006-2007, Ext JS, LLC.
11681  *
11682  * Originally Released Under LGPL - original licence link has changed is not relivant.
11683  *
11684  * Fork - LGPL
11685  * <script type="text/javascript">
11686  */
11687  
11688
11689 /**
11690  * @class Roo.tree.DefaultSelectionModel
11691  * @extends Roo.util.Observable
11692  * The default single selection for a TreePanel.
11693  * @param {Object} cfg Configuration
11694  */
11695 Roo.tree.DefaultSelectionModel = function(cfg){
11696    this.selNode = null;
11697    
11698    
11699    
11700    this.addEvents({
11701        /**
11702         * @event selectionchange
11703         * Fires when the selected node changes
11704         * @param {DefaultSelectionModel} this
11705         * @param {TreeNode} node the new selection
11706         */
11707        "selectionchange" : true,
11708
11709        /**
11710         * @event beforeselect
11711         * Fires before the selected node changes, return false to cancel the change
11712         * @param {DefaultSelectionModel} this
11713         * @param {TreeNode} node the new selection
11714         * @param {TreeNode} node the old selection
11715         */
11716        "beforeselect" : true
11717    });
11718    
11719     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11720 };
11721
11722 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11723     init : function(tree){
11724         this.tree = tree;
11725         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11726         tree.on("click", this.onNodeClick, this);
11727     },
11728     
11729     onNodeClick : function(node, e){
11730         if (e.ctrlKey && this.selNode == node)  {
11731             this.unselect(node);
11732             return;
11733         }
11734         this.select(node);
11735     },
11736     
11737     /**
11738      * Select a node.
11739      * @param {TreeNode} node The node to select
11740      * @return {TreeNode} The selected node
11741      */
11742     select : function(node){
11743         var last = this.selNode;
11744         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11745             if(last){
11746                 last.ui.onSelectedChange(false);
11747             }
11748             this.selNode = node;
11749             node.ui.onSelectedChange(true);
11750             this.fireEvent("selectionchange", this, node, last);
11751         }
11752         return node;
11753     },
11754     
11755     /**
11756      * Deselect a node.
11757      * @param {TreeNode} node The node to unselect
11758      */
11759     unselect : function(node){
11760         if(this.selNode == node){
11761             this.clearSelections();
11762         }    
11763     },
11764     
11765     /**
11766      * Clear all selections
11767      */
11768     clearSelections : function(){
11769         var n = this.selNode;
11770         if(n){
11771             n.ui.onSelectedChange(false);
11772             this.selNode = null;
11773             this.fireEvent("selectionchange", this, null);
11774         }
11775         return n;
11776     },
11777     
11778     /**
11779      * Get the selected node
11780      * @return {TreeNode} The selected node
11781      */
11782     getSelectedNode : function(){
11783         return this.selNode;    
11784     },
11785     
11786     /**
11787      * Returns true if the node is selected
11788      * @param {TreeNode} node The node to check
11789      * @return {Boolean}
11790      */
11791     isSelected : function(node){
11792         return this.selNode == node;  
11793     },
11794
11795     /**
11796      * Selects the node above the selected node in the tree, intelligently walking the nodes
11797      * @return TreeNode The new selection
11798      */
11799     selectPrevious : function(){
11800         var s = this.selNode || this.lastSelNode;
11801         if(!s){
11802             return null;
11803         }
11804         var ps = s.previousSibling;
11805         if(ps){
11806             if(!ps.isExpanded() || ps.childNodes.length < 1){
11807                 return this.select(ps);
11808             } else{
11809                 var lc = ps.lastChild;
11810                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11811                     lc = lc.lastChild;
11812                 }
11813                 return this.select(lc);
11814             }
11815         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11816             return this.select(s.parentNode);
11817         }
11818         return null;
11819     },
11820
11821     /**
11822      * Selects the node above the selected node in the tree, intelligently walking the nodes
11823      * @return TreeNode The new selection
11824      */
11825     selectNext : function(){
11826         var s = this.selNode || this.lastSelNode;
11827         if(!s){
11828             return null;
11829         }
11830         if(s.firstChild && s.isExpanded()){
11831              return this.select(s.firstChild);
11832          }else if(s.nextSibling){
11833              return this.select(s.nextSibling);
11834          }else if(s.parentNode){
11835             var newS = null;
11836             s.parentNode.bubble(function(){
11837                 if(this.nextSibling){
11838                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11839                     return false;
11840                 }
11841             });
11842             return newS;
11843          }
11844         return null;
11845     },
11846
11847     onKeyDown : function(e){
11848         var s = this.selNode || this.lastSelNode;
11849         // undesirable, but required
11850         var sm = this;
11851         if(!s){
11852             return;
11853         }
11854         var k = e.getKey();
11855         switch(k){
11856              case e.DOWN:
11857                  e.stopEvent();
11858                  this.selectNext();
11859              break;
11860              case e.UP:
11861                  e.stopEvent();
11862                  this.selectPrevious();
11863              break;
11864              case e.RIGHT:
11865                  e.preventDefault();
11866                  if(s.hasChildNodes()){
11867                      if(!s.isExpanded()){
11868                          s.expand();
11869                      }else if(s.firstChild){
11870                          this.select(s.firstChild, e);
11871                      }
11872                  }
11873              break;
11874              case e.LEFT:
11875                  e.preventDefault();
11876                  if(s.hasChildNodes() && s.isExpanded()){
11877                      s.collapse();
11878                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11879                      this.select(s.parentNode, e);
11880                  }
11881              break;
11882         };
11883     }
11884 });
11885
11886 /**
11887  * @class Roo.tree.MultiSelectionModel
11888  * @extends Roo.util.Observable
11889  * Multi selection for a TreePanel.
11890  * @param {Object} cfg Configuration
11891  */
11892 Roo.tree.MultiSelectionModel = function(){
11893    this.selNodes = [];
11894    this.selMap = {};
11895    this.addEvents({
11896        /**
11897         * @event selectionchange
11898         * Fires when the selected nodes change
11899         * @param {MultiSelectionModel} this
11900         * @param {Array} nodes Array of the selected nodes
11901         */
11902        "selectionchange" : true
11903    });
11904    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11905    
11906 };
11907
11908 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11909     init : function(tree){
11910         this.tree = tree;
11911         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11912         tree.on("click", this.onNodeClick, this);
11913     },
11914     
11915     onNodeClick : function(node, e){
11916         this.select(node, e, e.ctrlKey);
11917     },
11918     
11919     /**
11920      * Select a node.
11921      * @param {TreeNode} node The node to select
11922      * @param {EventObject} e (optional) An event associated with the selection
11923      * @param {Boolean} keepExisting True to retain existing selections
11924      * @return {TreeNode} The selected node
11925      */
11926     select : function(node, e, keepExisting){
11927         if(keepExisting !== true){
11928             this.clearSelections(true);
11929         }
11930         if(this.isSelected(node)){
11931             this.lastSelNode = node;
11932             return node;
11933         }
11934         this.selNodes.push(node);
11935         this.selMap[node.id] = node;
11936         this.lastSelNode = node;
11937         node.ui.onSelectedChange(true);
11938         this.fireEvent("selectionchange", this, this.selNodes);
11939         return node;
11940     },
11941     
11942     /**
11943      * Deselect a node.
11944      * @param {TreeNode} node The node to unselect
11945      */
11946     unselect : function(node){
11947         if(this.selMap[node.id]){
11948             node.ui.onSelectedChange(false);
11949             var sn = this.selNodes;
11950             var index = -1;
11951             if(sn.indexOf){
11952                 index = sn.indexOf(node);
11953             }else{
11954                 for(var i = 0, len = sn.length; i < len; i++){
11955                     if(sn[i] == node){
11956                         index = i;
11957                         break;
11958                     }
11959                 }
11960             }
11961             if(index != -1){
11962                 this.selNodes.splice(index, 1);
11963             }
11964             delete this.selMap[node.id];
11965             this.fireEvent("selectionchange", this, this.selNodes);
11966         }
11967     },
11968     
11969     /**
11970      * Clear all selections
11971      */
11972     clearSelections : function(suppressEvent){
11973         var sn = this.selNodes;
11974         if(sn.length > 0){
11975             for(var i = 0, len = sn.length; i < len; i++){
11976                 sn[i].ui.onSelectedChange(false);
11977             }
11978             this.selNodes = [];
11979             this.selMap = {};
11980             if(suppressEvent !== true){
11981                 this.fireEvent("selectionchange", this, this.selNodes);
11982             }
11983         }
11984     },
11985     
11986     /**
11987      * Returns true if the node is selected
11988      * @param {TreeNode} node The node to check
11989      * @return {Boolean}
11990      */
11991     isSelected : function(node){
11992         return this.selMap[node.id] ? true : false;  
11993     },
11994     
11995     /**
11996      * Returns an array of the selected nodes
11997      * @return {Array}
11998      */
11999     getSelectedNodes : function(){
12000         return this.selNodes;    
12001     },
12002
12003     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12004
12005     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12006
12007     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12008 });/*
12009  * Based on:
12010  * Ext JS Library 1.1.1
12011  * Copyright(c) 2006-2007, Ext JS, LLC.
12012  *
12013  * Originally Released Under LGPL - original licence link has changed is not relivant.
12014  *
12015  * Fork - LGPL
12016  * <script type="text/javascript">
12017  */
12018  
12019 /**
12020  * @class Roo.tree.TreeNode
12021  * @extends Roo.data.Node
12022  * @cfg {String} text The text for this node
12023  * @cfg {Boolean} expanded true to start the node expanded
12024  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12025  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12026  * @cfg {Boolean} disabled true to start the node disabled
12027  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12028  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12029  * @cfg {String} cls A css class to be added to the node
12030  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12031  * @cfg {String} href URL of the link used for the node (defaults to #)
12032  * @cfg {String} hrefTarget target frame for the link
12033  * @cfg {String} qtip An Ext QuickTip for the node
12034  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12035  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12036  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12037  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12038  * (defaults to undefined with no checkbox rendered)
12039  * @constructor
12040  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12041  */
12042 Roo.tree.TreeNode = function(attributes){
12043     attributes = attributes || {};
12044     if(typeof attributes == "string"){
12045         attributes = {text: attributes};
12046     }
12047     this.childrenRendered = false;
12048     this.rendered = false;
12049     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12050     this.expanded = attributes.expanded === true;
12051     this.isTarget = attributes.isTarget !== false;
12052     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12053     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12054
12055     /**
12056      * Read-only. The text for this node. To change it use setText().
12057      * @type String
12058      */
12059     this.text = attributes.text;
12060     /**
12061      * True if this node is disabled.
12062      * @type Boolean
12063      */
12064     this.disabled = attributes.disabled === true;
12065
12066     this.addEvents({
12067         /**
12068         * @event textchange
12069         * Fires when the text for this node is changed
12070         * @param {Node} this This node
12071         * @param {String} text The new text
12072         * @param {String} oldText The old text
12073         */
12074         "textchange" : true,
12075         /**
12076         * @event beforeexpand
12077         * Fires before this node is expanded, return false to cancel.
12078         * @param {Node} this This node
12079         * @param {Boolean} deep
12080         * @param {Boolean} anim
12081         */
12082         "beforeexpand" : true,
12083         /**
12084         * @event beforecollapse
12085         * Fires before this node is collapsed, return false to cancel.
12086         * @param {Node} this This node
12087         * @param {Boolean} deep
12088         * @param {Boolean} anim
12089         */
12090         "beforecollapse" : true,
12091         /**
12092         * @event expand
12093         * Fires when this node is expanded
12094         * @param {Node} this This node
12095         */
12096         "expand" : true,
12097         /**
12098         * @event disabledchange
12099         * Fires when the disabled status of this node changes
12100         * @param {Node} this This node
12101         * @param {Boolean} disabled
12102         */
12103         "disabledchange" : true,
12104         /**
12105         * @event collapse
12106         * Fires when this node is collapsed
12107         * @param {Node} this This node
12108         */
12109         "collapse" : true,
12110         /**
12111         * @event beforeclick
12112         * Fires before click processing. Return false to cancel the default action.
12113         * @param {Node} this This node
12114         * @param {Roo.EventObject} e The event object
12115         */
12116         "beforeclick":true,
12117         /**
12118         * @event checkchange
12119         * Fires when a node with a checkbox's checked property changes
12120         * @param {Node} this This node
12121         * @param {Boolean} checked
12122         */
12123         "checkchange":true,
12124         /**
12125         * @event click
12126         * Fires when this node is clicked
12127         * @param {Node} this This node
12128         * @param {Roo.EventObject} e The event object
12129         */
12130         "click":true,
12131         /**
12132         * @event dblclick
12133         * Fires when this node is double clicked
12134         * @param {Node} this This node
12135         * @param {Roo.EventObject} e The event object
12136         */
12137         "dblclick":true,
12138         /**
12139         * @event contextmenu
12140         * Fires when this node is right clicked
12141         * @param {Node} this This node
12142         * @param {Roo.EventObject} e The event object
12143         */
12144         "contextmenu":true,
12145         /**
12146         * @event beforechildrenrendered
12147         * Fires right before the child nodes for this node are rendered
12148         * @param {Node} this This node
12149         */
12150         "beforechildrenrendered":true
12151     });
12152
12153     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12154
12155     /**
12156      * Read-only. The UI for this node
12157      * @type TreeNodeUI
12158      */
12159     this.ui = new uiClass(this);
12160     
12161     // finally support items[]
12162     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12163         return;
12164     }
12165     
12166     
12167     Roo.each(this.attributes.items, function(c) {
12168         this.appendChild(Roo.factory(c,Roo.Tree));
12169     }, this);
12170     delete this.attributes.items;
12171     
12172     
12173     
12174 };
12175 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12176     preventHScroll: true,
12177     /**
12178      * Returns true if this node is expanded
12179      * @return {Boolean}
12180      */
12181     isExpanded : function(){
12182         return this.expanded;
12183     },
12184
12185     /**
12186      * Returns the UI object for this node
12187      * @return {TreeNodeUI}
12188      */
12189     getUI : function(){
12190         return this.ui;
12191     },
12192
12193     // private override
12194     setFirstChild : function(node){
12195         var of = this.firstChild;
12196         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12197         if(this.childrenRendered && of && node != of){
12198             of.renderIndent(true, true);
12199         }
12200         if(this.rendered){
12201             this.renderIndent(true, true);
12202         }
12203     },
12204
12205     // private override
12206     setLastChild : function(node){
12207         var ol = this.lastChild;
12208         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12209         if(this.childrenRendered && ol && node != ol){
12210             ol.renderIndent(true, true);
12211         }
12212         if(this.rendered){
12213             this.renderIndent(true, true);
12214         }
12215     },
12216
12217     // these methods are overridden to provide lazy rendering support
12218     // private override
12219     appendChild : function()
12220     {
12221         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12222         if(node && this.childrenRendered){
12223             node.render();
12224         }
12225         this.ui.updateExpandIcon();
12226         return node;
12227     },
12228
12229     // private override
12230     removeChild : function(node){
12231         this.ownerTree.getSelectionModel().unselect(node);
12232         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12233         // if it's been rendered remove dom node
12234         if(this.childrenRendered){
12235             node.ui.remove();
12236         }
12237         if(this.childNodes.length < 1){
12238             this.collapse(false, false);
12239         }else{
12240             this.ui.updateExpandIcon();
12241         }
12242         if(!this.firstChild) {
12243             this.childrenRendered = false;
12244         }
12245         return node;
12246     },
12247
12248     // private override
12249     insertBefore : function(node, refNode){
12250         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12251         if(newNode && refNode && this.childrenRendered){
12252             node.render();
12253         }
12254         this.ui.updateExpandIcon();
12255         return newNode;
12256     },
12257
12258     /**
12259      * Sets the text for this node
12260      * @param {String} text
12261      */
12262     setText : function(text){
12263         var oldText = this.text;
12264         this.text = text;
12265         this.attributes.text = text;
12266         if(this.rendered){ // event without subscribing
12267             this.ui.onTextChange(this, text, oldText);
12268         }
12269         this.fireEvent("textchange", this, text, oldText);
12270     },
12271
12272     /**
12273      * Triggers selection of this node
12274      */
12275     select : function(){
12276         this.getOwnerTree().getSelectionModel().select(this);
12277     },
12278
12279     /**
12280      * Triggers deselection of this node
12281      */
12282     unselect : function(){
12283         this.getOwnerTree().getSelectionModel().unselect(this);
12284     },
12285
12286     /**
12287      * Returns true if this node is selected
12288      * @return {Boolean}
12289      */
12290     isSelected : function(){
12291         return this.getOwnerTree().getSelectionModel().isSelected(this);
12292     },
12293
12294     /**
12295      * Expand this node.
12296      * @param {Boolean} deep (optional) True to expand all children as well
12297      * @param {Boolean} anim (optional) false to cancel the default animation
12298      * @param {Function} callback (optional) A callback to be called when
12299      * expanding this node completes (does not wait for deep expand to complete).
12300      * Called with 1 parameter, this node.
12301      */
12302     expand : function(deep, anim, callback){
12303         if(!this.expanded){
12304             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12305                 return;
12306             }
12307             if(!this.childrenRendered){
12308                 this.renderChildren();
12309             }
12310             this.expanded = true;
12311             
12312             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12313                 this.ui.animExpand(function(){
12314                     this.fireEvent("expand", this);
12315                     if(typeof callback == "function"){
12316                         callback(this);
12317                     }
12318                     if(deep === true){
12319                         this.expandChildNodes(true);
12320                     }
12321                 }.createDelegate(this));
12322                 return;
12323             }else{
12324                 this.ui.expand();
12325                 this.fireEvent("expand", this);
12326                 if(typeof callback == "function"){
12327                     callback(this);
12328                 }
12329             }
12330         }else{
12331            if(typeof callback == "function"){
12332                callback(this);
12333            }
12334         }
12335         if(deep === true){
12336             this.expandChildNodes(true);
12337         }
12338     },
12339
12340     isHiddenRoot : function(){
12341         return this.isRoot && !this.getOwnerTree().rootVisible;
12342     },
12343
12344     /**
12345      * Collapse this node.
12346      * @param {Boolean} deep (optional) True to collapse all children as well
12347      * @param {Boolean} anim (optional) false to cancel the default animation
12348      */
12349     collapse : function(deep, anim){
12350         if(this.expanded && !this.isHiddenRoot()){
12351             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12352                 return;
12353             }
12354             this.expanded = false;
12355             if((this.getOwnerTree().animate && anim !== false) || anim){
12356                 this.ui.animCollapse(function(){
12357                     this.fireEvent("collapse", this);
12358                     if(deep === true){
12359                         this.collapseChildNodes(true);
12360                     }
12361                 }.createDelegate(this));
12362                 return;
12363             }else{
12364                 this.ui.collapse();
12365                 this.fireEvent("collapse", this);
12366             }
12367         }
12368         if(deep === true){
12369             var cs = this.childNodes;
12370             for(var i = 0, len = cs.length; i < len; i++) {
12371                 cs[i].collapse(true, false);
12372             }
12373         }
12374     },
12375
12376     // private
12377     delayedExpand : function(delay){
12378         if(!this.expandProcId){
12379             this.expandProcId = this.expand.defer(delay, this);
12380         }
12381     },
12382
12383     // private
12384     cancelExpand : function(){
12385         if(this.expandProcId){
12386             clearTimeout(this.expandProcId);
12387         }
12388         this.expandProcId = false;
12389     },
12390
12391     /**
12392      * Toggles expanded/collapsed state of the node
12393      */
12394     toggle : function(){
12395         if(this.expanded){
12396             this.collapse();
12397         }else{
12398             this.expand();
12399         }
12400     },
12401
12402     /**
12403      * Ensures all parent nodes are expanded
12404      */
12405     ensureVisible : function(callback){
12406         var tree = this.getOwnerTree();
12407         tree.expandPath(this.parentNode.getPath(), false, function(){
12408             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12409             Roo.callback(callback);
12410         }.createDelegate(this));
12411     },
12412
12413     /**
12414      * Expand all child nodes
12415      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12416      */
12417     expandChildNodes : function(deep){
12418         var cs = this.childNodes;
12419         for(var i = 0, len = cs.length; i < len; i++) {
12420                 cs[i].expand(deep);
12421         }
12422     },
12423
12424     /**
12425      * Collapse all child nodes
12426      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12427      */
12428     collapseChildNodes : function(deep){
12429         var cs = this.childNodes;
12430         for(var i = 0, len = cs.length; i < len; i++) {
12431                 cs[i].collapse(deep);
12432         }
12433     },
12434
12435     /**
12436      * Disables this node
12437      */
12438     disable : function(){
12439         this.disabled = true;
12440         this.unselect();
12441         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12442             this.ui.onDisableChange(this, true);
12443         }
12444         this.fireEvent("disabledchange", this, true);
12445     },
12446
12447     /**
12448      * Enables this node
12449      */
12450     enable : function(){
12451         this.disabled = false;
12452         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12453             this.ui.onDisableChange(this, false);
12454         }
12455         this.fireEvent("disabledchange", this, false);
12456     },
12457
12458     // private
12459     renderChildren : function(suppressEvent){
12460         if(suppressEvent !== false){
12461             this.fireEvent("beforechildrenrendered", this);
12462         }
12463         var cs = this.childNodes;
12464         for(var i = 0, len = cs.length; i < len; i++){
12465             cs[i].render(true);
12466         }
12467         this.childrenRendered = true;
12468     },
12469
12470     // private
12471     sort : function(fn, scope){
12472         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12473         if(this.childrenRendered){
12474             var cs = this.childNodes;
12475             for(var i = 0, len = cs.length; i < len; i++){
12476                 cs[i].render(true);
12477             }
12478         }
12479     },
12480
12481     // private
12482     render : function(bulkRender){
12483         this.ui.render(bulkRender);
12484         if(!this.rendered){
12485             this.rendered = true;
12486             if(this.expanded){
12487                 this.expanded = false;
12488                 this.expand(false, false);
12489             }
12490         }
12491     },
12492
12493     // private
12494     renderIndent : function(deep, refresh){
12495         if(refresh){
12496             this.ui.childIndent = null;
12497         }
12498         this.ui.renderIndent();
12499         if(deep === true && this.childrenRendered){
12500             var cs = this.childNodes;
12501             for(var i = 0, len = cs.length; i < len; i++){
12502                 cs[i].renderIndent(true, refresh);
12503             }
12504         }
12505     }
12506 });/*
12507  * Based on:
12508  * Ext JS Library 1.1.1
12509  * Copyright(c) 2006-2007, Ext JS, LLC.
12510  *
12511  * Originally Released Under LGPL - original licence link has changed is not relivant.
12512  *
12513  * Fork - LGPL
12514  * <script type="text/javascript">
12515  */
12516  
12517 /**
12518  * @class Roo.tree.AsyncTreeNode
12519  * @extends Roo.tree.TreeNode
12520  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12521  * @constructor
12522  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12523  */
12524  Roo.tree.AsyncTreeNode = function(config){
12525     this.loaded = false;
12526     this.loading = false;
12527     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12528     /**
12529     * @event beforeload
12530     * Fires before this node is loaded, return false to cancel
12531     * @param {Node} this This node
12532     */
12533     this.addEvents({'beforeload':true, 'load': true});
12534     /**
12535     * @event load
12536     * Fires when this node is loaded
12537     * @param {Node} this This node
12538     */
12539     /**
12540      * The loader used by this node (defaults to using the tree's defined loader)
12541      * @type TreeLoader
12542      * @property loader
12543      */
12544 };
12545 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12546     expand : function(deep, anim, callback){
12547         if(this.loading){ // if an async load is already running, waiting til it's done
12548             var timer;
12549             var f = function(){
12550                 if(!this.loading){ // done loading
12551                     clearInterval(timer);
12552                     this.expand(deep, anim, callback);
12553                 }
12554             }.createDelegate(this);
12555             timer = setInterval(f, 200);
12556             return;
12557         }
12558         if(!this.loaded){
12559             if(this.fireEvent("beforeload", this) === false){
12560                 return;
12561             }
12562             this.loading = true;
12563             this.ui.beforeLoad(this);
12564             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12565             if(loader){
12566                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12567                 return;
12568             }
12569         }
12570         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12571     },
12572     
12573     /**
12574      * Returns true if this node is currently loading
12575      * @return {Boolean}
12576      */
12577     isLoading : function(){
12578         return this.loading;  
12579     },
12580     
12581     loadComplete : function(deep, anim, callback){
12582         this.loading = false;
12583         this.loaded = true;
12584         this.ui.afterLoad(this);
12585         this.fireEvent("load", this);
12586         this.expand(deep, anim, callback);
12587     },
12588     
12589     /**
12590      * Returns true if this node has been loaded
12591      * @return {Boolean}
12592      */
12593     isLoaded : function(){
12594         return this.loaded;
12595     },
12596     
12597     hasChildNodes : function(){
12598         if(!this.isLeaf() && !this.loaded){
12599             return true;
12600         }else{
12601             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12602         }
12603     },
12604
12605     /**
12606      * Trigger a reload for this node
12607      * @param {Function} callback
12608      */
12609     reload : function(callback){
12610         this.collapse(false, false);
12611         while(this.firstChild){
12612             this.removeChild(this.firstChild);
12613         }
12614         this.childrenRendered = false;
12615         this.loaded = false;
12616         if(this.isHiddenRoot()){
12617             this.expanded = false;
12618         }
12619         this.expand(false, false, callback);
12620     }
12621 });/*
12622  * Based on:
12623  * Ext JS Library 1.1.1
12624  * Copyright(c) 2006-2007, Ext JS, LLC.
12625  *
12626  * Originally Released Under LGPL - original licence link has changed is not relivant.
12627  *
12628  * Fork - LGPL
12629  * <script type="text/javascript">
12630  */
12631  
12632 /**
12633  * @class Roo.tree.TreeNodeUI
12634  * @constructor
12635  * @param {Object} node The node to render
12636  * The TreeNode UI implementation is separate from the
12637  * tree implementation. Unless you are customizing the tree UI,
12638  * you should never have to use this directly.
12639  */
12640 Roo.tree.TreeNodeUI = function(node){
12641     this.node = node;
12642     this.rendered = false;
12643     this.animating = false;
12644     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12645 };
12646
12647 Roo.tree.TreeNodeUI.prototype = {
12648     removeChild : function(node){
12649         if(this.rendered){
12650             this.ctNode.removeChild(node.ui.getEl());
12651         }
12652     },
12653
12654     beforeLoad : function(){
12655          this.addClass("x-tree-node-loading");
12656     },
12657
12658     afterLoad : function(){
12659          this.removeClass("x-tree-node-loading");
12660     },
12661
12662     onTextChange : function(node, text, oldText){
12663         if(this.rendered){
12664             this.textNode.innerHTML = text;
12665         }
12666     },
12667
12668     onDisableChange : function(node, state){
12669         this.disabled = state;
12670         if(state){
12671             this.addClass("x-tree-node-disabled");
12672         }else{
12673             this.removeClass("x-tree-node-disabled");
12674         }
12675     },
12676
12677     onSelectedChange : function(state){
12678         if(state){
12679             this.focus();
12680             this.addClass("x-tree-selected");
12681         }else{
12682             //this.blur();
12683             this.removeClass("x-tree-selected");
12684         }
12685     },
12686
12687     onMove : function(tree, node, oldParent, newParent, index, refNode){
12688         this.childIndent = null;
12689         if(this.rendered){
12690             var targetNode = newParent.ui.getContainer();
12691             if(!targetNode){//target not rendered
12692                 this.holder = document.createElement("div");
12693                 this.holder.appendChild(this.wrap);
12694                 return;
12695             }
12696             var insertBefore = refNode ? refNode.ui.getEl() : null;
12697             if(insertBefore){
12698                 targetNode.insertBefore(this.wrap, insertBefore);
12699             }else{
12700                 targetNode.appendChild(this.wrap);
12701             }
12702             this.node.renderIndent(true);
12703         }
12704     },
12705
12706     addClass : function(cls){
12707         if(this.elNode){
12708             Roo.fly(this.elNode).addClass(cls);
12709         }
12710     },
12711
12712     removeClass : function(cls){
12713         if(this.elNode){
12714             Roo.fly(this.elNode).removeClass(cls);
12715         }
12716     },
12717
12718     remove : function(){
12719         if(this.rendered){
12720             this.holder = document.createElement("div");
12721             this.holder.appendChild(this.wrap);
12722         }
12723     },
12724
12725     fireEvent : function(){
12726         return this.node.fireEvent.apply(this.node, arguments);
12727     },
12728
12729     initEvents : function(){
12730         this.node.on("move", this.onMove, this);
12731         var E = Roo.EventManager;
12732         var a = this.anchor;
12733
12734         var el = Roo.fly(a, '_treeui');
12735
12736         if(Roo.isOpera){ // opera render bug ignores the CSS
12737             el.setStyle("text-decoration", "none");
12738         }
12739
12740         el.on("click", this.onClick, this);
12741         el.on("dblclick", this.onDblClick, this);
12742
12743         if(this.checkbox){
12744             Roo.EventManager.on(this.checkbox,
12745                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12746         }
12747
12748         el.on("contextmenu", this.onContextMenu, this);
12749
12750         var icon = Roo.fly(this.iconNode);
12751         icon.on("click", this.onClick, this);
12752         icon.on("dblclick", this.onDblClick, this);
12753         icon.on("contextmenu", this.onContextMenu, this);
12754         E.on(this.ecNode, "click", this.ecClick, this, true);
12755
12756         if(this.node.disabled){
12757             this.addClass("x-tree-node-disabled");
12758         }
12759         if(this.node.hidden){
12760             this.addClass("x-tree-node-disabled");
12761         }
12762         var ot = this.node.getOwnerTree();
12763         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12764         if(dd && (!this.node.isRoot || ot.rootVisible)){
12765             Roo.dd.Registry.register(this.elNode, {
12766                 node: this.node,
12767                 handles: this.getDDHandles(),
12768                 isHandle: false
12769             });
12770         }
12771     },
12772
12773     getDDHandles : function(){
12774         return [this.iconNode, this.textNode];
12775     },
12776
12777     hide : function(){
12778         if(this.rendered){
12779             this.wrap.style.display = "none";
12780         }
12781     },
12782
12783     show : function(){
12784         if(this.rendered){
12785             this.wrap.style.display = "";
12786         }
12787     },
12788
12789     onContextMenu : function(e){
12790         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12791             e.preventDefault();
12792             this.focus();
12793             this.fireEvent("contextmenu", this.node, e);
12794         }
12795     },
12796
12797     onClick : function(e){
12798         if(this.dropping){
12799             e.stopEvent();
12800             return;
12801         }
12802         if(this.fireEvent("beforeclick", this.node, e) !== false){
12803             if(!this.disabled && this.node.attributes.href){
12804                 this.fireEvent("click", this.node, e);
12805                 return;
12806             }
12807             e.preventDefault();
12808             if(this.disabled){
12809                 return;
12810             }
12811
12812             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12813                 this.node.toggle();
12814             }
12815
12816             this.fireEvent("click", this.node, e);
12817         }else{
12818             e.stopEvent();
12819         }
12820     },
12821
12822     onDblClick : function(e){
12823         e.preventDefault();
12824         if(this.disabled){
12825             return;
12826         }
12827         if(this.checkbox){
12828             this.toggleCheck();
12829         }
12830         if(!this.animating && this.node.hasChildNodes()){
12831             this.node.toggle();
12832         }
12833         this.fireEvent("dblclick", this.node, e);
12834     },
12835
12836     onCheckChange : function(){
12837         var checked = this.checkbox.checked;
12838         this.node.attributes.checked = checked;
12839         this.fireEvent('checkchange', this.node, checked);
12840     },
12841
12842     ecClick : function(e){
12843         if(!this.animating && this.node.hasChildNodes()){
12844             this.node.toggle();
12845         }
12846     },
12847
12848     startDrop : function(){
12849         this.dropping = true;
12850     },
12851
12852     // delayed drop so the click event doesn't get fired on a drop
12853     endDrop : function(){
12854        setTimeout(function(){
12855            this.dropping = false;
12856        }.createDelegate(this), 50);
12857     },
12858
12859     expand : function(){
12860         this.updateExpandIcon();
12861         this.ctNode.style.display = "";
12862     },
12863
12864     focus : function(){
12865         if(!this.node.preventHScroll){
12866             try{this.anchor.focus();
12867             }catch(e){}
12868         }else if(!Roo.isIE){
12869             try{
12870                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12871                 var l = noscroll.scrollLeft;
12872                 this.anchor.focus();
12873                 noscroll.scrollLeft = l;
12874             }catch(e){}
12875         }
12876     },
12877
12878     toggleCheck : function(value){
12879         var cb = this.checkbox;
12880         if(cb){
12881             cb.checked = (value === undefined ? !cb.checked : value);
12882         }
12883     },
12884
12885     blur : function(){
12886         try{
12887             this.anchor.blur();
12888         }catch(e){}
12889     },
12890
12891     animExpand : function(callback){
12892         var ct = Roo.get(this.ctNode);
12893         ct.stopFx();
12894         if(!this.node.hasChildNodes()){
12895             this.updateExpandIcon();
12896             this.ctNode.style.display = "";
12897             Roo.callback(callback);
12898             return;
12899         }
12900         this.animating = true;
12901         this.updateExpandIcon();
12902
12903         ct.slideIn('t', {
12904            callback : function(){
12905                this.animating = false;
12906                Roo.callback(callback);
12907             },
12908             scope: this,
12909             duration: this.node.ownerTree.duration || .25
12910         });
12911     },
12912
12913     highlight : function(){
12914         var tree = this.node.getOwnerTree();
12915         Roo.fly(this.wrap).highlight(
12916             tree.hlColor || "C3DAF9",
12917             {endColor: tree.hlBaseColor}
12918         );
12919     },
12920
12921     collapse : function(){
12922         this.updateExpandIcon();
12923         this.ctNode.style.display = "none";
12924     },
12925
12926     animCollapse : function(callback){
12927         var ct = Roo.get(this.ctNode);
12928         ct.enableDisplayMode('block');
12929         ct.stopFx();
12930
12931         this.animating = true;
12932         this.updateExpandIcon();
12933
12934         ct.slideOut('t', {
12935             callback : function(){
12936                this.animating = false;
12937                Roo.callback(callback);
12938             },
12939             scope: this,
12940             duration: this.node.ownerTree.duration || .25
12941         });
12942     },
12943
12944     getContainer : function(){
12945         return this.ctNode;
12946     },
12947
12948     getEl : function(){
12949         return this.wrap;
12950     },
12951
12952     appendDDGhost : function(ghostNode){
12953         ghostNode.appendChild(this.elNode.cloneNode(true));
12954     },
12955
12956     getDDRepairXY : function(){
12957         return Roo.lib.Dom.getXY(this.iconNode);
12958     },
12959
12960     onRender : function(){
12961         this.render();
12962     },
12963
12964     render : function(bulkRender){
12965         var n = this.node, a = n.attributes;
12966         var targetNode = n.parentNode ?
12967               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12968
12969         if(!this.rendered){
12970             this.rendered = true;
12971
12972             this.renderElements(n, a, targetNode, bulkRender);
12973
12974             if(a.qtip){
12975                if(this.textNode.setAttributeNS){
12976                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
12977                    if(a.qtipTitle){
12978                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
12979                    }
12980                }else{
12981                    this.textNode.setAttribute("ext:qtip", a.qtip);
12982                    if(a.qtipTitle){
12983                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
12984                    }
12985                }
12986             }else if(a.qtipCfg){
12987                 a.qtipCfg.target = Roo.id(this.textNode);
12988                 Roo.QuickTips.register(a.qtipCfg);
12989             }
12990             this.initEvents();
12991             if(!this.node.expanded){
12992                 this.updateExpandIcon();
12993             }
12994         }else{
12995             if(bulkRender === true) {
12996                 targetNode.appendChild(this.wrap);
12997             }
12998         }
12999     },
13000
13001     renderElements : function(n, a, targetNode, bulkRender)
13002     {
13003         // add some indent caching, this helps performance when rendering a large tree
13004         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13005         var t = n.getOwnerTree();
13006         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13007         if (typeof(n.attributes.html) != 'undefined') {
13008             txt = n.attributes.html;
13009         }
13010         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13011         var cb = typeof a.checked == 'boolean';
13012         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13013         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13014             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13015             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13016             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13017             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13018             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13019              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13020                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13021             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13022             "</li>"];
13023
13024         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13025             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13026                                 n.nextSibling.ui.getEl(), buf.join(""));
13027         }else{
13028             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13029         }
13030
13031         this.elNode = this.wrap.childNodes[0];
13032         this.ctNode = this.wrap.childNodes[1];
13033         var cs = this.elNode.childNodes;
13034         this.indentNode = cs[0];
13035         this.ecNode = cs[1];
13036         this.iconNode = cs[2];
13037         var index = 3;
13038         if(cb){
13039             this.checkbox = cs[3];
13040             index++;
13041         }
13042         this.anchor = cs[index];
13043         this.textNode = cs[index].firstChild;
13044     },
13045
13046     getAnchor : function(){
13047         return this.anchor;
13048     },
13049
13050     getTextEl : function(){
13051         return this.textNode;
13052     },
13053
13054     getIconEl : function(){
13055         return this.iconNode;
13056     },
13057
13058     isChecked : function(){
13059         return this.checkbox ? this.checkbox.checked : false;
13060     },
13061
13062     updateExpandIcon : function(){
13063         if(this.rendered){
13064             var n = this.node, c1, c2;
13065             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13066             var hasChild = n.hasChildNodes();
13067             if(hasChild){
13068                 if(n.expanded){
13069                     cls += "-minus";
13070                     c1 = "x-tree-node-collapsed";
13071                     c2 = "x-tree-node-expanded";
13072                 }else{
13073                     cls += "-plus";
13074                     c1 = "x-tree-node-expanded";
13075                     c2 = "x-tree-node-collapsed";
13076                 }
13077                 if(this.wasLeaf){
13078                     this.removeClass("x-tree-node-leaf");
13079                     this.wasLeaf = false;
13080                 }
13081                 if(this.c1 != c1 || this.c2 != c2){
13082                     Roo.fly(this.elNode).replaceClass(c1, c2);
13083                     this.c1 = c1; this.c2 = c2;
13084                 }
13085             }else{
13086                 // this changes non-leafs into leafs if they have no children.
13087                 // it's not very rational behaviour..
13088                 
13089                 if(!this.wasLeaf && this.node.leaf){
13090                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13091                     delete this.c1;
13092                     delete this.c2;
13093                     this.wasLeaf = true;
13094                 }
13095             }
13096             var ecc = "x-tree-ec-icon "+cls;
13097             if(this.ecc != ecc){
13098                 this.ecNode.className = ecc;
13099                 this.ecc = ecc;
13100             }
13101         }
13102     },
13103
13104     getChildIndent : function(){
13105         if(!this.childIndent){
13106             var buf = [];
13107             var p = this.node;
13108             while(p){
13109                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13110                     if(!p.isLast()) {
13111                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13112                     } else {
13113                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13114                     }
13115                 }
13116                 p = p.parentNode;
13117             }
13118             this.childIndent = buf.join("");
13119         }
13120         return this.childIndent;
13121     },
13122
13123     renderIndent : function(){
13124         if(this.rendered){
13125             var indent = "";
13126             var p = this.node.parentNode;
13127             if(p){
13128                 indent = p.ui.getChildIndent();
13129             }
13130             if(this.indentMarkup != indent){ // don't rerender if not required
13131                 this.indentNode.innerHTML = indent;
13132                 this.indentMarkup = indent;
13133             }
13134             this.updateExpandIcon();
13135         }
13136     }
13137 };
13138
13139 Roo.tree.RootTreeNodeUI = function(){
13140     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13141 };
13142 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13143     render : function(){
13144         if(!this.rendered){
13145             var targetNode = this.node.ownerTree.innerCt.dom;
13146             this.node.expanded = true;
13147             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13148             this.wrap = this.ctNode = targetNode.firstChild;
13149         }
13150     },
13151     collapse : function(){
13152     },
13153     expand : function(){
13154     }
13155 });/*
13156  * Based on:
13157  * Ext JS Library 1.1.1
13158  * Copyright(c) 2006-2007, Ext JS, LLC.
13159  *
13160  * Originally Released Under LGPL - original licence link has changed is not relivant.
13161  *
13162  * Fork - LGPL
13163  * <script type="text/javascript">
13164  */
13165 /**
13166  * @class Roo.tree.TreeLoader
13167  * @extends Roo.util.Observable
13168  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13169  * nodes from a specified URL. The response must be a javascript Array definition
13170  * who's elements are node definition objects. eg:
13171  * <pre><code>
13172 {  success : true,
13173    data :      [
13174    
13175     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13176     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13177     ]
13178 }
13179
13180
13181 </code></pre>
13182  * <br><br>
13183  * The old style respose with just an array is still supported, but not recommended.
13184  * <br><br>
13185  *
13186  * A server request is sent, and child nodes are loaded only when a node is expanded.
13187  * The loading node's id is passed to the server under the parameter name "node" to
13188  * enable the server to produce the correct child nodes.
13189  * <br><br>
13190  * To pass extra parameters, an event handler may be attached to the "beforeload"
13191  * event, and the parameters specified in the TreeLoader's baseParams property:
13192  * <pre><code>
13193     myTreeLoader.on("beforeload", function(treeLoader, node) {
13194         this.baseParams.category = node.attributes.category;
13195     }, this);
13196     
13197 </code></pre>
13198  *
13199  * This would pass an HTTP parameter called "category" to the server containing
13200  * the value of the Node's "category" attribute.
13201  * @constructor
13202  * Creates a new Treeloader.
13203  * @param {Object} config A config object containing config properties.
13204  */
13205 Roo.tree.TreeLoader = function(config){
13206     this.baseParams = {};
13207     this.requestMethod = "POST";
13208     Roo.apply(this, config);
13209
13210     this.addEvents({
13211     
13212         /**
13213          * @event beforeload
13214          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13215          * @param {Object} This TreeLoader object.
13216          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13217          * @param {Object} callback The callback function specified in the {@link #load} call.
13218          */
13219         beforeload : true,
13220         /**
13221          * @event load
13222          * Fires when the node has been successfuly loaded.
13223          * @param {Object} This TreeLoader object.
13224          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13225          * @param {Object} response The response object containing the data from the server.
13226          */
13227         load : true,
13228         /**
13229          * @event loadexception
13230          * Fires if the network request failed.
13231          * @param {Object} This TreeLoader object.
13232          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13233          * @param {Object} response The response object containing the data from the server.
13234          */
13235         loadexception : true,
13236         /**
13237          * @event create
13238          * Fires before a node is created, enabling you to return custom Node types 
13239          * @param {Object} This TreeLoader object.
13240          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13241          */
13242         create : true
13243     });
13244
13245     Roo.tree.TreeLoader.superclass.constructor.call(this);
13246 };
13247
13248 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13249     /**
13250     * @cfg {String} dataUrl The URL from which to request a Json string which
13251     * specifies an array of node definition object representing the child nodes
13252     * to be loaded.
13253     */
13254     /**
13255     * @cfg {String} requestMethod either GET or POST
13256     * defaults to POST (due to BC)
13257     * to be loaded.
13258     */
13259     /**
13260     * @cfg {Object} baseParams (optional) An object containing properties which
13261     * specify HTTP parameters to be passed to each request for child nodes.
13262     */
13263     /**
13264     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13265     * created by this loader. If the attributes sent by the server have an attribute in this object,
13266     * they take priority.
13267     */
13268     /**
13269     * @cfg {Object} uiProviders (optional) An object containing properties which
13270     * 
13271     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13272     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13273     * <i>uiProvider</i> attribute of a returned child node is a string rather
13274     * than a reference to a TreeNodeUI implementation, this that string value
13275     * is used as a property name in the uiProviders object. You can define the provider named
13276     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13277     */
13278     uiProviders : {},
13279
13280     /**
13281     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13282     * child nodes before loading.
13283     */
13284     clearOnLoad : true,
13285
13286     /**
13287     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13288     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13289     * Grid query { data : [ .....] }
13290     */
13291     
13292     root : false,
13293      /**
13294     * @cfg {String} queryParam (optional) 
13295     * Name of the query as it will be passed on the querystring (defaults to 'node')
13296     * eg. the request will be ?node=[id]
13297     */
13298     
13299     
13300     queryParam: false,
13301     
13302     /**
13303      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13304      * This is called automatically when a node is expanded, but may be used to reload
13305      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13306      * @param {Roo.tree.TreeNode} node
13307      * @param {Function} callback
13308      */
13309     load : function(node, callback){
13310         if(this.clearOnLoad){
13311             while(node.firstChild){
13312                 node.removeChild(node.firstChild);
13313             }
13314         }
13315         if(node.attributes.children){ // preloaded json children
13316             var cs = node.attributes.children;
13317             for(var i = 0, len = cs.length; i < len; i++){
13318                 node.appendChild(this.createNode(cs[i]));
13319             }
13320             if(typeof callback == "function"){
13321                 callback();
13322             }
13323         }else if(this.dataUrl){
13324             this.requestData(node, callback);
13325         }
13326     },
13327
13328     getParams: function(node){
13329         var buf = [], bp = this.baseParams;
13330         for(var key in bp){
13331             if(typeof bp[key] != "function"){
13332                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13333             }
13334         }
13335         var n = this.queryParam === false ? 'node' : this.queryParam;
13336         buf.push(n + "=", encodeURIComponent(node.id));
13337         return buf.join("");
13338     },
13339
13340     requestData : function(node, callback){
13341         if(this.fireEvent("beforeload", this, node, callback) !== false){
13342             this.transId = Roo.Ajax.request({
13343                 method:this.requestMethod,
13344                 url: this.dataUrl||this.url,
13345                 success: this.handleResponse,
13346                 failure: this.handleFailure,
13347                 scope: this,
13348                 argument: {callback: callback, node: node},
13349                 params: this.getParams(node)
13350             });
13351         }else{
13352             // if the load is cancelled, make sure we notify
13353             // the node that we are done
13354             if(typeof callback == "function"){
13355                 callback();
13356             }
13357         }
13358     },
13359
13360     isLoading : function(){
13361         return this.transId ? true : false;
13362     },
13363
13364     abort : function(){
13365         if(this.isLoading()){
13366             Roo.Ajax.abort(this.transId);
13367         }
13368     },
13369
13370     // private
13371     createNode : function(attr)
13372     {
13373         // apply baseAttrs, nice idea Corey!
13374         if(this.baseAttrs){
13375             Roo.applyIf(attr, this.baseAttrs);
13376         }
13377         if(this.applyLoader !== false){
13378             attr.loader = this;
13379         }
13380         // uiProvider = depreciated..
13381         
13382         if(typeof(attr.uiProvider) == 'string'){
13383            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13384                 /**  eval:var:attr */ eval(attr.uiProvider);
13385         }
13386         if(typeof(this.uiProviders['default']) != 'undefined') {
13387             attr.uiProvider = this.uiProviders['default'];
13388         }
13389         
13390         this.fireEvent('create', this, attr);
13391         
13392         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13393         return(attr.leaf ?
13394                         new Roo.tree.TreeNode(attr) :
13395                         new Roo.tree.AsyncTreeNode(attr));
13396     },
13397
13398     processResponse : function(response, node, callback)
13399     {
13400         var json = response.responseText;
13401         try {
13402             
13403             var o = Roo.decode(json);
13404             
13405             if (this.root === false && typeof(o.success) != undefined) {
13406                 this.root = 'data'; // the default behaviour for list like data..
13407                 }
13408                 
13409             if (this.root !== false &&  !o.success) {
13410                 // it's a failure condition.
13411                 var a = response.argument;
13412                 this.fireEvent("loadexception", this, a.node, response);
13413                 Roo.log("Load failed - should have a handler really");
13414                 return;
13415             }
13416             
13417             
13418             
13419             if (this.root !== false) {
13420                  o = o[this.root];
13421             }
13422             
13423             for(var i = 0, len = o.length; i < len; i++){
13424                 var n = this.createNode(o[i]);
13425                 if(n){
13426                     node.appendChild(n);
13427                 }
13428             }
13429             if(typeof callback == "function"){
13430                 callback(this, node);
13431             }
13432         }catch(e){
13433             this.handleFailure(response);
13434         }
13435     },
13436
13437     handleResponse : function(response){
13438         this.transId = false;
13439         var a = response.argument;
13440         this.processResponse(response, a.node, a.callback);
13441         this.fireEvent("load", this, a.node, response);
13442     },
13443
13444     handleFailure : function(response)
13445     {
13446         // should handle failure better..
13447         this.transId = false;
13448         var a = response.argument;
13449         this.fireEvent("loadexception", this, a.node, response);
13450         if(typeof a.callback == "function"){
13451             a.callback(this, a.node);
13452         }
13453     }
13454 });/*
13455  * Based on:
13456  * Ext JS Library 1.1.1
13457  * Copyright(c) 2006-2007, Ext JS, LLC.
13458  *
13459  * Originally Released Under LGPL - original licence link has changed is not relivant.
13460  *
13461  * Fork - LGPL
13462  * <script type="text/javascript">
13463  */
13464
13465 /**
13466 * @class Roo.tree.TreeFilter
13467 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13468 * @param {TreePanel} tree
13469 * @param {Object} config (optional)
13470  */
13471 Roo.tree.TreeFilter = function(tree, config){
13472     this.tree = tree;
13473     this.filtered = {};
13474     Roo.apply(this, config);
13475 };
13476
13477 Roo.tree.TreeFilter.prototype = {
13478     clearBlank:false,
13479     reverse:false,
13480     autoClear:false,
13481     remove:false,
13482
13483      /**
13484      * Filter the data by a specific attribute.
13485      * @param {String/RegExp} value Either string that the attribute value
13486      * should start with or a RegExp to test against the attribute
13487      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13488      * @param {TreeNode} startNode (optional) The node to start the filter at.
13489      */
13490     filter : function(value, attr, startNode){
13491         attr = attr || "text";
13492         var f;
13493         if(typeof value == "string"){
13494             var vlen = value.length;
13495             // auto clear empty filter
13496             if(vlen == 0 && this.clearBlank){
13497                 this.clear();
13498                 return;
13499             }
13500             value = value.toLowerCase();
13501             f = function(n){
13502                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13503             };
13504         }else if(value.exec){ // regex?
13505             f = function(n){
13506                 return value.test(n.attributes[attr]);
13507             };
13508         }else{
13509             throw 'Illegal filter type, must be string or regex';
13510         }
13511         this.filterBy(f, null, startNode);
13512         },
13513
13514     /**
13515      * Filter by a function. The passed function will be called with each
13516      * node in the tree (or from the startNode). If the function returns true, the node is kept
13517      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13518      * @param {Function} fn The filter function
13519      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13520      */
13521     filterBy : function(fn, scope, startNode){
13522         startNode = startNode || this.tree.root;
13523         if(this.autoClear){
13524             this.clear();
13525         }
13526         var af = this.filtered, rv = this.reverse;
13527         var f = function(n){
13528             if(n == startNode){
13529                 return true;
13530             }
13531             if(af[n.id]){
13532                 return false;
13533             }
13534             var m = fn.call(scope || n, n);
13535             if(!m || rv){
13536                 af[n.id] = n;
13537                 n.ui.hide();
13538                 return false;
13539             }
13540             return true;
13541         };
13542         startNode.cascade(f);
13543         if(this.remove){
13544            for(var id in af){
13545                if(typeof id != "function"){
13546                    var n = af[id];
13547                    if(n && n.parentNode){
13548                        n.parentNode.removeChild(n);
13549                    }
13550                }
13551            }
13552         }
13553     },
13554
13555     /**
13556      * Clears the current filter. Note: with the "remove" option
13557      * set a filter cannot be cleared.
13558      */
13559     clear : function(){
13560         var t = this.tree;
13561         var af = this.filtered;
13562         for(var id in af){
13563             if(typeof id != "function"){
13564                 var n = af[id];
13565                 if(n){
13566                     n.ui.show();
13567                 }
13568             }
13569         }
13570         this.filtered = {};
13571     }
13572 };
13573 /*
13574  * Based on:
13575  * Ext JS Library 1.1.1
13576  * Copyright(c) 2006-2007, Ext JS, LLC.
13577  *
13578  * Originally Released Under LGPL - original licence link has changed is not relivant.
13579  *
13580  * Fork - LGPL
13581  * <script type="text/javascript">
13582  */
13583  
13584
13585 /**
13586  * @class Roo.tree.TreeSorter
13587  * Provides sorting of nodes in a TreePanel
13588  * 
13589  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13590  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13591  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13592  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13593  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13594  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13595  * @constructor
13596  * @param {TreePanel} tree
13597  * @param {Object} config
13598  */
13599 Roo.tree.TreeSorter = function(tree, config){
13600     Roo.apply(this, config);
13601     tree.on("beforechildrenrendered", this.doSort, this);
13602     tree.on("append", this.updateSort, this);
13603     tree.on("insert", this.updateSort, this);
13604     
13605     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13606     var p = this.property || "text";
13607     var sortType = this.sortType;
13608     var fs = this.folderSort;
13609     var cs = this.caseSensitive === true;
13610     var leafAttr = this.leafAttr || 'leaf';
13611
13612     this.sortFn = function(n1, n2){
13613         if(fs){
13614             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13615                 return 1;
13616             }
13617             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13618                 return -1;
13619             }
13620         }
13621         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13622         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13623         if(v1 < v2){
13624                         return dsc ? +1 : -1;
13625                 }else if(v1 > v2){
13626                         return dsc ? -1 : +1;
13627         }else{
13628                 return 0;
13629         }
13630     };
13631 };
13632
13633 Roo.tree.TreeSorter.prototype = {
13634     doSort : function(node){
13635         node.sort(this.sortFn);
13636     },
13637     
13638     compareNodes : function(n1, n2){
13639         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13640     },
13641     
13642     updateSort : function(tree, node){
13643         if(node.childrenRendered){
13644             this.doSort.defer(1, this, [node]);
13645         }
13646     }
13647 };/*
13648  * Based on:
13649  * Ext JS Library 1.1.1
13650  * Copyright(c) 2006-2007, Ext JS, LLC.
13651  *
13652  * Originally Released Under LGPL - original licence link has changed is not relivant.
13653  *
13654  * Fork - LGPL
13655  * <script type="text/javascript">
13656  */
13657
13658 if(Roo.dd.DropZone){
13659     
13660 Roo.tree.TreeDropZone = function(tree, config){
13661     this.allowParentInsert = false;
13662     this.allowContainerDrop = false;
13663     this.appendOnly = false;
13664     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13665     this.tree = tree;
13666     this.lastInsertClass = "x-tree-no-status";
13667     this.dragOverData = {};
13668 };
13669
13670 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13671     ddGroup : "TreeDD",
13672     scroll:  true,
13673     
13674     expandDelay : 1000,
13675     
13676     expandNode : function(node){
13677         if(node.hasChildNodes() && !node.isExpanded()){
13678             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13679         }
13680     },
13681     
13682     queueExpand : function(node){
13683         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13684     },
13685     
13686     cancelExpand : function(){
13687         if(this.expandProcId){
13688             clearTimeout(this.expandProcId);
13689             this.expandProcId = false;
13690         }
13691     },
13692     
13693     isValidDropPoint : function(n, pt, dd, e, data){
13694         if(!n || !data){ return false; }
13695         var targetNode = n.node;
13696         var dropNode = data.node;
13697         // default drop rules
13698         if(!(targetNode && targetNode.isTarget && pt)){
13699             return false;
13700         }
13701         if(pt == "append" && targetNode.allowChildren === false){
13702             return false;
13703         }
13704         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13705             return false;
13706         }
13707         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13708             return false;
13709         }
13710         // reuse the object
13711         var overEvent = this.dragOverData;
13712         overEvent.tree = this.tree;
13713         overEvent.target = targetNode;
13714         overEvent.data = data;
13715         overEvent.point = pt;
13716         overEvent.source = dd;
13717         overEvent.rawEvent = e;
13718         overEvent.dropNode = dropNode;
13719         overEvent.cancel = false;  
13720         var result = this.tree.fireEvent("nodedragover", overEvent);
13721         return overEvent.cancel === false && result !== false;
13722     },
13723     
13724     getDropPoint : function(e, n, dd)
13725     {
13726         var tn = n.node;
13727         if(tn.isRoot){
13728             return tn.allowChildren !== false ? "append" : false; // always append for root
13729         }
13730         var dragEl = n.ddel;
13731         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13732         var y = Roo.lib.Event.getPageY(e);
13733         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13734         
13735         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13736         var noAppend = tn.allowChildren === false;
13737         if(this.appendOnly || tn.parentNode.allowChildren === false){
13738             return noAppend ? false : "append";
13739         }
13740         var noBelow = false;
13741         if(!this.allowParentInsert){
13742             noBelow = tn.hasChildNodes() && tn.isExpanded();
13743         }
13744         var q = (b - t) / (noAppend ? 2 : 3);
13745         if(y >= t && y < (t + q)){
13746             return "above";
13747         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13748             return "below";
13749         }else{
13750             return "append";
13751         }
13752     },
13753     
13754     onNodeEnter : function(n, dd, e, data)
13755     {
13756         this.cancelExpand();
13757     },
13758     
13759     onNodeOver : function(n, dd, e, data)
13760     {
13761        
13762         var pt = this.getDropPoint(e, n, dd);
13763         var node = n.node;
13764         
13765         // auto node expand check
13766         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13767             this.queueExpand(node);
13768         }else if(pt != "append"){
13769             this.cancelExpand();
13770         }
13771         
13772         // set the insert point style on the target node
13773         var returnCls = this.dropNotAllowed;
13774         if(this.isValidDropPoint(n, pt, dd, e, data)){
13775            if(pt){
13776                var el = n.ddel;
13777                var cls;
13778                if(pt == "above"){
13779                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13780                    cls = "x-tree-drag-insert-above";
13781                }else if(pt == "below"){
13782                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13783                    cls = "x-tree-drag-insert-below";
13784                }else{
13785                    returnCls = "x-tree-drop-ok-append";
13786                    cls = "x-tree-drag-append";
13787                }
13788                if(this.lastInsertClass != cls){
13789                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13790                    this.lastInsertClass = cls;
13791                }
13792            }
13793        }
13794        return returnCls;
13795     },
13796     
13797     onNodeOut : function(n, dd, e, data){
13798         
13799         this.cancelExpand();
13800         this.removeDropIndicators(n);
13801     },
13802     
13803     onNodeDrop : function(n, dd, e, data){
13804         var point = this.getDropPoint(e, n, dd);
13805         var targetNode = n.node;
13806         targetNode.ui.startDrop();
13807         if(!this.isValidDropPoint(n, point, dd, e, data)){
13808             targetNode.ui.endDrop();
13809             return false;
13810         }
13811         // first try to find the drop node
13812         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13813         var dropEvent = {
13814             tree : this.tree,
13815             target: targetNode,
13816             data: data,
13817             point: point,
13818             source: dd,
13819             rawEvent: e,
13820             dropNode: dropNode,
13821             cancel: !dropNode   
13822         };
13823         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13824         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13825             targetNode.ui.endDrop();
13826             return false;
13827         }
13828         // allow target changing
13829         targetNode = dropEvent.target;
13830         if(point == "append" && !targetNode.isExpanded()){
13831             targetNode.expand(false, null, function(){
13832                 this.completeDrop(dropEvent);
13833             }.createDelegate(this));
13834         }else{
13835             this.completeDrop(dropEvent);
13836         }
13837         return true;
13838     },
13839     
13840     completeDrop : function(de){
13841         var ns = de.dropNode, p = de.point, t = de.target;
13842         if(!(ns instanceof Array)){
13843             ns = [ns];
13844         }
13845         var n;
13846         for(var i = 0, len = ns.length; i < len; i++){
13847             n = ns[i];
13848             if(p == "above"){
13849                 t.parentNode.insertBefore(n, t);
13850             }else if(p == "below"){
13851                 t.parentNode.insertBefore(n, t.nextSibling);
13852             }else{
13853                 t.appendChild(n);
13854             }
13855         }
13856         n.ui.focus();
13857         if(this.tree.hlDrop){
13858             n.ui.highlight();
13859         }
13860         t.ui.endDrop();
13861         this.tree.fireEvent("nodedrop", de);
13862     },
13863     
13864     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13865         if(this.tree.hlDrop){
13866             dropNode.ui.focus();
13867             dropNode.ui.highlight();
13868         }
13869         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13870     },
13871     
13872     getTree : function(){
13873         return this.tree;
13874     },
13875     
13876     removeDropIndicators : function(n){
13877         if(n && n.ddel){
13878             var el = n.ddel;
13879             Roo.fly(el).removeClass([
13880                     "x-tree-drag-insert-above",
13881                     "x-tree-drag-insert-below",
13882                     "x-tree-drag-append"]);
13883             this.lastInsertClass = "_noclass";
13884         }
13885     },
13886     
13887     beforeDragDrop : function(target, e, id){
13888         this.cancelExpand();
13889         return true;
13890     },
13891     
13892     afterRepair : function(data){
13893         if(data && Roo.enableFx){
13894             data.node.ui.highlight();
13895         }
13896         this.hideProxy();
13897     } 
13898     
13899 });
13900
13901 }
13902 /*
13903  * Based on:
13904  * Ext JS Library 1.1.1
13905  * Copyright(c) 2006-2007, Ext JS, LLC.
13906  *
13907  * Originally Released Under LGPL - original licence link has changed is not relivant.
13908  *
13909  * Fork - LGPL
13910  * <script type="text/javascript">
13911  */
13912  
13913
13914 if(Roo.dd.DragZone){
13915 Roo.tree.TreeDragZone = function(tree, config){
13916     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13917     this.tree = tree;
13918 };
13919
13920 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13921     ddGroup : "TreeDD",
13922    
13923     onBeforeDrag : function(data, e){
13924         var n = data.node;
13925         return n && n.draggable && !n.disabled;
13926     },
13927      
13928     
13929     onInitDrag : function(e){
13930         var data = this.dragData;
13931         this.tree.getSelectionModel().select(data.node);
13932         this.proxy.update("");
13933         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13934         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13935     },
13936     
13937     getRepairXY : function(e, data){
13938         return data.node.ui.getDDRepairXY();
13939     },
13940     
13941     onEndDrag : function(data, e){
13942         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13943         
13944         
13945     },
13946     
13947     onValidDrop : function(dd, e, id){
13948         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13949         this.hideProxy();
13950     },
13951     
13952     beforeInvalidDrop : function(e, id){
13953         // this scrolls the original position back into view
13954         var sm = this.tree.getSelectionModel();
13955         sm.clearSelections();
13956         sm.select(this.dragData.node);
13957     }
13958 });
13959 }/*
13960  * Based on:
13961  * Ext JS Library 1.1.1
13962  * Copyright(c) 2006-2007, Ext JS, LLC.
13963  *
13964  * Originally Released Under LGPL - original licence link has changed is not relivant.
13965  *
13966  * Fork - LGPL
13967  * <script type="text/javascript">
13968  */
13969 /**
13970  * @class Roo.tree.TreeEditor
13971  * @extends Roo.Editor
13972  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
13973  * as the editor field.
13974  * @constructor
13975  * @param {Object} config (used to be the tree panel.)
13976  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
13977  * 
13978  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
13979  * @cfg {Roo.form.TextField} field [required] The field configuration
13980  *
13981  * 
13982  */
13983 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
13984     var tree = config;
13985     var field;
13986     if (oldconfig) { // old style..
13987         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
13988     } else {
13989         // new style..
13990         tree = config.tree;
13991         config.field = config.field  || {};
13992         config.field.xtype = 'TextField';
13993         field = Roo.factory(config.field, Roo.form);
13994     }
13995     config = config || {};
13996     
13997     
13998     this.addEvents({
13999         /**
14000          * @event beforenodeedit
14001          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14002          * false from the handler of this event.
14003          * @param {Editor} this
14004          * @param {Roo.tree.Node} node 
14005          */
14006         "beforenodeedit" : true
14007     });
14008     
14009     //Roo.log(config);
14010     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14011
14012     this.tree = tree;
14013
14014     tree.on('beforeclick', this.beforeNodeClick, this);
14015     tree.getTreeEl().on('mousedown', this.hide, this);
14016     this.on('complete', this.updateNode, this);
14017     this.on('beforestartedit', this.fitToTree, this);
14018     this.on('startedit', this.bindScroll, this, {delay:10});
14019     this.on('specialkey', this.onSpecialKey, this);
14020 };
14021
14022 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14023     /**
14024      * @cfg {String} alignment
14025      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14026      */
14027     alignment: "l-l",
14028     // inherit
14029     autoSize: false,
14030     /**
14031      * @cfg {Boolean} hideEl
14032      * True to hide the bound element while the editor is displayed (defaults to false)
14033      */
14034     hideEl : false,
14035     /**
14036      * @cfg {String} cls
14037      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14038      */
14039     cls: "x-small-editor x-tree-editor",
14040     /**
14041      * @cfg {Boolean} shim
14042      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14043      */
14044     shim:false,
14045     // inherit
14046     shadow:"frame",
14047     /**
14048      * @cfg {Number} maxWidth
14049      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14050      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14051      * scroll and client offsets into account prior to each edit.
14052      */
14053     maxWidth: 250,
14054
14055     editDelay : 350,
14056
14057     // private
14058     fitToTree : function(ed, el){
14059         var td = this.tree.getTreeEl().dom, nd = el.dom;
14060         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14061             td.scrollLeft = nd.offsetLeft;
14062         }
14063         var w = Math.min(
14064                 this.maxWidth,
14065                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14066         this.setSize(w, '');
14067         
14068         return this.fireEvent('beforenodeedit', this, this.editNode);
14069         
14070     },
14071
14072     // private
14073     triggerEdit : function(node){
14074         this.completeEdit();
14075         this.editNode = node;
14076         this.startEdit(node.ui.textNode, node.text);
14077     },
14078
14079     // private
14080     bindScroll : function(){
14081         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14082     },
14083
14084     // private
14085     beforeNodeClick : function(node, e){
14086         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14087         this.lastClick = new Date();
14088         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14089             e.stopEvent();
14090             this.triggerEdit(node);
14091             return false;
14092         }
14093         return true;
14094     },
14095
14096     // private
14097     updateNode : function(ed, value){
14098         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14099         this.editNode.setText(value);
14100     },
14101
14102     // private
14103     onHide : function(){
14104         Roo.tree.TreeEditor.superclass.onHide.call(this);
14105         if(this.editNode){
14106             this.editNode.ui.focus();
14107         }
14108     },
14109
14110     // private
14111     onSpecialKey : function(field, e){
14112         var k = e.getKey();
14113         if(k == e.ESC){
14114             e.stopEvent();
14115             this.cancelEdit();
14116         }else if(k == e.ENTER && !e.hasModifier()){
14117             e.stopEvent();
14118             this.completeEdit();
14119         }
14120     }
14121 });//<Script type="text/javascript">
14122 /*
14123  * Based on:
14124  * Ext JS Library 1.1.1
14125  * Copyright(c) 2006-2007, Ext JS, LLC.
14126  *
14127  * Originally Released Under LGPL - original licence link has changed is not relivant.
14128  *
14129  * Fork - LGPL
14130  * <script type="text/javascript">
14131  */
14132  
14133 /**
14134  * Not documented??? - probably should be...
14135  */
14136
14137 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14138     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14139     
14140     renderElements : function(n, a, targetNode, bulkRender){
14141         //consel.log("renderElements?");
14142         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14143
14144         var t = n.getOwnerTree();
14145         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14146         
14147         var cols = t.columns;
14148         var bw = t.borderWidth;
14149         var c = cols[0];
14150         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14151          var cb = typeof a.checked == "boolean";
14152         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14153         var colcls = 'x-t-' + tid + '-c0';
14154         var buf = [
14155             '<li class="x-tree-node">',
14156             
14157                 
14158                 '<div class="x-tree-node-el ', a.cls,'">',
14159                     // extran...
14160                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14161                 
14162                 
14163                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14164                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14165                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14166                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14167                            (a.iconCls ? ' '+a.iconCls : ''),
14168                            '" unselectable="on" />',
14169                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14170                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14171                              
14172                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14173                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14174                             '<span unselectable="on" qtip="' + tx + '">',
14175                              tx,
14176                              '</span></a>' ,
14177                     '</div>',
14178                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14179                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14180                  ];
14181         for(var i = 1, len = cols.length; i < len; i++){
14182             c = cols[i];
14183             colcls = 'x-t-' + tid + '-c' +i;
14184             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14185             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14186                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14187                       "</div>");
14188          }
14189          
14190          buf.push(
14191             '</a>',
14192             '<div class="x-clear"></div></div>',
14193             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14194             "</li>");
14195         
14196         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14197             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14198                                 n.nextSibling.ui.getEl(), buf.join(""));
14199         }else{
14200             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14201         }
14202         var el = this.wrap.firstChild;
14203         this.elRow = el;
14204         this.elNode = el.firstChild;
14205         this.ranchor = el.childNodes[1];
14206         this.ctNode = this.wrap.childNodes[1];
14207         var cs = el.firstChild.childNodes;
14208         this.indentNode = cs[0];
14209         this.ecNode = cs[1];
14210         this.iconNode = cs[2];
14211         var index = 3;
14212         if(cb){
14213             this.checkbox = cs[3];
14214             index++;
14215         }
14216         this.anchor = cs[index];
14217         
14218         this.textNode = cs[index].firstChild;
14219         
14220         //el.on("click", this.onClick, this);
14221         //el.on("dblclick", this.onDblClick, this);
14222         
14223         
14224        // console.log(this);
14225     },
14226     initEvents : function(){
14227         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14228         
14229             
14230         var a = this.ranchor;
14231
14232         var el = Roo.get(a);
14233
14234         if(Roo.isOpera){ // opera render bug ignores the CSS
14235             el.setStyle("text-decoration", "none");
14236         }
14237
14238         el.on("click", this.onClick, this);
14239         el.on("dblclick", this.onDblClick, this);
14240         el.on("contextmenu", this.onContextMenu, this);
14241         
14242     },
14243     
14244     /*onSelectedChange : function(state){
14245         if(state){
14246             this.focus();
14247             this.addClass("x-tree-selected");
14248         }else{
14249             //this.blur();
14250             this.removeClass("x-tree-selected");
14251         }
14252     },*/
14253     addClass : function(cls){
14254         if(this.elRow){
14255             Roo.fly(this.elRow).addClass(cls);
14256         }
14257         
14258     },
14259     
14260     
14261     removeClass : function(cls){
14262         if(this.elRow){
14263             Roo.fly(this.elRow).removeClass(cls);
14264         }
14265     }
14266
14267     
14268     
14269 });//<Script type="text/javascript">
14270
14271 /*
14272  * Based on:
14273  * Ext JS Library 1.1.1
14274  * Copyright(c) 2006-2007, Ext JS, LLC.
14275  *
14276  * Originally Released Under LGPL - original licence link has changed is not relivant.
14277  *
14278  * Fork - LGPL
14279  * <script type="text/javascript">
14280  */
14281  
14282
14283 /**
14284  * @class Roo.tree.ColumnTree
14285  * @extends Roo.data.TreePanel
14286  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14287  * @cfg {int} borderWidth  compined right/left border allowance
14288  * @constructor
14289  * @param {String/HTMLElement/Element} el The container element
14290  * @param {Object} config
14291  */
14292 Roo.tree.ColumnTree =  function(el, config)
14293 {
14294    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14295    this.addEvents({
14296         /**
14297         * @event resize
14298         * Fire this event on a container when it resizes
14299         * @param {int} w Width
14300         * @param {int} h Height
14301         */
14302        "resize" : true
14303     });
14304     this.on('resize', this.onResize, this);
14305 };
14306
14307 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14308     //lines:false,
14309     
14310     
14311     borderWidth: Roo.isBorderBox ? 0 : 2, 
14312     headEls : false,
14313     
14314     render : function(){
14315         // add the header.....
14316        
14317         Roo.tree.ColumnTree.superclass.render.apply(this);
14318         
14319         this.el.addClass('x-column-tree');
14320         
14321         this.headers = this.el.createChild(
14322             {cls:'x-tree-headers'},this.innerCt.dom);
14323    
14324         var cols = this.columns, c;
14325         var totalWidth = 0;
14326         this.headEls = [];
14327         var  len = cols.length;
14328         for(var i = 0; i < len; i++){
14329              c = cols[i];
14330              totalWidth += c.width;
14331             this.headEls.push(this.headers.createChild({
14332                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14333                  cn: {
14334                      cls:'x-tree-hd-text',
14335                      html: c.header
14336                  },
14337                  style:'width:'+(c.width-this.borderWidth)+'px;'
14338              }));
14339         }
14340         this.headers.createChild({cls:'x-clear'});
14341         // prevent floats from wrapping when clipped
14342         this.headers.setWidth(totalWidth);
14343         //this.innerCt.setWidth(totalWidth);
14344         this.innerCt.setStyle({ overflow: 'auto' });
14345         this.onResize(this.width, this.height);
14346              
14347         
14348     },
14349     onResize : function(w,h)
14350     {
14351         this.height = h;
14352         this.width = w;
14353         // resize cols..
14354         this.innerCt.setWidth(this.width);
14355         this.innerCt.setHeight(this.height-20);
14356         
14357         // headers...
14358         var cols = this.columns, c;
14359         var totalWidth = 0;
14360         var expEl = false;
14361         var len = cols.length;
14362         for(var i = 0; i < len; i++){
14363             c = cols[i];
14364             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14365                 // it's the expander..
14366                 expEl  = this.headEls[i];
14367                 continue;
14368             }
14369             totalWidth += c.width;
14370             
14371         }
14372         if (expEl) {
14373             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14374         }
14375         this.headers.setWidth(w-20);
14376
14377         
14378         
14379         
14380     }
14381 });
14382 /*
14383  * Based on:
14384  * Ext JS Library 1.1.1
14385  * Copyright(c) 2006-2007, Ext JS, LLC.
14386  *
14387  * Originally Released Under LGPL - original licence link has changed is not relivant.
14388  *
14389  * Fork - LGPL
14390  * <script type="text/javascript">
14391  */
14392  
14393 /**
14394  * @class Roo.menu.Menu
14395  * @extends Roo.util.Observable
14396  * @children Roo.menu.BaseItem
14397  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14398  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14399  * @constructor
14400  * Creates a new Menu
14401  * @param {Object} config Configuration options
14402  */
14403 Roo.menu.Menu = function(config){
14404     
14405     Roo.menu.Menu.superclass.constructor.call(this, config);
14406     
14407     this.id = this.id || Roo.id();
14408     this.addEvents({
14409         /**
14410          * @event beforeshow
14411          * Fires before this menu is displayed
14412          * @param {Roo.menu.Menu} this
14413          */
14414         beforeshow : true,
14415         /**
14416          * @event beforehide
14417          * Fires before this menu is hidden
14418          * @param {Roo.menu.Menu} this
14419          */
14420         beforehide : true,
14421         /**
14422          * @event show
14423          * Fires after this menu is displayed
14424          * @param {Roo.menu.Menu} this
14425          */
14426         show : true,
14427         /**
14428          * @event hide
14429          * Fires after this menu is hidden
14430          * @param {Roo.menu.Menu} this
14431          */
14432         hide : true,
14433         /**
14434          * @event click
14435          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14436          * @param {Roo.menu.Menu} this
14437          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14438          * @param {Roo.EventObject} e
14439          */
14440         click : true,
14441         /**
14442          * @event mouseover
14443          * Fires when the mouse is hovering over this menu
14444          * @param {Roo.menu.Menu} this
14445          * @param {Roo.EventObject} e
14446          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14447          */
14448         mouseover : true,
14449         /**
14450          * @event mouseout
14451          * Fires when the mouse exits this menu
14452          * @param {Roo.menu.Menu} this
14453          * @param {Roo.EventObject} e
14454          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14455          */
14456         mouseout : true,
14457         /**
14458          * @event itemclick
14459          * Fires when a menu item contained in this menu is clicked
14460          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14461          * @param {Roo.EventObject} e
14462          */
14463         itemclick: true
14464     });
14465     if (this.registerMenu) {
14466         Roo.menu.MenuMgr.register(this);
14467     }
14468     
14469     var mis = this.items;
14470     this.items = new Roo.util.MixedCollection();
14471     if(mis){
14472         this.add.apply(this, mis);
14473     }
14474 };
14475
14476 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14477     /**
14478      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14479      */
14480     minWidth : 120,
14481     /**
14482      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14483      * for bottom-right shadow (defaults to "sides")
14484      */
14485     shadow : "sides",
14486     /**
14487      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14488      * this menu (defaults to "tl-tr?")
14489      */
14490     subMenuAlign : "tl-tr?",
14491     /**
14492      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14493      * relative to its element of origin (defaults to "tl-bl?")
14494      */
14495     defaultAlign : "tl-bl?",
14496     /**
14497      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14498      */
14499     allowOtherMenus : false,
14500     /**
14501      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14502      */
14503     registerMenu : true,
14504
14505     hidden:true,
14506
14507     // private
14508     render : function(){
14509         if(this.el){
14510             return;
14511         }
14512         var el = this.el = new Roo.Layer({
14513             cls: "x-menu",
14514             shadow:this.shadow,
14515             constrain: false,
14516             parentEl: this.parentEl || document.body,
14517             zindex:15000
14518         });
14519
14520         this.keyNav = new Roo.menu.MenuNav(this);
14521
14522         if(this.plain){
14523             el.addClass("x-menu-plain");
14524         }
14525         if(this.cls){
14526             el.addClass(this.cls);
14527         }
14528         // generic focus element
14529         this.focusEl = el.createChild({
14530             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14531         });
14532         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14533         //disabling touch- as it's causing issues ..
14534         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14535         ul.on('click'   , this.onClick, this);
14536         
14537         
14538         ul.on("mouseover", this.onMouseOver, this);
14539         ul.on("mouseout", this.onMouseOut, this);
14540         this.items.each(function(item){
14541             if (item.hidden) {
14542                 return;
14543             }
14544             
14545             var li = document.createElement("li");
14546             li.className = "x-menu-list-item";
14547             ul.dom.appendChild(li);
14548             item.render(li, this);
14549         }, this);
14550         this.ul = ul;
14551         this.autoWidth();
14552     },
14553
14554     // private
14555     autoWidth : function(){
14556         var el = this.el, ul = this.ul;
14557         if(!el){
14558             return;
14559         }
14560         var w = this.width;
14561         if(w){
14562             el.setWidth(w);
14563         }else if(Roo.isIE){
14564             el.setWidth(this.minWidth);
14565             var t = el.dom.offsetWidth; // force recalc
14566             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14567         }
14568     },
14569
14570     // private
14571     delayAutoWidth : function(){
14572         if(this.rendered){
14573             if(!this.awTask){
14574                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14575             }
14576             this.awTask.delay(20);
14577         }
14578     },
14579
14580     // private
14581     findTargetItem : function(e){
14582         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14583         if(t && t.menuItemId){
14584             return this.items.get(t.menuItemId);
14585         }
14586     },
14587
14588     // private
14589     onClick : function(e){
14590         Roo.log("menu.onClick");
14591         var t = this.findTargetItem(e);
14592         if(!t){
14593             return;
14594         }
14595         Roo.log(e);
14596         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14597             if(t == this.activeItem && t.shouldDeactivate(e)){
14598                 this.activeItem.deactivate();
14599                 delete this.activeItem;
14600                 return;
14601             }
14602             if(t.canActivate){
14603                 this.setActiveItem(t, true);
14604             }
14605             return;
14606             
14607             
14608         }
14609         
14610         t.onClick(e);
14611         this.fireEvent("click", this, t, e);
14612     },
14613
14614     // private
14615     setActiveItem : function(item, autoExpand){
14616         if(item != this.activeItem){
14617             if(this.activeItem){
14618                 this.activeItem.deactivate();
14619             }
14620             this.activeItem = item;
14621             item.activate(autoExpand);
14622         }else if(autoExpand){
14623             item.expandMenu();
14624         }
14625     },
14626
14627     // private
14628     tryActivate : function(start, step){
14629         var items = this.items;
14630         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14631             var item = items.get(i);
14632             if(!item.disabled && item.canActivate){
14633                 this.setActiveItem(item, false);
14634                 return item;
14635             }
14636         }
14637         return false;
14638     },
14639
14640     // private
14641     onMouseOver : function(e){
14642         var t;
14643         if(t = this.findTargetItem(e)){
14644             if(t.canActivate && !t.disabled){
14645                 this.setActiveItem(t, true);
14646             }
14647         }
14648         this.fireEvent("mouseover", this, e, t);
14649     },
14650
14651     // private
14652     onMouseOut : function(e){
14653         var t;
14654         if(t = this.findTargetItem(e)){
14655             if(t == this.activeItem && t.shouldDeactivate(e)){
14656                 this.activeItem.deactivate();
14657                 delete this.activeItem;
14658             }
14659         }
14660         this.fireEvent("mouseout", this, e, t);
14661     },
14662
14663     /**
14664      * Read-only.  Returns true if the menu is currently displayed, else false.
14665      * @type Boolean
14666      */
14667     isVisible : function(){
14668         return this.el && !this.hidden;
14669     },
14670
14671     /**
14672      * Displays this menu relative to another element
14673      * @param {String/HTMLElement/Roo.Element} element The element to align to
14674      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14675      * the element (defaults to this.defaultAlign)
14676      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14677      */
14678     show : function(el, pos, parentMenu){
14679         this.parentMenu = parentMenu;
14680         if(!this.el){
14681             this.render();
14682         }
14683         this.fireEvent("beforeshow", this);
14684         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14685     },
14686
14687     /**
14688      * Displays this menu at a specific xy position
14689      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14690      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14691      */
14692     showAt : function(xy, parentMenu, /* private: */_e){
14693         this.parentMenu = parentMenu;
14694         if(!this.el){
14695             this.render();
14696         }
14697         if(_e !== false){
14698             this.fireEvent("beforeshow", this);
14699             xy = this.el.adjustForConstraints(xy);
14700         }
14701         this.el.setXY(xy);
14702         this.el.show();
14703         this.hidden = false;
14704         this.focus();
14705         this.fireEvent("show", this);
14706     },
14707
14708     focus : function(){
14709         if(!this.hidden){
14710             this.doFocus.defer(50, this);
14711         }
14712     },
14713
14714     doFocus : function(){
14715         if(!this.hidden){
14716             this.focusEl.focus();
14717         }
14718     },
14719
14720     /**
14721      * Hides this menu and optionally all parent menus
14722      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14723      */
14724     hide : function(deep){
14725         if(this.el && this.isVisible()){
14726             this.fireEvent("beforehide", this);
14727             if(this.activeItem){
14728                 this.activeItem.deactivate();
14729                 this.activeItem = null;
14730             }
14731             this.el.hide();
14732             this.hidden = true;
14733             this.fireEvent("hide", this);
14734         }
14735         if(deep === true && this.parentMenu){
14736             this.parentMenu.hide(true);
14737         }
14738     },
14739
14740     /**
14741      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14742      * Any of the following are valid:
14743      * <ul>
14744      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14745      * <li>An HTMLElement object which will be converted to a menu item</li>
14746      * <li>A menu item config object that will be created as a new menu item</li>
14747      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14748      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14749      * </ul>
14750      * Usage:
14751      * <pre><code>
14752 // Create the menu
14753 var menu = new Roo.menu.Menu();
14754
14755 // Create a menu item to add by reference
14756 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14757
14758 // Add a bunch of items at once using different methods.
14759 // Only the last item added will be returned.
14760 var item = menu.add(
14761     menuItem,                // add existing item by ref
14762     'Dynamic Item',          // new TextItem
14763     '-',                     // new separator
14764     { text: 'Config Item' }  // new item by config
14765 );
14766 </code></pre>
14767      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14768      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14769      */
14770     add : function(){
14771         var a = arguments, l = a.length, item;
14772         for(var i = 0; i < l; i++){
14773             var el = a[i];
14774             if ((typeof(el) == "object") && el.xtype && el.xns) {
14775                 el = Roo.factory(el, Roo.menu);
14776             }
14777             
14778             if(el.render){ // some kind of Item
14779                 item = this.addItem(el);
14780             }else if(typeof el == "string"){ // string
14781                 if(el == "separator" || el == "-"){
14782                     item = this.addSeparator();
14783                 }else{
14784                     item = this.addText(el);
14785                 }
14786             }else if(el.tagName || el.el){ // element
14787                 item = this.addElement(el);
14788             }else if(typeof el == "object"){ // must be menu item config?
14789                 item = this.addMenuItem(el);
14790             }
14791         }
14792         return item;
14793     },
14794
14795     /**
14796      * Returns this menu's underlying {@link Roo.Element} object
14797      * @return {Roo.Element} The element
14798      */
14799     getEl : function(){
14800         if(!this.el){
14801             this.render();
14802         }
14803         return this.el;
14804     },
14805
14806     /**
14807      * Adds a separator bar to the menu
14808      * @return {Roo.menu.Item} The menu item that was added
14809      */
14810     addSeparator : function(){
14811         return this.addItem(new Roo.menu.Separator());
14812     },
14813
14814     /**
14815      * Adds an {@link Roo.Element} object to the menu
14816      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14817      * @return {Roo.menu.Item} The menu item that was added
14818      */
14819     addElement : function(el){
14820         return this.addItem(new Roo.menu.BaseItem(el));
14821     },
14822
14823     /**
14824      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14825      * @param {Roo.menu.Item} item The menu item to add
14826      * @return {Roo.menu.Item} The menu item that was added
14827      */
14828     addItem : function(item){
14829         this.items.add(item);
14830         if(this.ul){
14831             var li = document.createElement("li");
14832             li.className = "x-menu-list-item";
14833             this.ul.dom.appendChild(li);
14834             item.render(li, this);
14835             this.delayAutoWidth();
14836         }
14837         return item;
14838     },
14839
14840     /**
14841      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14842      * @param {Object} config A MenuItem config object
14843      * @return {Roo.menu.Item} The menu item that was added
14844      */
14845     addMenuItem : function(config){
14846         if(!(config instanceof Roo.menu.Item)){
14847             if(typeof config.checked == "boolean"){ // must be check menu item config?
14848                 config = new Roo.menu.CheckItem(config);
14849             }else{
14850                 config = new Roo.menu.Item(config);
14851             }
14852         }
14853         return this.addItem(config);
14854     },
14855
14856     /**
14857      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14858      * @param {String} text The text to display in the menu item
14859      * @return {Roo.menu.Item} The menu item that was added
14860      */
14861     addText : function(text){
14862         return this.addItem(new Roo.menu.TextItem({ text : text }));
14863     },
14864
14865     /**
14866      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14867      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14868      * @param {Roo.menu.Item} item The menu item to add
14869      * @return {Roo.menu.Item} The menu item that was added
14870      */
14871     insert : function(index, item){
14872         this.items.insert(index, item);
14873         if(this.ul){
14874             var li = document.createElement("li");
14875             li.className = "x-menu-list-item";
14876             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14877             item.render(li, this);
14878             this.delayAutoWidth();
14879         }
14880         return item;
14881     },
14882
14883     /**
14884      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14885      * @param {Roo.menu.Item} item The menu item to remove
14886      */
14887     remove : function(item){
14888         this.items.removeKey(item.id);
14889         item.destroy();
14890     },
14891
14892     /**
14893      * Removes and destroys all items in the menu
14894      */
14895     removeAll : function(){
14896         var f;
14897         while(f = this.items.first()){
14898             this.remove(f);
14899         }
14900     }
14901 });
14902
14903 // MenuNav is a private utility class used internally by the Menu
14904 Roo.menu.MenuNav = function(menu){
14905     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14906     this.scope = this.menu = menu;
14907 };
14908
14909 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14910     doRelay : function(e, h){
14911         var k = e.getKey();
14912         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14913             this.menu.tryActivate(0, 1);
14914             return false;
14915         }
14916         return h.call(this.scope || this, e, this.menu);
14917     },
14918
14919     up : function(e, m){
14920         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14921             m.tryActivate(m.items.length-1, -1);
14922         }
14923     },
14924
14925     down : function(e, m){
14926         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14927             m.tryActivate(0, 1);
14928         }
14929     },
14930
14931     right : function(e, m){
14932         if(m.activeItem){
14933             m.activeItem.expandMenu(true);
14934         }
14935     },
14936
14937     left : function(e, m){
14938         m.hide();
14939         if(m.parentMenu && m.parentMenu.activeItem){
14940             m.parentMenu.activeItem.activate();
14941         }
14942     },
14943
14944     enter : function(e, m){
14945         if(m.activeItem){
14946             e.stopPropagation();
14947             m.activeItem.onClick(e);
14948             m.fireEvent("click", this, m.activeItem);
14949             return true;
14950         }
14951     }
14952 });/*
14953  * Based on:
14954  * Ext JS Library 1.1.1
14955  * Copyright(c) 2006-2007, Ext JS, LLC.
14956  *
14957  * Originally Released Under LGPL - original licence link has changed is not relivant.
14958  *
14959  * Fork - LGPL
14960  * <script type="text/javascript">
14961  */
14962  
14963 /**
14964  * @class Roo.menu.MenuMgr
14965  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14966  * @singleton
14967  */
14968 Roo.menu.MenuMgr = function(){
14969    var menus, active, groups = {}, attached = false, lastShow = new Date();
14970
14971    // private - called when first menu is created
14972    function init(){
14973        menus = {};
14974        active = new Roo.util.MixedCollection();
14975        Roo.get(document).addKeyListener(27, function(){
14976            if(active.length > 0){
14977                hideAll();
14978            }
14979        });
14980    }
14981
14982    // private
14983    function hideAll(){
14984        if(active && active.length > 0){
14985            var c = active.clone();
14986            c.each(function(m){
14987                m.hide();
14988            });
14989        }
14990    }
14991
14992    // private
14993    function onHide(m){
14994        active.remove(m);
14995        if(active.length < 1){
14996            Roo.get(document).un("mousedown", onMouseDown);
14997            attached = false;
14998        }
14999    }
15000
15001    // private
15002    function onShow(m){
15003        var last = active.last();
15004        lastShow = new Date();
15005        active.add(m);
15006        if(!attached){
15007            Roo.get(document).on("mousedown", onMouseDown);
15008            attached = true;
15009        }
15010        if(m.parentMenu){
15011           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15012           m.parentMenu.activeChild = m;
15013        }else if(last && last.isVisible()){
15014           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15015        }
15016    }
15017
15018    // private
15019    function onBeforeHide(m){
15020        if(m.activeChild){
15021            m.activeChild.hide();
15022        }
15023        if(m.autoHideTimer){
15024            clearTimeout(m.autoHideTimer);
15025            delete m.autoHideTimer;
15026        }
15027    }
15028
15029    // private
15030    function onBeforeShow(m){
15031        var pm = m.parentMenu;
15032        if(!pm && !m.allowOtherMenus){
15033            hideAll();
15034        }else if(pm && pm.activeChild && active != m){
15035            pm.activeChild.hide();
15036        }
15037    }
15038
15039    // private
15040    function onMouseDown(e){
15041        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15042            hideAll();
15043        }
15044    }
15045
15046    // private
15047    function onBeforeCheck(mi, state){
15048        if(state){
15049            var g = groups[mi.group];
15050            for(var i = 0, l = g.length; i < l; i++){
15051                if(g[i] != mi){
15052                    g[i].setChecked(false);
15053                }
15054            }
15055        }
15056    }
15057
15058    return {
15059
15060        /**
15061         * Hides all menus that are currently visible
15062         */
15063        hideAll : function(){
15064             hideAll();  
15065        },
15066
15067        // private
15068        register : function(menu){
15069            if(!menus){
15070                init();
15071            }
15072            menus[menu.id] = menu;
15073            menu.on("beforehide", onBeforeHide);
15074            menu.on("hide", onHide);
15075            menu.on("beforeshow", onBeforeShow);
15076            menu.on("show", onShow);
15077            var g = menu.group;
15078            if(g && menu.events["checkchange"]){
15079                if(!groups[g]){
15080                    groups[g] = [];
15081                }
15082                groups[g].push(menu);
15083                menu.on("checkchange", onCheck);
15084            }
15085        },
15086
15087         /**
15088          * Returns a {@link Roo.menu.Menu} object
15089          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15090          * be used to generate and return a new Menu instance.
15091          */
15092        get : function(menu){
15093            if(typeof menu == "string"){ // menu id
15094                return menus[menu];
15095            }else if(menu.events){  // menu instance
15096                return menu;
15097            }else if(typeof menu.length == 'number'){ // array of menu items?
15098                return new Roo.menu.Menu({items:menu});
15099            }else{ // otherwise, must be a config
15100                return new Roo.menu.Menu(menu);
15101            }
15102        },
15103
15104        // private
15105        unregister : function(menu){
15106            delete menus[menu.id];
15107            menu.un("beforehide", onBeforeHide);
15108            menu.un("hide", onHide);
15109            menu.un("beforeshow", onBeforeShow);
15110            menu.un("show", onShow);
15111            var g = menu.group;
15112            if(g && menu.events["checkchange"]){
15113                groups[g].remove(menu);
15114                menu.un("checkchange", onCheck);
15115            }
15116        },
15117
15118        // private
15119        registerCheckable : function(menuItem){
15120            var g = menuItem.group;
15121            if(g){
15122                if(!groups[g]){
15123                    groups[g] = [];
15124                }
15125                groups[g].push(menuItem);
15126                menuItem.on("beforecheckchange", onBeforeCheck);
15127            }
15128        },
15129
15130        // private
15131        unregisterCheckable : function(menuItem){
15132            var g = menuItem.group;
15133            if(g){
15134                groups[g].remove(menuItem);
15135                menuItem.un("beforecheckchange", onBeforeCheck);
15136            }
15137        }
15138    };
15139 }();/*
15140  * Based on:
15141  * Ext JS Library 1.1.1
15142  * Copyright(c) 2006-2007, Ext JS, LLC.
15143  *
15144  * Originally Released Under LGPL - original licence link has changed is not relivant.
15145  *
15146  * Fork - LGPL
15147  * <script type="text/javascript">
15148  */
15149  
15150
15151 /**
15152  * @class Roo.menu.BaseItem
15153  * @extends Roo.Component
15154  * @abstract
15155  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15156  * management and base configuration options shared by all menu components.
15157  * @constructor
15158  * Creates a new BaseItem
15159  * @param {Object} config Configuration options
15160  */
15161 Roo.menu.BaseItem = function(config){
15162     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15163
15164     this.addEvents({
15165         /**
15166          * @event click
15167          * Fires when this item is clicked
15168          * @param {Roo.menu.BaseItem} this
15169          * @param {Roo.EventObject} e
15170          */
15171         click: true,
15172         /**
15173          * @event activate
15174          * Fires when this item is activated
15175          * @param {Roo.menu.BaseItem} this
15176          */
15177         activate : true,
15178         /**
15179          * @event deactivate
15180          * Fires when this item is deactivated
15181          * @param {Roo.menu.BaseItem} this
15182          */
15183         deactivate : true
15184     });
15185
15186     if(this.handler){
15187         this.on("click", this.handler, this.scope, true);
15188     }
15189 };
15190
15191 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15192     /**
15193      * @cfg {Function} handler
15194      * A function that will handle the click event of this menu item (defaults to undefined)
15195      */
15196     /**
15197      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15198      */
15199     canActivate : false,
15200     
15201      /**
15202      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15203      */
15204     hidden: false,
15205     
15206     /**
15207      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15208      */
15209     activeClass : "x-menu-item-active",
15210     /**
15211      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15212      */
15213     hideOnClick : true,
15214     /**
15215      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15216      */
15217     hideDelay : 100,
15218
15219     // private
15220     ctype: "Roo.menu.BaseItem",
15221
15222     // private
15223     actionMode : "container",
15224
15225     // private
15226     render : function(container, parentMenu){
15227         this.parentMenu = parentMenu;
15228         Roo.menu.BaseItem.superclass.render.call(this, container);
15229         this.container.menuItemId = this.id;
15230     },
15231
15232     // private
15233     onRender : function(container, position){
15234         this.el = Roo.get(this.el);
15235         container.dom.appendChild(this.el.dom);
15236     },
15237
15238     // private
15239     onClick : function(e){
15240         if(!this.disabled && this.fireEvent("click", this, e) !== false
15241                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15242             this.handleClick(e);
15243         }else{
15244             e.stopEvent();
15245         }
15246     },
15247
15248     // private
15249     activate : function(){
15250         if(this.disabled){
15251             return false;
15252         }
15253         var li = this.container;
15254         li.addClass(this.activeClass);
15255         this.region = li.getRegion().adjust(2, 2, -2, -2);
15256         this.fireEvent("activate", this);
15257         return true;
15258     },
15259
15260     // private
15261     deactivate : function(){
15262         this.container.removeClass(this.activeClass);
15263         this.fireEvent("deactivate", this);
15264     },
15265
15266     // private
15267     shouldDeactivate : function(e){
15268         return !this.region || !this.region.contains(e.getPoint());
15269     },
15270
15271     // private
15272     handleClick : function(e){
15273         if(this.hideOnClick){
15274             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15275         }
15276     },
15277
15278     // private
15279     expandMenu : function(autoActivate){
15280         // do nothing
15281     },
15282
15283     // private
15284     hideMenu : function(){
15285         // do nothing
15286     }
15287 });/*
15288  * Based on:
15289  * Ext JS Library 1.1.1
15290  * Copyright(c) 2006-2007, Ext JS, LLC.
15291  *
15292  * Originally Released Under LGPL - original licence link has changed is not relivant.
15293  *
15294  * Fork - LGPL
15295  * <script type="text/javascript">
15296  */
15297  
15298 /**
15299  * @class Roo.menu.Adapter
15300  * @extends Roo.menu.BaseItem
15301  * @abstract
15302  * 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.
15303  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15304  * @constructor
15305  * Creates a new Adapter
15306  * @param {Object} config Configuration options
15307  */
15308 Roo.menu.Adapter = function(component, config){
15309     Roo.menu.Adapter.superclass.constructor.call(this, config);
15310     this.component = component;
15311 };
15312 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15313     // private
15314     canActivate : true,
15315
15316     // private
15317     onRender : function(container, position){
15318         this.component.render(container);
15319         this.el = this.component.getEl();
15320     },
15321
15322     // private
15323     activate : function(){
15324         if(this.disabled){
15325             return false;
15326         }
15327         this.component.focus();
15328         this.fireEvent("activate", this);
15329         return true;
15330     },
15331
15332     // private
15333     deactivate : function(){
15334         this.fireEvent("deactivate", this);
15335     },
15336
15337     // private
15338     disable : function(){
15339         this.component.disable();
15340         Roo.menu.Adapter.superclass.disable.call(this);
15341     },
15342
15343     // private
15344     enable : function(){
15345         this.component.enable();
15346         Roo.menu.Adapter.superclass.enable.call(this);
15347     }
15348 });/*
15349  * Based on:
15350  * Ext JS Library 1.1.1
15351  * Copyright(c) 2006-2007, Ext JS, LLC.
15352  *
15353  * Originally Released Under LGPL - original licence link has changed is not relivant.
15354  *
15355  * Fork - LGPL
15356  * <script type="text/javascript">
15357  */
15358
15359 /**
15360  * @class Roo.menu.TextItem
15361  * @extends Roo.menu.BaseItem
15362  * Adds a static text string to a menu, usually used as either a heading or group separator.
15363  * Note: old style constructor with text is still supported.
15364  * 
15365  * @constructor
15366  * Creates a new TextItem
15367  * @param {Object} cfg Configuration
15368  */
15369 Roo.menu.TextItem = function(cfg){
15370     if (typeof(cfg) == 'string') {
15371         this.text = cfg;
15372     } else {
15373         Roo.apply(this,cfg);
15374     }
15375     
15376     Roo.menu.TextItem.superclass.constructor.call(this);
15377 };
15378
15379 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15380     /**
15381      * @cfg {String} text Text to show on item.
15382      */
15383     text : '',
15384     
15385     /**
15386      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15387      */
15388     hideOnClick : false,
15389     /**
15390      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15391      */
15392     itemCls : "x-menu-text",
15393
15394     // private
15395     onRender : function(){
15396         var s = document.createElement("span");
15397         s.className = this.itemCls;
15398         s.innerHTML = this.text;
15399         this.el = s;
15400         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15401     }
15402 });/*
15403  * Based on:
15404  * Ext JS Library 1.1.1
15405  * Copyright(c) 2006-2007, Ext JS, LLC.
15406  *
15407  * Originally Released Under LGPL - original licence link has changed is not relivant.
15408  *
15409  * Fork - LGPL
15410  * <script type="text/javascript">
15411  */
15412
15413 /**
15414  * @class Roo.menu.Separator
15415  * @extends Roo.menu.BaseItem
15416  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15417  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15418  * @constructor
15419  * @param {Object} config Configuration options
15420  */
15421 Roo.menu.Separator = function(config){
15422     Roo.menu.Separator.superclass.constructor.call(this, config);
15423 };
15424
15425 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15426     /**
15427      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15428      */
15429     itemCls : "x-menu-sep",
15430     /**
15431      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15432      */
15433     hideOnClick : false,
15434
15435     // private
15436     onRender : function(li){
15437         var s = document.createElement("span");
15438         s.className = this.itemCls;
15439         s.innerHTML = "&#160;";
15440         this.el = s;
15441         li.addClass("x-menu-sep-li");
15442         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15443     }
15444 });/*
15445  * Based on:
15446  * Ext JS Library 1.1.1
15447  * Copyright(c) 2006-2007, Ext JS, LLC.
15448  *
15449  * Originally Released Under LGPL - original licence link has changed is not relivant.
15450  *
15451  * Fork - LGPL
15452  * <script type="text/javascript">
15453  */
15454 /**
15455  * @class Roo.menu.Item
15456  * @extends Roo.menu.BaseItem
15457  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15458  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15459  * activation and click handling.
15460  * @constructor
15461  * Creates a new Item
15462  * @param {Object} config Configuration options
15463  */
15464 Roo.menu.Item = function(config){
15465     Roo.menu.Item.superclass.constructor.call(this, config);
15466     if(this.menu){
15467         this.menu = Roo.menu.MenuMgr.get(this.menu);
15468     }
15469 };
15470 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15471     /**
15472      * @cfg {Roo.menu.Menu} menu
15473      * A Sub menu
15474      */
15475     /**
15476      * @cfg {String} text
15477      * The text to show on the menu item.
15478      */
15479     text: '',
15480      /**
15481      * @cfg {String} HTML to render in menu
15482      * The text to show on the menu item (HTML version).
15483      */
15484     html: '',
15485     /**
15486      * @cfg {String} icon
15487      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15488      */
15489     icon: undefined,
15490     /**
15491      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15492      */
15493     itemCls : "x-menu-item",
15494     /**
15495      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15496      */
15497     canActivate : true,
15498     /**
15499      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15500      */
15501     showDelay: 200,
15502     // doc'd in BaseItem
15503     hideDelay: 200,
15504
15505     // private
15506     ctype: "Roo.menu.Item",
15507     
15508     // private
15509     onRender : function(container, position){
15510         var el = document.createElement("a");
15511         el.hideFocus = true;
15512         el.unselectable = "on";
15513         el.href = this.href || "#";
15514         if(this.hrefTarget){
15515             el.target = this.hrefTarget;
15516         }
15517         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15518         
15519         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15520         
15521         el.innerHTML = String.format(
15522                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15523                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15524         this.el = el;
15525         Roo.menu.Item.superclass.onRender.call(this, container, position);
15526     },
15527
15528     /**
15529      * Sets the text to display in this menu item
15530      * @param {String} text The text to display
15531      * @param {Boolean} isHTML true to indicate text is pure html.
15532      */
15533     setText : function(text, isHTML){
15534         if (isHTML) {
15535             this.html = text;
15536         } else {
15537             this.text = text;
15538             this.html = '';
15539         }
15540         if(this.rendered){
15541             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15542      
15543             this.el.update(String.format(
15544                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15545                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15546             this.parentMenu.autoWidth();
15547         }
15548     },
15549
15550     // private
15551     handleClick : function(e){
15552         if(!this.href){ // if no link defined, stop the event automatically
15553             e.stopEvent();
15554         }
15555         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15556     },
15557
15558     // private
15559     activate : function(autoExpand){
15560         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15561             this.focus();
15562             if(autoExpand){
15563                 this.expandMenu();
15564             }
15565         }
15566         return true;
15567     },
15568
15569     // private
15570     shouldDeactivate : function(e){
15571         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15572             if(this.menu && this.menu.isVisible()){
15573                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15574             }
15575             return true;
15576         }
15577         return false;
15578     },
15579
15580     // private
15581     deactivate : function(){
15582         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15583         this.hideMenu();
15584     },
15585
15586     // private
15587     expandMenu : function(autoActivate){
15588         if(!this.disabled && this.menu){
15589             clearTimeout(this.hideTimer);
15590             delete this.hideTimer;
15591             if(!this.menu.isVisible() && !this.showTimer){
15592                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15593             }else if (this.menu.isVisible() && autoActivate){
15594                 this.menu.tryActivate(0, 1);
15595             }
15596         }
15597     },
15598
15599     // private
15600     deferExpand : function(autoActivate){
15601         delete this.showTimer;
15602         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15603         if(autoActivate){
15604             this.menu.tryActivate(0, 1);
15605         }
15606     },
15607
15608     // private
15609     hideMenu : function(){
15610         clearTimeout(this.showTimer);
15611         delete this.showTimer;
15612         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15613             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15614         }
15615     },
15616
15617     // private
15618     deferHide : function(){
15619         delete this.hideTimer;
15620         this.menu.hide();
15621     }
15622 });/*
15623  * Based on:
15624  * Ext JS Library 1.1.1
15625  * Copyright(c) 2006-2007, Ext JS, LLC.
15626  *
15627  * Originally Released Under LGPL - original licence link has changed is not relivant.
15628  *
15629  * Fork - LGPL
15630  * <script type="text/javascript">
15631  */
15632  
15633 /**
15634  * @class Roo.menu.CheckItem
15635  * @extends Roo.menu.Item
15636  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15637  * @constructor
15638  * Creates a new CheckItem
15639  * @param {Object} config Configuration options
15640  */
15641 Roo.menu.CheckItem = function(config){
15642     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15643     this.addEvents({
15644         /**
15645          * @event beforecheckchange
15646          * Fires before the checked value is set, providing an opportunity to cancel if needed
15647          * @param {Roo.menu.CheckItem} this
15648          * @param {Boolean} checked The new checked value that will be set
15649          */
15650         "beforecheckchange" : true,
15651         /**
15652          * @event checkchange
15653          * Fires after the checked value has been set
15654          * @param {Roo.menu.CheckItem} this
15655          * @param {Boolean} checked The checked value that was set
15656          */
15657         "checkchange" : true
15658     });
15659     if(this.checkHandler){
15660         this.on('checkchange', this.checkHandler, this.scope);
15661     }
15662 };
15663 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15664     /**
15665      * @cfg {String} group
15666      * All check items with the same group name will automatically be grouped into a single-select
15667      * radio button group (defaults to '')
15668      */
15669     /**
15670      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15671      */
15672     itemCls : "x-menu-item x-menu-check-item",
15673     /**
15674      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15675      */
15676     groupClass : "x-menu-group-item",
15677
15678     /**
15679      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15680      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15681      * initialized with checked = true will be rendered as checked.
15682      */
15683     checked: false,
15684
15685     // private
15686     ctype: "Roo.menu.CheckItem",
15687
15688     // private
15689     onRender : function(c){
15690         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15691         if(this.group){
15692             this.el.addClass(this.groupClass);
15693         }
15694         Roo.menu.MenuMgr.registerCheckable(this);
15695         if(this.checked){
15696             this.checked = false;
15697             this.setChecked(true, true);
15698         }
15699     },
15700
15701     // private
15702     destroy : function(){
15703         if(this.rendered){
15704             Roo.menu.MenuMgr.unregisterCheckable(this);
15705         }
15706         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15707     },
15708
15709     /**
15710      * Set the checked state of this item
15711      * @param {Boolean} checked The new checked value
15712      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15713      */
15714     setChecked : function(state, suppressEvent){
15715         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15716             if(this.container){
15717                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15718             }
15719             this.checked = state;
15720             if(suppressEvent !== true){
15721                 this.fireEvent("checkchange", this, state);
15722             }
15723         }
15724     },
15725
15726     // private
15727     handleClick : function(e){
15728        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15729            this.setChecked(!this.checked);
15730        }
15731        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15732     }
15733 });/*
15734  * Based on:
15735  * Ext JS Library 1.1.1
15736  * Copyright(c) 2006-2007, Ext JS, LLC.
15737  *
15738  * Originally Released Under LGPL - original licence link has changed is not relivant.
15739  *
15740  * Fork - LGPL
15741  * <script type="text/javascript">
15742  */
15743  
15744 /**
15745  * @class Roo.menu.DateItem
15746  * @extends Roo.menu.Adapter
15747  * A menu item that wraps the {@link Roo.DatPicker} component.
15748  * @constructor
15749  * Creates a new DateItem
15750  * @param {Object} config Configuration options
15751  */
15752 Roo.menu.DateItem = function(config){
15753     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15754     /** The Roo.DatePicker object @type Roo.DatePicker */
15755     this.picker = this.component;
15756     this.addEvents({select: true});
15757     
15758     this.picker.on("render", function(picker){
15759         picker.getEl().swallowEvent("click");
15760         picker.container.addClass("x-menu-date-item");
15761     });
15762
15763     this.picker.on("select", this.onSelect, this);
15764 };
15765
15766 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15767     // private
15768     onSelect : function(picker, date){
15769         this.fireEvent("select", this, date, picker);
15770         Roo.menu.DateItem.superclass.handleClick.call(this);
15771     }
15772 });/*
15773  * Based on:
15774  * Ext JS Library 1.1.1
15775  * Copyright(c) 2006-2007, Ext JS, LLC.
15776  *
15777  * Originally Released Under LGPL - original licence link has changed is not relivant.
15778  *
15779  * Fork - LGPL
15780  * <script type="text/javascript">
15781  */
15782  
15783 /**
15784  * @class Roo.menu.ColorItem
15785  * @extends Roo.menu.Adapter
15786  * A menu item that wraps the {@link Roo.ColorPalette} component.
15787  * @constructor
15788  * Creates a new ColorItem
15789  * @param {Object} config Configuration options
15790  */
15791 Roo.menu.ColorItem = function(config){
15792     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15793     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15794     this.palette = this.component;
15795     this.relayEvents(this.palette, ["select"]);
15796     if(this.selectHandler){
15797         this.on('select', this.selectHandler, this.scope);
15798     }
15799 };
15800 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15801  * Based on:
15802  * Ext JS Library 1.1.1
15803  * Copyright(c) 2006-2007, Ext JS, LLC.
15804  *
15805  * Originally Released Under LGPL - original licence link has changed is not relivant.
15806  *
15807  * Fork - LGPL
15808  * <script type="text/javascript">
15809  */
15810  
15811
15812 /**
15813  * @class Roo.menu.DateMenu
15814  * @extends Roo.menu.Menu
15815  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15816  * @constructor
15817  * Creates a new DateMenu
15818  * @param {Object} config Configuration options
15819  */
15820 Roo.menu.DateMenu = function(config){
15821     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15822     this.plain = true;
15823     var di = new Roo.menu.DateItem(config);
15824     this.add(di);
15825     /**
15826      * The {@link Roo.DatePicker} instance for this DateMenu
15827      * @type DatePicker
15828      */
15829     this.picker = di.picker;
15830     /**
15831      * @event select
15832      * @param {DatePicker} picker
15833      * @param {Date} date
15834      */
15835     this.relayEvents(di, ["select"]);
15836     this.on('beforeshow', function(){
15837         if(this.picker){
15838             this.picker.hideMonthPicker(false);
15839         }
15840     }, this);
15841 };
15842 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15843     cls:'x-date-menu'
15844 });/*
15845  * Based on:
15846  * Ext JS Library 1.1.1
15847  * Copyright(c) 2006-2007, Ext JS, LLC.
15848  *
15849  * Originally Released Under LGPL - original licence link has changed is not relivant.
15850  *
15851  * Fork - LGPL
15852  * <script type="text/javascript">
15853  */
15854  
15855
15856 /**
15857  * @class Roo.menu.ColorMenu
15858  * @extends Roo.menu.Menu
15859  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15860  * @constructor
15861  * Creates a new ColorMenu
15862  * @param {Object} config Configuration options
15863  */
15864 Roo.menu.ColorMenu = function(config){
15865     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15866     this.plain = true;
15867     var ci = new Roo.menu.ColorItem(config);
15868     this.add(ci);
15869     /**
15870      * The {@link Roo.ColorPalette} instance for this ColorMenu
15871      * @type ColorPalette
15872      */
15873     this.palette = ci.palette;
15874     /**
15875      * @event select
15876      * @param {ColorPalette} palette
15877      * @param {String} color
15878      */
15879     this.relayEvents(ci, ["select"]);
15880 };
15881 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15882  * Based on:
15883  * Ext JS Library 1.1.1
15884  * Copyright(c) 2006-2007, Ext JS, LLC.
15885  *
15886  * Originally Released Under LGPL - original licence link has changed is not relivant.
15887  *
15888  * Fork - LGPL
15889  * <script type="text/javascript">
15890  */
15891  
15892 /**
15893  * @class Roo.form.TextItem
15894  * @extends Roo.BoxComponent
15895  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15896  * @constructor
15897  * Creates a new TextItem
15898  * @param {Object} config Configuration options
15899  */
15900 Roo.form.TextItem = function(config){
15901     Roo.form.TextItem.superclass.constructor.call(this, config);
15902 };
15903
15904 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15905     
15906     /**
15907      * @cfg {String} tag the tag for this item (default div)
15908      */
15909     tag : 'div',
15910     /**
15911      * @cfg {String} html the content for this item
15912      */
15913     html : '',
15914     
15915     getAutoCreate : function()
15916     {
15917         var cfg = {
15918             id: this.id,
15919             tag: this.tag,
15920             html: this.html,
15921             cls: 'x-form-item'
15922         };
15923         
15924         return cfg;
15925         
15926     },
15927     
15928     onRender : function(ct, position)
15929     {
15930         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15931         
15932         if(!this.el){
15933             var cfg = this.getAutoCreate();
15934             if(!cfg.name){
15935                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15936             }
15937             if (!cfg.name.length) {
15938                 delete cfg.name;
15939             }
15940             this.el = ct.createChild(cfg, position);
15941         }
15942     },
15943     /*
15944      * setHTML
15945      * @param {String} html update the Contents of the element.
15946      */
15947     setHTML : function(html)
15948     {
15949         this.fieldEl.dom.innerHTML = html;
15950     }
15951     
15952 });/*
15953  * Based on:
15954  * Ext JS Library 1.1.1
15955  * Copyright(c) 2006-2007, Ext JS, LLC.
15956  *
15957  * Originally Released Under LGPL - original licence link has changed is not relivant.
15958  *
15959  * Fork - LGPL
15960  * <script type="text/javascript">
15961  */
15962  
15963 /**
15964  * @class Roo.form.Field
15965  * @extends Roo.BoxComponent
15966  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15967  * @constructor
15968  * Creates a new Field
15969  * @param {Object} config Configuration options
15970  */
15971 Roo.form.Field = function(config){
15972     Roo.form.Field.superclass.constructor.call(this, config);
15973 };
15974
15975 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
15976     /**
15977      * @cfg {String} fieldLabel Label to use when rendering a form.
15978      */
15979        /**
15980      * @cfg {String} qtip Mouse over tip
15981      */
15982      
15983     /**
15984      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
15985      */
15986     invalidClass : "x-form-invalid",
15987     /**
15988      * @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")
15989      */
15990     invalidText : "The value in this field is invalid",
15991     /**
15992      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
15993      */
15994     focusClass : "x-form-focus",
15995     /**
15996      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
15997       automatic validation (defaults to "keyup").
15998      */
15999     validationEvent : "keyup",
16000     /**
16001      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16002      */
16003     validateOnBlur : true,
16004     /**
16005      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16006      */
16007     validationDelay : 250,
16008     /**
16009      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16010      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16011      */
16012     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16013     /**
16014      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16015      */
16016     fieldClass : "x-form-field",
16017     /**
16018      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16019      *<pre>
16020 Value         Description
16021 -----------   ----------------------------------------------------------------------
16022 qtip          Display a quick tip when the user hovers over the field
16023 title         Display a default browser title attribute popup
16024 under         Add a block div beneath the field containing the error text
16025 side          Add an error icon to the right of the field with a popup on hover
16026 [element id]  Add the error text directly to the innerHTML of the specified element
16027 </pre>
16028      */
16029     msgTarget : 'qtip',
16030     /**
16031      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16032      */
16033     msgFx : 'normal',
16034
16035     /**
16036      * @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.
16037      */
16038     readOnly : false,
16039
16040     /**
16041      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16042      */
16043     disabled : false,
16044
16045     /**
16046      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16047      */
16048     inputType : undefined,
16049     
16050     /**
16051      * @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).
16052          */
16053         tabIndex : undefined,
16054         
16055     // private
16056     isFormField : true,
16057
16058     // private
16059     hasFocus : false,
16060     /**
16061      * @property {Roo.Element} fieldEl
16062      * Element Containing the rendered Field (with label etc.)
16063      */
16064     /**
16065      * @cfg {Mixed} value A value to initialize this field with.
16066      */
16067     value : undefined,
16068
16069     /**
16070      * @cfg {String} name The field's HTML name attribute.
16071      */
16072     /**
16073      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16074      */
16075     // private
16076     loadedValue : false,
16077      
16078      
16079         // private ??
16080         initComponent : function(){
16081         Roo.form.Field.superclass.initComponent.call(this);
16082         this.addEvents({
16083             /**
16084              * @event focus
16085              * Fires when this field receives input focus.
16086              * @param {Roo.form.Field} this
16087              */
16088             focus : true,
16089             /**
16090              * @event blur
16091              * Fires when this field loses input focus.
16092              * @param {Roo.form.Field} this
16093              */
16094             blur : true,
16095             /**
16096              * @event specialkey
16097              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16098              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16099              * @param {Roo.form.Field} this
16100              * @param {Roo.EventObject} e The event object
16101              */
16102             specialkey : true,
16103             /**
16104              * @event change
16105              * Fires just before the field blurs if the field value has changed.
16106              * @param {Roo.form.Field} this
16107              * @param {Mixed} newValue The new value
16108              * @param {Mixed} oldValue The original value
16109              */
16110             change : true,
16111             /**
16112              * @event invalid
16113              * Fires after the field has been marked as invalid.
16114              * @param {Roo.form.Field} this
16115              * @param {String} msg The validation message
16116              */
16117             invalid : true,
16118             /**
16119              * @event valid
16120              * Fires after the field has been validated with no errors.
16121              * @param {Roo.form.Field} this
16122              */
16123             valid : true,
16124              /**
16125              * @event keyup
16126              * Fires after the key up
16127              * @param {Roo.form.Field} this
16128              * @param {Roo.EventObject}  e The event Object
16129              */
16130             keyup : true
16131         });
16132     },
16133
16134     /**
16135      * Returns the name attribute of the field if available
16136      * @return {String} name The field name
16137      */
16138     getName: function(){
16139          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16140     },
16141
16142     // private
16143     onRender : function(ct, position){
16144         Roo.form.Field.superclass.onRender.call(this, ct, position);
16145         if(!this.el){
16146             var cfg = this.getAutoCreate();
16147             if(!cfg.name){
16148                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16149             }
16150             if (!cfg.name.length) {
16151                 delete cfg.name;
16152             }
16153             if(this.inputType){
16154                 cfg.type = this.inputType;
16155             }
16156             this.el = ct.createChild(cfg, position);
16157         }
16158         var type = this.el.dom.type;
16159         if(type){
16160             if(type == 'password'){
16161                 type = 'text';
16162             }
16163             this.el.addClass('x-form-'+type);
16164         }
16165         if(this.readOnly){
16166             this.el.dom.readOnly = true;
16167         }
16168         if(this.tabIndex !== undefined){
16169             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16170         }
16171
16172         this.el.addClass([this.fieldClass, this.cls]);
16173         this.initValue();
16174     },
16175
16176     /**
16177      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16178      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16179      * @return {Roo.form.Field} this
16180      */
16181     applyTo : function(target){
16182         this.allowDomMove = false;
16183         this.el = Roo.get(target);
16184         this.render(this.el.dom.parentNode);
16185         return this;
16186     },
16187
16188     // private
16189     initValue : function(){
16190         if(this.value !== undefined){
16191             this.setValue(this.value);
16192         }else if(this.el.dom.value.length > 0){
16193             this.setValue(this.el.dom.value);
16194         }
16195     },
16196
16197     /**
16198      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16199      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16200      */
16201     isDirty : function() {
16202         if(this.disabled) {
16203             return false;
16204         }
16205         return String(this.getValue()) !== String(this.originalValue);
16206     },
16207
16208     /**
16209      * stores the current value in loadedValue
16210      */
16211     resetHasChanged : function()
16212     {
16213         this.loadedValue = String(this.getValue());
16214     },
16215     /**
16216      * checks the current value against the 'loaded' value.
16217      * Note - will return false if 'resetHasChanged' has not been called first.
16218      */
16219     hasChanged : function()
16220     {
16221         if(this.disabled || this.readOnly) {
16222             return false;
16223         }
16224         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16225     },
16226     
16227     
16228     
16229     // private
16230     afterRender : function(){
16231         Roo.form.Field.superclass.afterRender.call(this);
16232         this.initEvents();
16233     },
16234
16235     // private
16236     fireKey : function(e){
16237         //Roo.log('field ' + e.getKey());
16238         if(e.isNavKeyPress()){
16239             this.fireEvent("specialkey", this, e);
16240         }
16241     },
16242
16243     /**
16244      * Resets the current field value to the originally loaded value and clears any validation messages
16245      */
16246     reset : function(){
16247         this.setValue(this.resetValue);
16248         this.originalValue = this.getValue();
16249         this.clearInvalid();
16250     },
16251
16252     // private
16253     initEvents : function(){
16254         // safari killled keypress - so keydown is now used..
16255         this.el.on("keydown" , this.fireKey,  this);
16256         this.el.on("focus", this.onFocus,  this);
16257         this.el.on("blur", this.onBlur,  this);
16258         this.el.relayEvent('keyup', this);
16259
16260         // reference to original value for reset
16261         this.originalValue = this.getValue();
16262         this.resetValue =  this.getValue();
16263     },
16264
16265     // private
16266     onFocus : function(){
16267         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16268             this.el.addClass(this.focusClass);
16269         }
16270         if(!this.hasFocus){
16271             this.hasFocus = true;
16272             this.startValue = this.getValue();
16273             this.fireEvent("focus", this);
16274         }
16275     },
16276
16277     beforeBlur : Roo.emptyFn,
16278
16279     // private
16280     onBlur : function(){
16281         this.beforeBlur();
16282         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16283             this.el.removeClass(this.focusClass);
16284         }
16285         this.hasFocus = false;
16286         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16287             this.validate();
16288         }
16289         var v = this.getValue();
16290         if(String(v) !== String(this.startValue)){
16291             this.fireEvent('change', this, v, this.startValue);
16292         }
16293         this.fireEvent("blur", this);
16294     },
16295
16296     /**
16297      * Returns whether or not the field value is currently valid
16298      * @param {Boolean} preventMark True to disable marking the field invalid
16299      * @return {Boolean} True if the value is valid, else false
16300      */
16301     isValid : function(preventMark){
16302         if(this.disabled){
16303             return true;
16304         }
16305         var restore = this.preventMark;
16306         this.preventMark = preventMark === true;
16307         var v = this.validateValue(this.processValue(this.getRawValue()));
16308         this.preventMark = restore;
16309         return v;
16310     },
16311
16312     /**
16313      * Validates the field value
16314      * @return {Boolean} True if the value is valid, else false
16315      */
16316     validate : function(){
16317         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16318             this.clearInvalid();
16319             return true;
16320         }
16321         return false;
16322     },
16323
16324     processValue : function(value){
16325         return value;
16326     },
16327
16328     // private
16329     // Subclasses should provide the validation implementation by overriding this
16330     validateValue : function(value){
16331         return true;
16332     },
16333
16334     /**
16335      * Mark this field as invalid
16336      * @param {String} msg The validation message
16337      */
16338     markInvalid : function(msg){
16339         if(!this.rendered || this.preventMark){ // not rendered
16340             return;
16341         }
16342         
16343         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16344         
16345         obj.el.addClass(this.invalidClass);
16346         msg = msg || this.invalidText;
16347         switch(this.msgTarget){
16348             case 'qtip':
16349                 obj.el.dom.qtip = msg;
16350                 obj.el.dom.qclass = 'x-form-invalid-tip';
16351                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16352                     Roo.QuickTips.enable();
16353                 }
16354                 break;
16355             case 'title':
16356                 this.el.dom.title = msg;
16357                 break;
16358             case 'under':
16359                 if(!this.errorEl){
16360                     var elp = this.el.findParent('.x-form-element', 5, true);
16361                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16362                     this.errorEl.setWidth(elp.getWidth(true)-20);
16363                 }
16364                 this.errorEl.update(msg);
16365                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16366                 break;
16367             case 'side':
16368                 if(!this.errorIcon){
16369                     var elp = this.el.findParent('.x-form-element', 5, true);
16370                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16371                 }
16372                 this.alignErrorIcon();
16373                 this.errorIcon.dom.qtip = msg;
16374                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16375                 this.errorIcon.show();
16376                 this.on('resize', this.alignErrorIcon, this);
16377                 break;
16378             default:
16379                 var t = Roo.getDom(this.msgTarget);
16380                 t.innerHTML = msg;
16381                 t.style.display = this.msgDisplay;
16382                 break;
16383         }
16384         this.fireEvent('invalid', this, msg);
16385     },
16386
16387     // private
16388     alignErrorIcon : function(){
16389         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16390     },
16391
16392     /**
16393      * Clear any invalid styles/messages for this field
16394      */
16395     clearInvalid : function(){
16396         if(!this.rendered || this.preventMark){ // not rendered
16397             return;
16398         }
16399         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16400         
16401         obj.el.removeClass(this.invalidClass);
16402         switch(this.msgTarget){
16403             case 'qtip':
16404                 obj.el.dom.qtip = '';
16405                 break;
16406             case 'title':
16407                 this.el.dom.title = '';
16408                 break;
16409             case 'under':
16410                 if(this.errorEl){
16411                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16412                 }
16413                 break;
16414             case 'side':
16415                 if(this.errorIcon){
16416                     this.errorIcon.dom.qtip = '';
16417                     this.errorIcon.hide();
16418                     this.un('resize', this.alignErrorIcon, this);
16419                 }
16420                 break;
16421             default:
16422                 var t = Roo.getDom(this.msgTarget);
16423                 t.innerHTML = '';
16424                 t.style.display = 'none';
16425                 break;
16426         }
16427         this.fireEvent('valid', this);
16428     },
16429
16430     /**
16431      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16432      * @return {Mixed} value The field value
16433      */
16434     getRawValue : function(){
16435         var v = this.el.getValue();
16436         
16437         return v;
16438     },
16439
16440     /**
16441      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16442      * @return {Mixed} value The field value
16443      */
16444     getValue : function(){
16445         var v = this.el.getValue();
16446          
16447         return v;
16448     },
16449
16450     /**
16451      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16452      * @param {Mixed} value The value to set
16453      */
16454     setRawValue : function(v){
16455         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16456     },
16457
16458     /**
16459      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16460      * @param {Mixed} value The value to set
16461      */
16462     setValue : function(v){
16463         this.value = v;
16464         if(this.rendered){
16465             this.el.dom.value = (v === null || v === undefined ? '' : v);
16466              this.validate();
16467         }
16468     },
16469
16470     adjustSize : function(w, h){
16471         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16472         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16473         return s;
16474     },
16475
16476     adjustWidth : function(tag, w){
16477         tag = tag.toLowerCase();
16478         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16479             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16480                 if(tag == 'input'){
16481                     return w + 2;
16482                 }
16483                 if(tag == 'textarea'){
16484                     return w-2;
16485                 }
16486             }else if(Roo.isOpera){
16487                 if(tag == 'input'){
16488                     return w + 2;
16489                 }
16490                 if(tag == 'textarea'){
16491                     return w-2;
16492                 }
16493             }
16494         }
16495         return w;
16496     }
16497 });
16498
16499
16500 // anything other than normal should be considered experimental
16501 Roo.form.Field.msgFx = {
16502     normal : {
16503         show: function(msgEl, f){
16504             msgEl.setDisplayed('block');
16505         },
16506
16507         hide : function(msgEl, f){
16508             msgEl.setDisplayed(false).update('');
16509         }
16510     },
16511
16512     slide : {
16513         show: function(msgEl, f){
16514             msgEl.slideIn('t', {stopFx:true});
16515         },
16516
16517         hide : function(msgEl, f){
16518             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16519         }
16520     },
16521
16522     slideRight : {
16523         show: function(msgEl, f){
16524             msgEl.fixDisplay();
16525             msgEl.alignTo(f.el, 'tl-tr');
16526             msgEl.slideIn('l', {stopFx:true});
16527         },
16528
16529         hide : function(msgEl, f){
16530             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16531         }
16532     }
16533 };/*
16534  * Based on:
16535  * Ext JS Library 1.1.1
16536  * Copyright(c) 2006-2007, Ext JS, LLC.
16537  *
16538  * Originally Released Under LGPL - original licence link has changed is not relivant.
16539  *
16540  * Fork - LGPL
16541  * <script type="text/javascript">
16542  */
16543  
16544
16545 /**
16546  * @class Roo.form.TextField
16547  * @extends Roo.form.Field
16548  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16549  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16550  * @constructor
16551  * Creates a new TextField
16552  * @param {Object} config Configuration options
16553  */
16554 Roo.form.TextField = function(config){
16555     Roo.form.TextField.superclass.constructor.call(this, config);
16556     this.addEvents({
16557         /**
16558          * @event autosize
16559          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16560          * according to the default logic, but this event provides a hook for the developer to apply additional
16561          * logic at runtime to resize the field if needed.
16562              * @param {Roo.form.Field} this This text field
16563              * @param {Number} width The new field width
16564              */
16565         autosize : true
16566     });
16567 };
16568
16569 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16570     /**
16571      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16572      */
16573     grow : false,
16574     /**
16575      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16576      */
16577     growMin : 30,
16578     /**
16579      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16580      */
16581     growMax : 800,
16582     /**
16583      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16584      */
16585     vtype : null,
16586     /**
16587      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16588      */
16589     maskRe : null,
16590     /**
16591      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16592      */
16593     disableKeyFilter : false,
16594     /**
16595      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16596      */
16597     allowBlank : true,
16598     /**
16599      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16600      */
16601     minLength : 0,
16602     /**
16603      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16604      */
16605     maxLength : Number.MAX_VALUE,
16606     /**
16607      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16608      */
16609     minLengthText : "The minimum length for this field is {0}",
16610     /**
16611      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16612      */
16613     maxLengthText : "The maximum length for this field is {0}",
16614     /**
16615      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16616      */
16617     selectOnFocus : false,
16618     /**
16619      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16620      */    
16621     allowLeadingSpace : false,
16622     /**
16623      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16624      */
16625     blankText : "This field is required",
16626     /**
16627      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16628      * If available, this function will be called only after the basic validators all return true, and will be passed the
16629      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16630      */
16631     validator : null,
16632     /**
16633      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16634      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16635      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16636      */
16637     regex : null,
16638     /**
16639      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16640      */
16641     regexText : "",
16642     /**
16643      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16644      */
16645     emptyText : null,
16646    
16647
16648     // private
16649     initEvents : function()
16650     {
16651         if (this.emptyText) {
16652             this.el.attr('placeholder', this.emptyText);
16653         }
16654         
16655         Roo.form.TextField.superclass.initEvents.call(this);
16656         if(this.validationEvent == 'keyup'){
16657             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16658             this.el.on('keyup', this.filterValidation, this);
16659         }
16660         else if(this.validationEvent !== false){
16661             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16662         }
16663         
16664         if(this.selectOnFocus){
16665             this.on("focus", this.preFocus, this);
16666         }
16667         if (!this.allowLeadingSpace) {
16668             this.on('blur', this.cleanLeadingSpace, this);
16669         }
16670         
16671         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16672             this.el.on("keypress", this.filterKeys, this);
16673         }
16674         if(this.grow){
16675             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16676             this.el.on("click", this.autoSize,  this);
16677         }
16678         if(this.el.is('input[type=password]') && Roo.isSafari){
16679             this.el.on('keydown', this.SafariOnKeyDown, this);
16680         }
16681     },
16682
16683     processValue : function(value){
16684         if(this.stripCharsRe){
16685             var newValue = value.replace(this.stripCharsRe, '');
16686             if(newValue !== value){
16687                 this.setRawValue(newValue);
16688                 return newValue;
16689             }
16690         }
16691         return value;
16692     },
16693
16694     filterValidation : function(e){
16695         if(!e.isNavKeyPress()){
16696             this.validationTask.delay(this.validationDelay);
16697         }
16698     },
16699
16700     // private
16701     onKeyUp : function(e){
16702         if(!e.isNavKeyPress()){
16703             this.autoSize();
16704         }
16705     },
16706     // private - clean the leading white space
16707     cleanLeadingSpace : function(e)
16708     {
16709         if ( this.inputType == 'file') {
16710             return;
16711         }
16712         
16713         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16714     },
16715     /**
16716      * Resets the current field value to the originally-loaded value and clears any validation messages.
16717      *  
16718      */
16719     reset : function(){
16720         Roo.form.TextField.superclass.reset.call(this);
16721        
16722     }, 
16723     // private
16724     preFocus : function(){
16725         
16726         if(this.selectOnFocus){
16727             this.el.dom.select();
16728         }
16729     },
16730
16731     
16732     // private
16733     filterKeys : function(e){
16734         var k = e.getKey();
16735         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16736             return;
16737         }
16738         var c = e.getCharCode(), cc = String.fromCharCode(c);
16739         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16740             return;
16741         }
16742         if(!this.maskRe.test(cc)){
16743             e.stopEvent();
16744         }
16745     },
16746
16747     setValue : function(v){
16748         
16749         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16750         
16751         this.autoSize();
16752     },
16753
16754     /**
16755      * Validates a value according to the field's validation rules and marks the field as invalid
16756      * if the validation fails
16757      * @param {Mixed} value The value to validate
16758      * @return {Boolean} True if the value is valid, else false
16759      */
16760     validateValue : function(value){
16761         if(value.length < 1)  { // if it's blank
16762              if(this.allowBlank){
16763                 this.clearInvalid();
16764                 return true;
16765              }else{
16766                 this.markInvalid(this.blankText);
16767                 return false;
16768              }
16769         }
16770         if(value.length < this.minLength){
16771             this.markInvalid(String.format(this.minLengthText, this.minLength));
16772             return false;
16773         }
16774         if(value.length > this.maxLength){
16775             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16776             return false;
16777         }
16778         if(this.vtype){
16779             var vt = Roo.form.VTypes;
16780             if(!vt[this.vtype](value, this)){
16781                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16782                 return false;
16783             }
16784         }
16785         if(typeof this.validator == "function"){
16786             var msg = this.validator(value);
16787             if(msg !== true){
16788                 this.markInvalid(msg);
16789                 return false;
16790             }
16791         }
16792         if(this.regex && !this.regex.test(value)){
16793             this.markInvalid(this.regexText);
16794             return false;
16795         }
16796         return true;
16797     },
16798
16799     /**
16800      * Selects text in this field
16801      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16802      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16803      */
16804     selectText : function(start, end){
16805         var v = this.getRawValue();
16806         if(v.length > 0){
16807             start = start === undefined ? 0 : start;
16808             end = end === undefined ? v.length : end;
16809             var d = this.el.dom;
16810             if(d.setSelectionRange){
16811                 d.setSelectionRange(start, end);
16812             }else if(d.createTextRange){
16813                 var range = d.createTextRange();
16814                 range.moveStart("character", start);
16815                 range.moveEnd("character", v.length-end);
16816                 range.select();
16817             }
16818         }
16819     },
16820
16821     /**
16822      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16823      * This only takes effect if grow = true, and fires the autosize event.
16824      */
16825     autoSize : function(){
16826         if(!this.grow || !this.rendered){
16827             return;
16828         }
16829         if(!this.metrics){
16830             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16831         }
16832         var el = this.el;
16833         var v = el.dom.value;
16834         var d = document.createElement('div');
16835         d.appendChild(document.createTextNode(v));
16836         v = d.innerHTML;
16837         d = null;
16838         v += "&#160;";
16839         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16840         this.el.setWidth(w);
16841         this.fireEvent("autosize", this, w);
16842     },
16843     
16844     // private
16845     SafariOnKeyDown : function(event)
16846     {
16847         // this is a workaround for a password hang bug on chrome/ webkit.
16848         
16849         var isSelectAll = false;
16850         
16851         if(this.el.dom.selectionEnd > 0){
16852             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16853         }
16854         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16855             event.preventDefault();
16856             this.setValue('');
16857             return;
16858         }
16859         
16860         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16861             
16862             event.preventDefault();
16863             // this is very hacky as keydown always get's upper case.
16864             
16865             var cc = String.fromCharCode(event.getCharCode());
16866             
16867             
16868             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16869             
16870         }
16871         
16872         
16873     }
16874 });/*
16875  * Based on:
16876  * Ext JS Library 1.1.1
16877  * Copyright(c) 2006-2007, Ext JS, LLC.
16878  *
16879  * Originally Released Under LGPL - original licence link has changed is not relivant.
16880  *
16881  * Fork - LGPL
16882  * <script type="text/javascript">
16883  */
16884  
16885 /**
16886  * @class Roo.form.Hidden
16887  * @extends Roo.form.TextField
16888  * Simple Hidden element used on forms 
16889  * 
16890  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16891  * 
16892  * @constructor
16893  * Creates a new Hidden form element.
16894  * @param {Object} config Configuration options
16895  */
16896
16897
16898
16899 // easy hidden field...
16900 Roo.form.Hidden = function(config){
16901     Roo.form.Hidden.superclass.constructor.call(this, config);
16902 };
16903   
16904 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16905     fieldLabel:      '',
16906     inputType:      'hidden',
16907     width:          50,
16908     allowBlank:     true,
16909     labelSeparator: '',
16910     hidden:         true,
16911     itemCls :       'x-form-item-display-none'
16912
16913
16914 });
16915
16916
16917 /*
16918  * Based on:
16919  * Ext JS Library 1.1.1
16920  * Copyright(c) 2006-2007, Ext JS, LLC.
16921  *
16922  * Originally Released Under LGPL - original licence link has changed is not relivant.
16923  *
16924  * Fork - LGPL
16925  * <script type="text/javascript">
16926  */
16927  
16928 /**
16929  * @class Roo.form.TriggerField
16930  * @extends Roo.form.TextField
16931  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16932  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16933  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16934  * for which you can provide a custom implementation.  For example:
16935  * <pre><code>
16936 var trigger = new Roo.form.TriggerField();
16937 trigger.onTriggerClick = myTriggerFn;
16938 trigger.applyTo('my-field');
16939 </code></pre>
16940  *
16941  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16942  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16943  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16944  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16945  * @constructor
16946  * Create a new TriggerField.
16947  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16948  * to the base TextField)
16949  */
16950 Roo.form.TriggerField = function(config){
16951     this.mimicing = false;
16952     Roo.form.TriggerField.superclass.constructor.call(this, config);
16953 };
16954
16955 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16956     /**
16957      * @cfg {String} triggerClass A CSS class to apply to the trigger
16958      */
16959     /**
16960      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16961      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16962      */
16963     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16964     /**
16965      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16966      */
16967     hideTrigger:false,
16968
16969     /** @cfg {Boolean} grow @hide */
16970     /** @cfg {Number} growMin @hide */
16971     /** @cfg {Number} growMax @hide */
16972
16973     /**
16974      * @hide 
16975      * @method
16976      */
16977     autoSize: Roo.emptyFn,
16978     // private
16979     monitorTab : true,
16980     // private
16981     deferHeight : true,
16982
16983     
16984     actionMode : 'wrap',
16985     // private
16986     onResize : function(w, h){
16987         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
16988         if(typeof w == 'number'){
16989             var x = w - this.trigger.getWidth();
16990             this.el.setWidth(this.adjustWidth('input', x));
16991             this.trigger.setStyle('left', x+'px');
16992         }
16993     },
16994
16995     // private
16996     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16997
16998     // private
16999     getResizeEl : function(){
17000         return this.wrap;
17001     },
17002
17003     // private
17004     getPositionEl : function(){
17005         return this.wrap;
17006     },
17007
17008     // private
17009     alignErrorIcon : function(){
17010         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17011     },
17012
17013     // private
17014     onRender : function(ct, position){
17015         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17016         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17017         this.trigger = this.wrap.createChild(this.triggerConfig ||
17018                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17019         if(this.hideTrigger){
17020             this.trigger.setDisplayed(false);
17021         }
17022         this.initTrigger();
17023         if(!this.width){
17024             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17025         }
17026     },
17027
17028     // private
17029     initTrigger : function(){
17030         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17031         this.trigger.addClassOnOver('x-form-trigger-over');
17032         this.trigger.addClassOnClick('x-form-trigger-click');
17033     },
17034
17035     // private
17036     onDestroy : function(){
17037         if(this.trigger){
17038             this.trigger.removeAllListeners();
17039             this.trigger.remove();
17040         }
17041         if(this.wrap){
17042             this.wrap.remove();
17043         }
17044         Roo.form.TriggerField.superclass.onDestroy.call(this);
17045     },
17046
17047     // private
17048     onFocus : function(){
17049         Roo.form.TriggerField.superclass.onFocus.call(this);
17050         if(!this.mimicing){
17051             this.wrap.addClass('x-trigger-wrap-focus');
17052             this.mimicing = true;
17053             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17054             if(this.monitorTab){
17055                 this.el.on("keydown", this.checkTab, this);
17056             }
17057         }
17058     },
17059
17060     // private
17061     checkTab : function(e){
17062         if(e.getKey() == e.TAB){
17063             this.triggerBlur();
17064         }
17065     },
17066
17067     // private
17068     onBlur : function(){
17069         // do nothing
17070     },
17071
17072     // private
17073     mimicBlur : function(e, t){
17074         if(!this.wrap.contains(t) && this.validateBlur()){
17075             this.triggerBlur();
17076         }
17077     },
17078
17079     // private
17080     triggerBlur : function(){
17081         this.mimicing = false;
17082         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17083         if(this.monitorTab){
17084             this.el.un("keydown", this.checkTab, this);
17085         }
17086         this.wrap.removeClass('x-trigger-wrap-focus');
17087         Roo.form.TriggerField.superclass.onBlur.call(this);
17088     },
17089
17090     // private
17091     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17092     validateBlur : function(e, t){
17093         return true;
17094     },
17095
17096     // private
17097     onDisable : function(){
17098         Roo.form.TriggerField.superclass.onDisable.call(this);
17099         if(this.wrap){
17100             this.wrap.addClass('x-item-disabled');
17101         }
17102     },
17103
17104     // private
17105     onEnable : function(){
17106         Roo.form.TriggerField.superclass.onEnable.call(this);
17107         if(this.wrap){
17108             this.wrap.removeClass('x-item-disabled');
17109         }
17110     },
17111
17112     // private
17113     onShow : function(){
17114         var ae = this.getActionEl();
17115         
17116         if(ae){
17117             ae.dom.style.display = '';
17118             ae.dom.style.visibility = 'visible';
17119         }
17120     },
17121
17122     // private
17123     
17124     onHide : function(){
17125         var ae = this.getActionEl();
17126         ae.dom.style.display = 'none';
17127     },
17128
17129     /**
17130      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17131      * by an implementing function.
17132      * @method
17133      * @param {EventObject} e
17134      */
17135     onTriggerClick : Roo.emptyFn
17136 });
17137
17138 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17139 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17140 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17141 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17142     initComponent : function(){
17143         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17144
17145         this.triggerConfig = {
17146             tag:'span', cls:'x-form-twin-triggers', cn:[
17147             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17148             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17149         ]};
17150     },
17151
17152     getTrigger : function(index){
17153         return this.triggers[index];
17154     },
17155
17156     initTrigger : function(){
17157         var ts = this.trigger.select('.x-form-trigger', true);
17158         this.wrap.setStyle('overflow', 'hidden');
17159         var triggerField = this;
17160         ts.each(function(t, all, index){
17161             t.hide = function(){
17162                 var w = triggerField.wrap.getWidth();
17163                 this.dom.style.display = 'none';
17164                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17165             };
17166             t.show = function(){
17167                 var w = triggerField.wrap.getWidth();
17168                 this.dom.style.display = '';
17169                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17170             };
17171             var triggerIndex = 'Trigger'+(index+1);
17172
17173             if(this['hide'+triggerIndex]){
17174                 t.dom.style.display = 'none';
17175             }
17176             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17177             t.addClassOnOver('x-form-trigger-over');
17178             t.addClassOnClick('x-form-trigger-click');
17179         }, this);
17180         this.triggers = ts.elements;
17181     },
17182
17183     onTrigger1Click : Roo.emptyFn,
17184     onTrigger2Click : Roo.emptyFn
17185 });/*
17186  * Based on:
17187  * Ext JS Library 1.1.1
17188  * Copyright(c) 2006-2007, Ext JS, LLC.
17189  *
17190  * Originally Released Under LGPL - original licence link has changed is not relivant.
17191  *
17192  * Fork - LGPL
17193  * <script type="text/javascript">
17194  */
17195  
17196 /**
17197  * @class Roo.form.TextArea
17198  * @extends Roo.form.TextField
17199  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17200  * support for auto-sizing.
17201  * @constructor
17202  * Creates a new TextArea
17203  * @param {Object} config Configuration options
17204  */
17205 Roo.form.TextArea = function(config){
17206     Roo.form.TextArea.superclass.constructor.call(this, config);
17207     // these are provided exchanges for backwards compat
17208     // minHeight/maxHeight were replaced by growMin/growMax to be
17209     // compatible with TextField growing config values
17210     if(this.minHeight !== undefined){
17211         this.growMin = this.minHeight;
17212     }
17213     if(this.maxHeight !== undefined){
17214         this.growMax = this.maxHeight;
17215     }
17216 };
17217
17218 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17219     /**
17220      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17221      */
17222     growMin : 60,
17223     /**
17224      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17225      */
17226     growMax: 1000,
17227     /**
17228      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17229      * in the field (equivalent to setting overflow: hidden, defaults to false)
17230      */
17231     preventScrollbars: false,
17232     /**
17233      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17234      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17235      */
17236
17237     // private
17238     onRender : function(ct, position){
17239         if(!this.el){
17240             this.defaultAutoCreate = {
17241                 tag: "textarea",
17242                 style:"width:300px;height:60px;",
17243                 autocomplete: "new-password"
17244             };
17245         }
17246         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17247         if(this.grow){
17248             this.textSizeEl = Roo.DomHelper.append(document.body, {
17249                 tag: "pre", cls: "x-form-grow-sizer"
17250             });
17251             if(this.preventScrollbars){
17252                 this.el.setStyle("overflow", "hidden");
17253             }
17254             this.el.setHeight(this.growMin);
17255         }
17256     },
17257
17258     onDestroy : function(){
17259         if(this.textSizeEl){
17260             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17261         }
17262         Roo.form.TextArea.superclass.onDestroy.call(this);
17263     },
17264
17265     // private
17266     onKeyUp : function(e){
17267         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17268             this.autoSize();
17269         }
17270     },
17271
17272     /**
17273      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17274      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17275      */
17276     autoSize : function(){
17277         if(!this.grow || !this.textSizeEl){
17278             return;
17279         }
17280         var el = this.el;
17281         var v = el.dom.value;
17282         var ts = this.textSizeEl;
17283
17284         ts.innerHTML = '';
17285         ts.appendChild(document.createTextNode(v));
17286         v = ts.innerHTML;
17287
17288         Roo.fly(ts).setWidth(this.el.getWidth());
17289         if(v.length < 1){
17290             v = "&#160;&#160;";
17291         }else{
17292             if(Roo.isIE){
17293                 v = v.replace(/\n/g, '<p>&#160;</p>');
17294             }
17295             v += "&#160;\n&#160;";
17296         }
17297         ts.innerHTML = v;
17298         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17299         if(h != this.lastHeight){
17300             this.lastHeight = h;
17301             this.el.setHeight(h);
17302             this.fireEvent("autosize", this, h);
17303         }
17304     }
17305 });/*
17306  * Based on:
17307  * Ext JS Library 1.1.1
17308  * Copyright(c) 2006-2007, Ext JS, LLC.
17309  *
17310  * Originally Released Under LGPL - original licence link has changed is not relivant.
17311  *
17312  * Fork - LGPL
17313  * <script type="text/javascript">
17314  */
17315  
17316
17317 /**
17318  * @class Roo.form.NumberField
17319  * @extends Roo.form.TextField
17320  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17321  * @constructor
17322  * Creates a new NumberField
17323  * @param {Object} config Configuration options
17324  */
17325 Roo.form.NumberField = function(config){
17326     Roo.form.NumberField.superclass.constructor.call(this, config);
17327 };
17328
17329 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17330     /**
17331      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17332      */
17333     fieldClass: "x-form-field x-form-num-field",
17334     /**
17335      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17336      */
17337     allowDecimals : true,
17338     /**
17339      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17340      */
17341     decimalSeparator : ".",
17342     /**
17343      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17344      */
17345     decimalPrecision : 2,
17346     /**
17347      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17348      */
17349     allowNegative : true,
17350     /**
17351      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17352      */
17353     minValue : Number.NEGATIVE_INFINITY,
17354     /**
17355      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17356      */
17357     maxValue : Number.MAX_VALUE,
17358     /**
17359      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17360      */
17361     minText : "The minimum value for this field is {0}",
17362     /**
17363      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17364      */
17365     maxText : "The maximum value for this field is {0}",
17366     /**
17367      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17368      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17369      */
17370     nanText : "{0} is not a valid number",
17371
17372     // private
17373     initEvents : function(){
17374         Roo.form.NumberField.superclass.initEvents.call(this);
17375         var allowed = "0123456789";
17376         if(this.allowDecimals){
17377             allowed += this.decimalSeparator;
17378         }
17379         if(this.allowNegative){
17380             allowed += "-";
17381         }
17382         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17383         var keyPress = function(e){
17384             var k = e.getKey();
17385             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17386                 return;
17387             }
17388             var c = e.getCharCode();
17389             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17390                 e.stopEvent();
17391             }
17392         };
17393         this.el.on("keypress", keyPress, this);
17394     },
17395
17396     // private
17397     validateValue : function(value){
17398         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17399             return false;
17400         }
17401         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17402              return true;
17403         }
17404         var num = this.parseValue(value);
17405         if(isNaN(num)){
17406             this.markInvalid(String.format(this.nanText, value));
17407             return false;
17408         }
17409         if(num < this.minValue){
17410             this.markInvalid(String.format(this.minText, this.minValue));
17411             return false;
17412         }
17413         if(num > this.maxValue){
17414             this.markInvalid(String.format(this.maxText, this.maxValue));
17415             return false;
17416         }
17417         return true;
17418     },
17419
17420     getValue : function(){
17421         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17422     },
17423
17424     // private
17425     parseValue : function(value){
17426         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17427         return isNaN(value) ? '' : value;
17428     },
17429
17430     // private
17431     fixPrecision : function(value){
17432         var nan = isNaN(value);
17433         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17434             return nan ? '' : value;
17435         }
17436         return parseFloat(value).toFixed(this.decimalPrecision);
17437     },
17438
17439     setValue : function(v){
17440         v = this.fixPrecision(v);
17441         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17442     },
17443
17444     // private
17445     decimalPrecisionFcn : function(v){
17446         return Math.floor(v);
17447     },
17448
17449     beforeBlur : function(){
17450         var v = this.parseValue(this.getRawValue());
17451         if(v){
17452             this.setValue(v);
17453         }
17454     }
17455 });/*
17456  * Based on:
17457  * Ext JS Library 1.1.1
17458  * Copyright(c) 2006-2007, Ext JS, LLC.
17459  *
17460  * Originally Released Under LGPL - original licence link has changed is not relivant.
17461  *
17462  * Fork - LGPL
17463  * <script type="text/javascript">
17464  */
17465  
17466 /**
17467  * @class Roo.form.DateField
17468  * @extends Roo.form.TriggerField
17469  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17470 * @constructor
17471 * Create a new DateField
17472 * @param {Object} config
17473  */
17474 Roo.form.DateField = function(config)
17475 {
17476     Roo.form.DateField.superclass.constructor.call(this, config);
17477     
17478       this.addEvents({
17479          
17480         /**
17481          * @event select
17482          * Fires when a date is selected
17483              * @param {Roo.form.DateField} combo This combo box
17484              * @param {Date} date The date selected
17485              */
17486         'select' : true
17487          
17488     });
17489     
17490     
17491     if(typeof this.minValue == "string") {
17492         this.minValue = this.parseDate(this.minValue);
17493     }
17494     if(typeof this.maxValue == "string") {
17495         this.maxValue = this.parseDate(this.maxValue);
17496     }
17497     this.ddMatch = null;
17498     if(this.disabledDates){
17499         var dd = this.disabledDates;
17500         var re = "(?:";
17501         for(var i = 0; i < dd.length; i++){
17502             re += dd[i];
17503             if(i != dd.length-1) {
17504                 re += "|";
17505             }
17506         }
17507         this.ddMatch = new RegExp(re + ")");
17508     }
17509 };
17510
17511 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17512     /**
17513      * @cfg {String} format
17514      * The default date format string which can be overriden for localization support.  The format must be
17515      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17516      */
17517     format : "m/d/y",
17518     /**
17519      * @cfg {String} altFormats
17520      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17521      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17522      */
17523     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17524     /**
17525      * @cfg {Array} disabledDays
17526      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17527      */
17528     disabledDays : null,
17529     /**
17530      * @cfg {String} disabledDaysText
17531      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17532      */
17533     disabledDaysText : "Disabled",
17534     /**
17535      * @cfg {Array} disabledDates
17536      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17537      * expression so they are very powerful. Some examples:
17538      * <ul>
17539      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17540      * <li>["03/08", "09/16"] would disable those days for every year</li>
17541      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17542      * <li>["03/../2006"] would disable every day in March 2006</li>
17543      * <li>["^03"] would disable every day in every March</li>
17544      * </ul>
17545      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17546      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17547      */
17548     disabledDates : null,
17549     /**
17550      * @cfg {String} disabledDatesText
17551      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17552      */
17553     disabledDatesText : "Disabled",
17554     /**
17555      * @cfg {Date/String} minValue
17556      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17557      * valid format (defaults to null).
17558      */
17559     minValue : null,
17560     /**
17561      * @cfg {Date/String} maxValue
17562      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17563      * valid format (defaults to null).
17564      */
17565     maxValue : null,
17566     /**
17567      * @cfg {String} minText
17568      * The error text to display when the date in the cell is before minValue (defaults to
17569      * 'The date in this field must be after {minValue}').
17570      */
17571     minText : "The date in this field must be equal to or after {0}",
17572     /**
17573      * @cfg {String} maxText
17574      * The error text to display when the date in the cell is after maxValue (defaults to
17575      * 'The date in this field must be before {maxValue}').
17576      */
17577     maxText : "The date in this field must be equal to or before {0}",
17578     /**
17579      * @cfg {String} invalidText
17580      * The error text to display when the date in the field is invalid (defaults to
17581      * '{value} is not a valid date - it must be in the format {format}').
17582      */
17583     invalidText : "{0} is not a valid date - it must be in the format {1}",
17584     /**
17585      * @cfg {String} triggerClass
17586      * An additional CSS class used to style the trigger button.  The trigger will always get the
17587      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17588      * which displays a calendar icon).
17589      */
17590     triggerClass : 'x-form-date-trigger',
17591     
17592
17593     /**
17594      * @cfg {Boolean} useIso
17595      * if enabled, then the date field will use a hidden field to store the 
17596      * real value as iso formated date. default (false)
17597      */ 
17598     useIso : false,
17599     /**
17600      * @cfg {String/Object} autoCreate
17601      * A DomHelper element spec, or true for a default element spec (defaults to
17602      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17603      */ 
17604     // private
17605     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17606     
17607     // private
17608     hiddenField: false,
17609     
17610     onRender : function(ct, position)
17611     {
17612         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17613         if (this.useIso) {
17614             //this.el.dom.removeAttribute('name'); 
17615             Roo.log("Changing name?");
17616             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17617             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17618                     'before', true);
17619             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17620             // prevent input submission
17621             this.hiddenName = this.name;
17622         }
17623             
17624             
17625     },
17626     
17627     // private
17628     validateValue : function(value)
17629     {
17630         value = this.formatDate(value);
17631         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17632             Roo.log('super failed');
17633             return false;
17634         }
17635         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17636              return true;
17637         }
17638         var svalue = value;
17639         value = this.parseDate(value);
17640         if(!value){
17641             Roo.log('parse date failed' + svalue);
17642             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17643             return false;
17644         }
17645         var time = value.getTime();
17646         if(this.minValue && time < this.minValue.getTime()){
17647             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17648             return false;
17649         }
17650         if(this.maxValue && time > this.maxValue.getTime()){
17651             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17652             return false;
17653         }
17654         if(this.disabledDays){
17655             var day = value.getDay();
17656             for(var i = 0; i < this.disabledDays.length; i++) {
17657                 if(day === this.disabledDays[i]){
17658                     this.markInvalid(this.disabledDaysText);
17659                     return false;
17660                 }
17661             }
17662         }
17663         var fvalue = this.formatDate(value);
17664         if(this.ddMatch && this.ddMatch.test(fvalue)){
17665             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17666             return false;
17667         }
17668         return true;
17669     },
17670
17671     // private
17672     // Provides logic to override the default TriggerField.validateBlur which just returns true
17673     validateBlur : function(){
17674         return !this.menu || !this.menu.isVisible();
17675     },
17676     
17677     getName: function()
17678     {
17679         // returns hidden if it's set..
17680         if (!this.rendered) {return ''};
17681         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17682         
17683     },
17684
17685     /**
17686      * Returns the current date value of the date field.
17687      * @return {Date} The date value
17688      */
17689     getValue : function(){
17690         
17691         return  this.hiddenField ?
17692                 this.hiddenField.value :
17693                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17694     },
17695
17696     /**
17697      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17698      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17699      * (the default format used is "m/d/y").
17700      * <br />Usage:
17701      * <pre><code>
17702 //All of these calls set the same date value (May 4, 2006)
17703
17704 //Pass a date object:
17705 var dt = new Date('5/4/06');
17706 dateField.setValue(dt);
17707
17708 //Pass a date string (default format):
17709 dateField.setValue('5/4/06');
17710
17711 //Pass a date string (custom format):
17712 dateField.format = 'Y-m-d';
17713 dateField.setValue('2006-5-4');
17714 </code></pre>
17715      * @param {String/Date} date The date or valid date string
17716      */
17717     setValue : function(date){
17718         if (this.hiddenField) {
17719             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17720         }
17721         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17722         // make sure the value field is always stored as a date..
17723         this.value = this.parseDate(date);
17724         
17725         
17726     },
17727
17728     // private
17729     parseDate : function(value){
17730         if(!value || value instanceof Date){
17731             return value;
17732         }
17733         var v = Date.parseDate(value, this.format);
17734          if (!v && this.useIso) {
17735             v = Date.parseDate(value, 'Y-m-d');
17736         }
17737         if(!v && this.altFormats){
17738             if(!this.altFormatsArray){
17739                 this.altFormatsArray = this.altFormats.split("|");
17740             }
17741             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17742                 v = Date.parseDate(value, this.altFormatsArray[i]);
17743             }
17744         }
17745         return v;
17746     },
17747
17748     // private
17749     formatDate : function(date, fmt){
17750         return (!date || !(date instanceof Date)) ?
17751                date : date.dateFormat(fmt || this.format);
17752     },
17753
17754     // private
17755     menuListeners : {
17756         select: function(m, d){
17757             
17758             this.setValue(d);
17759             this.fireEvent('select', this, d);
17760         },
17761         show : function(){ // retain focus styling
17762             this.onFocus();
17763         },
17764         hide : function(){
17765             this.focus.defer(10, this);
17766             var ml = this.menuListeners;
17767             this.menu.un("select", ml.select,  this);
17768             this.menu.un("show", ml.show,  this);
17769             this.menu.un("hide", ml.hide,  this);
17770         }
17771     },
17772
17773     // private
17774     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17775     onTriggerClick : function(){
17776         if(this.disabled){
17777             return;
17778         }
17779         if(this.menu == null){
17780             this.menu = new Roo.menu.DateMenu();
17781         }
17782         Roo.apply(this.menu.picker,  {
17783             showClear: this.allowBlank,
17784             minDate : this.minValue,
17785             maxDate : this.maxValue,
17786             disabledDatesRE : this.ddMatch,
17787             disabledDatesText : this.disabledDatesText,
17788             disabledDays : this.disabledDays,
17789             disabledDaysText : this.disabledDaysText,
17790             format : this.useIso ? 'Y-m-d' : this.format,
17791             minText : String.format(this.minText, this.formatDate(this.minValue)),
17792             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17793         });
17794         this.menu.on(Roo.apply({}, this.menuListeners, {
17795             scope:this
17796         }));
17797         this.menu.picker.setValue(this.getValue() || new Date());
17798         this.menu.show(this.el, "tl-bl?");
17799     },
17800
17801     beforeBlur : function(){
17802         var v = this.parseDate(this.getRawValue());
17803         if(v){
17804             this.setValue(v);
17805         }
17806     },
17807
17808     /*@
17809      * overide
17810      * 
17811      */
17812     isDirty : function() {
17813         if(this.disabled) {
17814             return false;
17815         }
17816         
17817         if(typeof(this.startValue) === 'undefined'){
17818             return false;
17819         }
17820         
17821         return String(this.getValue()) !== String(this.startValue);
17822         
17823     },
17824     // @overide
17825     cleanLeadingSpace : function(e)
17826     {
17827        return;
17828     }
17829     
17830 });/*
17831  * Based on:
17832  * Ext JS Library 1.1.1
17833  * Copyright(c) 2006-2007, Ext JS, LLC.
17834  *
17835  * Originally Released Under LGPL - original licence link has changed is not relivant.
17836  *
17837  * Fork - LGPL
17838  * <script type="text/javascript">
17839  */
17840  
17841 /**
17842  * @class Roo.form.MonthField
17843  * @extends Roo.form.TriggerField
17844  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17845 * @constructor
17846 * Create a new MonthField
17847 * @param {Object} config
17848  */
17849 Roo.form.MonthField = function(config){
17850     
17851     Roo.form.MonthField.superclass.constructor.call(this, config);
17852     
17853       this.addEvents({
17854          
17855         /**
17856          * @event select
17857          * Fires when a date is selected
17858              * @param {Roo.form.MonthFieeld} combo This combo box
17859              * @param {Date} date The date selected
17860              */
17861         'select' : true
17862          
17863     });
17864     
17865     
17866     if(typeof this.minValue == "string") {
17867         this.minValue = this.parseDate(this.minValue);
17868     }
17869     if(typeof this.maxValue == "string") {
17870         this.maxValue = this.parseDate(this.maxValue);
17871     }
17872     this.ddMatch = null;
17873     if(this.disabledDates){
17874         var dd = this.disabledDates;
17875         var re = "(?:";
17876         for(var i = 0; i < dd.length; i++){
17877             re += dd[i];
17878             if(i != dd.length-1) {
17879                 re += "|";
17880             }
17881         }
17882         this.ddMatch = new RegExp(re + ")");
17883     }
17884 };
17885
17886 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17887     /**
17888      * @cfg {String} format
17889      * The default date format string which can be overriden for localization support.  The format must be
17890      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17891      */
17892     format : "M Y",
17893     /**
17894      * @cfg {String} altFormats
17895      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17896      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17897      */
17898     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17899     /**
17900      * @cfg {Array} disabledDays
17901      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17902      */
17903     disabledDays : [0,1,2,3,4,5,6],
17904     /**
17905      * @cfg {String} disabledDaysText
17906      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17907      */
17908     disabledDaysText : "Disabled",
17909     /**
17910      * @cfg {Array} disabledDates
17911      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17912      * expression so they are very powerful. Some examples:
17913      * <ul>
17914      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17915      * <li>["03/08", "09/16"] would disable those days for every year</li>
17916      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17917      * <li>["03/../2006"] would disable every day in March 2006</li>
17918      * <li>["^03"] would disable every day in every March</li>
17919      * </ul>
17920      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17921      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17922      */
17923     disabledDates : null,
17924     /**
17925      * @cfg {String} disabledDatesText
17926      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17927      */
17928     disabledDatesText : "Disabled",
17929     /**
17930      * @cfg {Date/String} minValue
17931      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17932      * valid format (defaults to null).
17933      */
17934     minValue : null,
17935     /**
17936      * @cfg {Date/String} maxValue
17937      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17938      * valid format (defaults to null).
17939      */
17940     maxValue : null,
17941     /**
17942      * @cfg {String} minText
17943      * The error text to display when the date in the cell is before minValue (defaults to
17944      * 'The date in this field must be after {minValue}').
17945      */
17946     minText : "The date in this field must be equal to or after {0}",
17947     /**
17948      * @cfg {String} maxTextf
17949      * The error text to display when the date in the cell is after maxValue (defaults to
17950      * 'The date in this field must be before {maxValue}').
17951      */
17952     maxText : "The date in this field must be equal to or before {0}",
17953     /**
17954      * @cfg {String} invalidText
17955      * The error text to display when the date in the field is invalid (defaults to
17956      * '{value} is not a valid date - it must be in the format {format}').
17957      */
17958     invalidText : "{0} is not a valid date - it must be in the format {1}",
17959     /**
17960      * @cfg {String} triggerClass
17961      * An additional CSS class used to style the trigger button.  The trigger will always get the
17962      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17963      * which displays a calendar icon).
17964      */
17965     triggerClass : 'x-form-date-trigger',
17966     
17967
17968     /**
17969      * @cfg {Boolean} useIso
17970      * if enabled, then the date field will use a hidden field to store the 
17971      * real value as iso formated date. default (true)
17972      */ 
17973     useIso : true,
17974     /**
17975      * @cfg {String/Object} autoCreate
17976      * A DomHelper element spec, or true for a default element spec (defaults to
17977      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17978      */ 
17979     // private
17980     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17981     
17982     // private
17983     hiddenField: false,
17984     
17985     hideMonthPicker : false,
17986     
17987     onRender : function(ct, position)
17988     {
17989         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
17990         if (this.useIso) {
17991             this.el.dom.removeAttribute('name'); 
17992             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17993                     'before', true);
17994             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17995             // prevent input submission
17996             this.hiddenName = this.name;
17997         }
17998             
17999             
18000     },
18001     
18002     // private
18003     validateValue : function(value)
18004     {
18005         value = this.formatDate(value);
18006         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18007             return false;
18008         }
18009         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18010              return true;
18011         }
18012         var svalue = value;
18013         value = this.parseDate(value);
18014         if(!value){
18015             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18016             return false;
18017         }
18018         var time = value.getTime();
18019         if(this.minValue && time < this.minValue.getTime()){
18020             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18021             return false;
18022         }
18023         if(this.maxValue && time > this.maxValue.getTime()){
18024             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18025             return false;
18026         }
18027         /*if(this.disabledDays){
18028             var day = value.getDay();
18029             for(var i = 0; i < this.disabledDays.length; i++) {
18030                 if(day === this.disabledDays[i]){
18031                     this.markInvalid(this.disabledDaysText);
18032                     return false;
18033                 }
18034             }
18035         }
18036         */
18037         var fvalue = this.formatDate(value);
18038         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18039             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18040             return false;
18041         }
18042         */
18043         return true;
18044     },
18045
18046     // private
18047     // Provides logic to override the default TriggerField.validateBlur which just returns true
18048     validateBlur : function(){
18049         return !this.menu || !this.menu.isVisible();
18050     },
18051
18052     /**
18053      * Returns the current date value of the date field.
18054      * @return {Date} The date value
18055      */
18056     getValue : function(){
18057         
18058         
18059         
18060         return  this.hiddenField ?
18061                 this.hiddenField.value :
18062                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18063     },
18064
18065     /**
18066      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18067      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18068      * (the default format used is "m/d/y").
18069      * <br />Usage:
18070      * <pre><code>
18071 //All of these calls set the same date value (May 4, 2006)
18072
18073 //Pass a date object:
18074 var dt = new Date('5/4/06');
18075 monthField.setValue(dt);
18076
18077 //Pass a date string (default format):
18078 monthField.setValue('5/4/06');
18079
18080 //Pass a date string (custom format):
18081 monthField.format = 'Y-m-d';
18082 monthField.setValue('2006-5-4');
18083 </code></pre>
18084      * @param {String/Date} date The date or valid date string
18085      */
18086     setValue : function(date){
18087         Roo.log('month setValue' + date);
18088         // can only be first of month..
18089         
18090         var val = this.parseDate(date);
18091         
18092         if (this.hiddenField) {
18093             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18094         }
18095         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18096         this.value = this.parseDate(date);
18097     },
18098
18099     // private
18100     parseDate : function(value){
18101         if(!value || value instanceof Date){
18102             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18103             return value;
18104         }
18105         var v = Date.parseDate(value, this.format);
18106         if (!v && this.useIso) {
18107             v = Date.parseDate(value, 'Y-m-d');
18108         }
18109         if (v) {
18110             // 
18111             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18112         }
18113         
18114         
18115         if(!v && this.altFormats){
18116             if(!this.altFormatsArray){
18117                 this.altFormatsArray = this.altFormats.split("|");
18118             }
18119             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18120                 v = Date.parseDate(value, this.altFormatsArray[i]);
18121             }
18122         }
18123         return v;
18124     },
18125
18126     // private
18127     formatDate : function(date, fmt){
18128         return (!date || !(date instanceof Date)) ?
18129                date : date.dateFormat(fmt || this.format);
18130     },
18131
18132     // private
18133     menuListeners : {
18134         select: function(m, d){
18135             this.setValue(d);
18136             this.fireEvent('select', this, d);
18137         },
18138         show : function(){ // retain focus styling
18139             this.onFocus();
18140         },
18141         hide : function(){
18142             this.focus.defer(10, this);
18143             var ml = this.menuListeners;
18144             this.menu.un("select", ml.select,  this);
18145             this.menu.un("show", ml.show,  this);
18146             this.menu.un("hide", ml.hide,  this);
18147         }
18148     },
18149     // private
18150     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18151     onTriggerClick : function(){
18152         if(this.disabled){
18153             return;
18154         }
18155         if(this.menu == null){
18156             this.menu = new Roo.menu.DateMenu();
18157            
18158         }
18159         
18160         Roo.apply(this.menu.picker,  {
18161             
18162             showClear: this.allowBlank,
18163             minDate : this.minValue,
18164             maxDate : this.maxValue,
18165             disabledDatesRE : this.ddMatch,
18166             disabledDatesText : this.disabledDatesText,
18167             
18168             format : this.useIso ? 'Y-m-d' : this.format,
18169             minText : String.format(this.minText, this.formatDate(this.minValue)),
18170             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18171             
18172         });
18173          this.menu.on(Roo.apply({}, this.menuListeners, {
18174             scope:this
18175         }));
18176        
18177         
18178         var m = this.menu;
18179         var p = m.picker;
18180         
18181         // hide month picker get's called when we called by 'before hide';
18182         
18183         var ignorehide = true;
18184         p.hideMonthPicker  = function(disableAnim){
18185             if (ignorehide) {
18186                 return;
18187             }
18188              if(this.monthPicker){
18189                 Roo.log("hideMonthPicker called");
18190                 if(disableAnim === true){
18191                     this.monthPicker.hide();
18192                 }else{
18193                     this.monthPicker.slideOut('t', {duration:.2});
18194                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18195                     p.fireEvent("select", this, this.value);
18196                     m.hide();
18197                 }
18198             }
18199         }
18200         
18201         Roo.log('picker set value');
18202         Roo.log(this.getValue());
18203         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18204         m.show(this.el, 'tl-bl?');
18205         ignorehide  = false;
18206         // this will trigger hideMonthPicker..
18207         
18208         
18209         // hidden the day picker
18210         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18211         
18212         
18213         
18214       
18215         
18216         p.showMonthPicker.defer(100, p);
18217     
18218         
18219        
18220     },
18221
18222     beforeBlur : function(){
18223         var v = this.parseDate(this.getRawValue());
18224         if(v){
18225             this.setValue(v);
18226         }
18227     }
18228
18229     /** @cfg {Boolean} grow @hide */
18230     /** @cfg {Number} growMin @hide */
18231     /** @cfg {Number} growMax @hide */
18232     /**
18233      * @hide
18234      * @method autoSize
18235      */
18236 });/*
18237  * Based on:
18238  * Ext JS Library 1.1.1
18239  * Copyright(c) 2006-2007, Ext JS, LLC.
18240  *
18241  * Originally Released Under LGPL - original licence link has changed is not relivant.
18242  *
18243  * Fork - LGPL
18244  * <script type="text/javascript">
18245  */
18246  
18247
18248 /**
18249  * @class Roo.form.ComboBox
18250  * @extends Roo.form.TriggerField
18251  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18252  * @constructor
18253  * Create a new ComboBox.
18254  * @param {Object} config Configuration options
18255  */
18256 Roo.form.ComboBox = function(config){
18257     Roo.form.ComboBox.superclass.constructor.call(this, config);
18258     this.addEvents({
18259         /**
18260          * @event expand
18261          * Fires when the dropdown list is expanded
18262              * @param {Roo.form.ComboBox} combo This combo box
18263              */
18264         'expand' : true,
18265         /**
18266          * @event collapse
18267          * Fires when the dropdown list is collapsed
18268              * @param {Roo.form.ComboBox} combo This combo box
18269              */
18270         'collapse' : true,
18271         /**
18272          * @event beforeselect
18273          * Fires before a list item is selected. Return false to cancel the selection.
18274              * @param {Roo.form.ComboBox} combo This combo box
18275              * @param {Roo.data.Record} record The data record returned from the underlying store
18276              * @param {Number} index The index of the selected item in the dropdown list
18277              */
18278         'beforeselect' : true,
18279         /**
18280          * @event select
18281          * Fires when a list item is selected
18282              * @param {Roo.form.ComboBox} combo This combo box
18283              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18284              * @param {Number} index The index of the selected item in the dropdown list
18285              */
18286         'select' : true,
18287         /**
18288          * @event beforequery
18289          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18290          * The event object passed has these properties:
18291              * @param {Roo.form.ComboBox} combo This combo box
18292              * @param {String} query The query
18293              * @param {Boolean} forceAll true to force "all" query
18294              * @param {Boolean} cancel true to cancel the query
18295              * @param {Object} e The query event object
18296              */
18297         'beforequery': true,
18298          /**
18299          * @event add
18300          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18301              * @param {Roo.form.ComboBox} combo This combo box
18302              */
18303         'add' : true,
18304         /**
18305          * @event edit
18306          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18307              * @param {Roo.form.ComboBox} combo This combo box
18308              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18309              */
18310         'edit' : true
18311         
18312         
18313     });
18314     if(this.transform){
18315         this.allowDomMove = false;
18316         var s = Roo.getDom(this.transform);
18317         if(!this.hiddenName){
18318             this.hiddenName = s.name;
18319         }
18320         if(!this.store){
18321             this.mode = 'local';
18322             var d = [], opts = s.options;
18323             for(var i = 0, len = opts.length;i < len; i++){
18324                 var o = opts[i];
18325                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18326                 if(o.selected) {
18327                     this.value = value;
18328                 }
18329                 d.push([value, o.text]);
18330             }
18331             this.store = new Roo.data.SimpleStore({
18332                 'id': 0,
18333                 fields: ['value', 'text'],
18334                 data : d
18335             });
18336             this.valueField = 'value';
18337             this.displayField = 'text';
18338         }
18339         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18340         if(!this.lazyRender){
18341             this.target = true;
18342             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18343             s.parentNode.removeChild(s); // remove it
18344             this.render(this.el.parentNode);
18345         }else{
18346             s.parentNode.removeChild(s); // remove it
18347         }
18348
18349     }
18350     if (this.store) {
18351         this.store = Roo.factory(this.store, Roo.data);
18352     }
18353     
18354     this.selectedIndex = -1;
18355     if(this.mode == 'local'){
18356         if(config.queryDelay === undefined){
18357             this.queryDelay = 10;
18358         }
18359         if(config.minChars === undefined){
18360             this.minChars = 0;
18361         }
18362     }
18363 };
18364
18365 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18366     /**
18367      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18368      */
18369     /**
18370      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18371      * rendering into an Roo.Editor, defaults to false)
18372      */
18373     /**
18374      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18375      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18376      */
18377     /**
18378      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18379      */
18380     /**
18381      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18382      * the dropdown list (defaults to undefined, with no header element)
18383      */
18384
18385      /**
18386      * @cfg {String/Roo.Template} tpl The template to use to render the output
18387      */
18388      
18389     // private
18390     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18391     /**
18392      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18393      */
18394     listWidth: undefined,
18395     /**
18396      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18397      * mode = 'remote' or 'text' if mode = 'local')
18398      */
18399     displayField: undefined,
18400     /**
18401      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18402      * mode = 'remote' or 'value' if mode = 'local'). 
18403      * Note: use of a valueField requires the user make a selection
18404      * in order for a value to be mapped.
18405      */
18406     valueField: undefined,
18407     
18408     
18409     /**
18410      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18411      * field's data value (defaults to the underlying DOM element's name)
18412      */
18413     hiddenName: undefined,
18414     /**
18415      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18416      */
18417     listClass: '',
18418     /**
18419      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18420      */
18421     selectedClass: 'x-combo-selected',
18422     /**
18423      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18424      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18425      * which displays a downward arrow icon).
18426      */
18427     triggerClass : 'x-form-arrow-trigger',
18428     /**
18429      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18430      */
18431     shadow:'sides',
18432     /**
18433      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18434      * anchor positions (defaults to 'tl-bl')
18435      */
18436     listAlign: 'tl-bl?',
18437     /**
18438      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18439      */
18440     maxHeight: 300,
18441     /**
18442      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18443      * query specified by the allQuery config option (defaults to 'query')
18444      */
18445     triggerAction: 'query',
18446     /**
18447      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18448      * (defaults to 4, does not apply if editable = false)
18449      */
18450     minChars : 4,
18451     /**
18452      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18453      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18454      */
18455     typeAhead: false,
18456     /**
18457      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18458      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18459      */
18460     queryDelay: 500,
18461     /**
18462      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18463      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18464      */
18465     pageSize: 0,
18466     /**
18467      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18468      * when editable = true (defaults to false)
18469      */
18470     selectOnFocus:false,
18471     /**
18472      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18473      */
18474     queryParam: 'query',
18475     /**
18476      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18477      * when mode = 'remote' (defaults to 'Loading...')
18478      */
18479     loadingText: 'Loading...',
18480     /**
18481      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18482      */
18483     resizable: false,
18484     /**
18485      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18486      */
18487     handleHeight : 8,
18488     /**
18489      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18490      * traditional select (defaults to true)
18491      */
18492     editable: true,
18493     /**
18494      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18495      */
18496     allQuery: '',
18497     /**
18498      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18499      */
18500     mode: 'remote',
18501     /**
18502      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18503      * listWidth has a higher value)
18504      */
18505     minListWidth : 70,
18506     /**
18507      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18508      * allow the user to set arbitrary text into the field (defaults to false)
18509      */
18510     forceSelection:false,
18511     /**
18512      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18513      * if typeAhead = true (defaults to 250)
18514      */
18515     typeAheadDelay : 250,
18516     /**
18517      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18518      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18519      */
18520     valueNotFoundText : undefined,
18521     /**
18522      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18523      */
18524     blockFocus : false,
18525     
18526     /**
18527      * @cfg {Boolean} disableClear Disable showing of clear button.
18528      */
18529     disableClear : false,
18530     /**
18531      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18532      */
18533     alwaysQuery : false,
18534     
18535     //private
18536     addicon : false,
18537     editicon: false,
18538     
18539     // element that contains real text value.. (when hidden is used..)
18540      
18541     // private
18542     onRender : function(ct, position)
18543     {
18544         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18545         
18546         if(this.hiddenName){
18547             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18548                     'before', true);
18549             this.hiddenField.value =
18550                 this.hiddenValue !== undefined ? this.hiddenValue :
18551                 this.value !== undefined ? this.value : '';
18552
18553             // prevent input submission
18554             this.el.dom.removeAttribute('name');
18555              
18556              
18557         }
18558         
18559         if(Roo.isGecko){
18560             this.el.dom.setAttribute('autocomplete', 'off');
18561         }
18562
18563         var cls = 'x-combo-list';
18564
18565         this.list = new Roo.Layer({
18566             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18567         });
18568
18569         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18570         this.list.setWidth(lw);
18571         this.list.swallowEvent('mousewheel');
18572         this.assetHeight = 0;
18573
18574         if(this.title){
18575             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18576             this.assetHeight += this.header.getHeight();
18577         }
18578
18579         this.innerList = this.list.createChild({cls:cls+'-inner'});
18580         this.innerList.on('mouseover', this.onViewOver, this);
18581         this.innerList.on('mousemove', this.onViewMove, this);
18582         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18583         
18584         if(this.allowBlank && !this.pageSize && !this.disableClear){
18585             this.footer = this.list.createChild({cls:cls+'-ft'});
18586             this.pageTb = new Roo.Toolbar(this.footer);
18587            
18588         }
18589         if(this.pageSize){
18590             this.footer = this.list.createChild({cls:cls+'-ft'});
18591             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18592                     {pageSize: this.pageSize});
18593             
18594         }
18595         
18596         if (this.pageTb && this.allowBlank && !this.disableClear) {
18597             var _this = this;
18598             this.pageTb.add(new Roo.Toolbar.Fill(), {
18599                 cls: 'x-btn-icon x-btn-clear',
18600                 text: '&#160;',
18601                 handler: function()
18602                 {
18603                     _this.collapse();
18604                     _this.clearValue();
18605                     _this.onSelect(false, -1);
18606                 }
18607             });
18608         }
18609         if (this.footer) {
18610             this.assetHeight += this.footer.getHeight();
18611         }
18612         
18613
18614         if(!this.tpl){
18615             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18616         }
18617
18618         this.view = new Roo.View(this.innerList, this.tpl, {
18619             singleSelect:true,
18620             store: this.store,
18621             selectedClass: this.selectedClass
18622         });
18623
18624         this.view.on('click', this.onViewClick, this);
18625
18626         this.store.on('beforeload', this.onBeforeLoad, this);
18627         this.store.on('load', this.onLoad, this);
18628         this.store.on('loadexception', this.onLoadException, this);
18629
18630         if(this.resizable){
18631             this.resizer = new Roo.Resizable(this.list,  {
18632                pinned:true, handles:'se'
18633             });
18634             this.resizer.on('resize', function(r, w, h){
18635                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18636                 this.listWidth = w;
18637                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18638                 this.restrictHeight();
18639             }, this);
18640             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18641         }
18642         if(!this.editable){
18643             this.editable = true;
18644             this.setEditable(false);
18645         }  
18646         
18647         
18648         if (typeof(this.events.add.listeners) != 'undefined') {
18649             
18650             this.addicon = this.wrap.createChild(
18651                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18652        
18653             this.addicon.on('click', function(e) {
18654                 this.fireEvent('add', this);
18655             }, this);
18656         }
18657         if (typeof(this.events.edit.listeners) != 'undefined') {
18658             
18659             this.editicon = this.wrap.createChild(
18660                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18661             if (this.addicon) {
18662                 this.editicon.setStyle('margin-left', '40px');
18663             }
18664             this.editicon.on('click', function(e) {
18665                 
18666                 // we fire even  if inothing is selected..
18667                 this.fireEvent('edit', this, this.lastData );
18668                 
18669             }, this);
18670         }
18671         
18672         
18673         
18674     },
18675
18676     // private
18677     initEvents : function(){
18678         Roo.form.ComboBox.superclass.initEvents.call(this);
18679
18680         this.keyNav = new Roo.KeyNav(this.el, {
18681             "up" : function(e){
18682                 this.inKeyMode = true;
18683                 this.selectPrev();
18684             },
18685
18686             "down" : function(e){
18687                 if(!this.isExpanded()){
18688                     this.onTriggerClick();
18689                 }else{
18690                     this.inKeyMode = true;
18691                     this.selectNext();
18692                 }
18693             },
18694
18695             "enter" : function(e){
18696                 this.onViewClick();
18697                 //return true;
18698             },
18699
18700             "esc" : function(e){
18701                 this.collapse();
18702             },
18703
18704             "tab" : function(e){
18705                 this.onViewClick(false);
18706                 this.fireEvent("specialkey", this, e);
18707                 return true;
18708             },
18709
18710             scope : this,
18711
18712             doRelay : function(foo, bar, hname){
18713                 if(hname == 'down' || this.scope.isExpanded()){
18714                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18715                 }
18716                 return true;
18717             },
18718
18719             forceKeyDown: true
18720         });
18721         this.queryDelay = Math.max(this.queryDelay || 10,
18722                 this.mode == 'local' ? 10 : 250);
18723         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18724         if(this.typeAhead){
18725             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18726         }
18727         if(this.editable !== false){
18728             this.el.on("keyup", this.onKeyUp, this);
18729         }
18730         if(this.forceSelection){
18731             this.on('blur', this.doForce, this);
18732         }
18733     },
18734
18735     onDestroy : function(){
18736         if(this.view){
18737             this.view.setStore(null);
18738             this.view.el.removeAllListeners();
18739             this.view.el.remove();
18740             this.view.purgeListeners();
18741         }
18742         if(this.list){
18743             this.list.destroy();
18744         }
18745         if(this.store){
18746             this.store.un('beforeload', this.onBeforeLoad, this);
18747             this.store.un('load', this.onLoad, this);
18748             this.store.un('loadexception', this.onLoadException, this);
18749         }
18750         Roo.form.ComboBox.superclass.onDestroy.call(this);
18751     },
18752
18753     // private
18754     fireKey : function(e){
18755         if(e.isNavKeyPress() && !this.list.isVisible()){
18756             this.fireEvent("specialkey", this, e);
18757         }
18758     },
18759
18760     // private
18761     onResize: function(w, h){
18762         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18763         
18764         if(typeof w != 'number'){
18765             // we do not handle it!?!?
18766             return;
18767         }
18768         var tw = this.trigger.getWidth();
18769         tw += this.addicon ? this.addicon.getWidth() : 0;
18770         tw += this.editicon ? this.editicon.getWidth() : 0;
18771         var x = w - tw;
18772         this.el.setWidth( this.adjustWidth('input', x));
18773             
18774         this.trigger.setStyle('left', x+'px');
18775         
18776         if(this.list && this.listWidth === undefined){
18777             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18778             this.list.setWidth(lw);
18779             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18780         }
18781         
18782     
18783         
18784     },
18785
18786     /**
18787      * Allow or prevent the user from directly editing the field text.  If false is passed,
18788      * the user will only be able to select from the items defined in the dropdown list.  This method
18789      * is the runtime equivalent of setting the 'editable' config option at config time.
18790      * @param {Boolean} value True to allow the user to directly edit the field text
18791      */
18792     setEditable : function(value){
18793         if(value == this.editable){
18794             return;
18795         }
18796         this.editable = value;
18797         if(!value){
18798             this.el.dom.setAttribute('readOnly', true);
18799             this.el.on('mousedown', this.onTriggerClick,  this);
18800             this.el.addClass('x-combo-noedit');
18801         }else{
18802             this.el.dom.setAttribute('readOnly', false);
18803             this.el.un('mousedown', this.onTriggerClick,  this);
18804             this.el.removeClass('x-combo-noedit');
18805         }
18806     },
18807
18808     // private
18809     onBeforeLoad : function(){
18810         if(!this.hasFocus){
18811             return;
18812         }
18813         this.innerList.update(this.loadingText ?
18814                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18815         this.restrictHeight();
18816         this.selectedIndex = -1;
18817     },
18818
18819     // private
18820     onLoad : function(){
18821         if(!this.hasFocus){
18822             return;
18823         }
18824         if(this.store.getCount() > 0){
18825             this.expand();
18826             this.restrictHeight();
18827             if(this.lastQuery == this.allQuery){
18828                 if(this.editable){
18829                     this.el.dom.select();
18830                 }
18831                 if(!this.selectByValue(this.value, true)){
18832                     this.select(0, true);
18833                 }
18834             }else{
18835                 this.selectNext();
18836                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18837                     this.taTask.delay(this.typeAheadDelay);
18838                 }
18839             }
18840         }else{
18841             this.onEmptyResults();
18842         }
18843         //this.el.focus();
18844     },
18845     // private
18846     onLoadException : function()
18847     {
18848         this.collapse();
18849         Roo.log(this.store.reader.jsonData);
18850         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18851             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18852         }
18853         
18854         
18855     },
18856     // private
18857     onTypeAhead : function(){
18858         if(this.store.getCount() > 0){
18859             var r = this.store.getAt(0);
18860             var newValue = r.data[this.displayField];
18861             var len = newValue.length;
18862             var selStart = this.getRawValue().length;
18863             if(selStart != len){
18864                 this.setRawValue(newValue);
18865                 this.selectText(selStart, newValue.length);
18866             }
18867         }
18868     },
18869
18870     // private
18871     onSelect : function(record, index){
18872         if(this.fireEvent('beforeselect', this, record, index) !== false){
18873             this.setFromData(index > -1 ? record.data : false);
18874             this.collapse();
18875             this.fireEvent('select', this, record, index);
18876         }
18877     },
18878
18879     /**
18880      * Returns the currently selected field value or empty string if no value is set.
18881      * @return {String} value The selected value
18882      */
18883     getValue : function(){
18884         if(this.valueField){
18885             return typeof this.value != 'undefined' ? this.value : '';
18886         }
18887         return Roo.form.ComboBox.superclass.getValue.call(this);
18888     },
18889
18890     /**
18891      * Clears any text/value currently set in the field
18892      */
18893     clearValue : function(){
18894         if(this.hiddenField){
18895             this.hiddenField.value = '';
18896         }
18897         this.value = '';
18898         this.setRawValue('');
18899         this.lastSelectionText = '';
18900         
18901     },
18902
18903     /**
18904      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18905      * will be displayed in the field.  If the value does not match the data value of an existing item,
18906      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18907      * Otherwise the field will be blank (although the value will still be set).
18908      * @param {String} value The value to match
18909      */
18910     setValue : function(v){
18911         var text = v;
18912         if(this.valueField){
18913             var r = this.findRecord(this.valueField, v);
18914             if(r){
18915                 text = r.data[this.displayField];
18916             }else if(this.valueNotFoundText !== undefined){
18917                 text = this.valueNotFoundText;
18918             }
18919         }
18920         this.lastSelectionText = text;
18921         if(this.hiddenField){
18922             this.hiddenField.value = v;
18923         }
18924         Roo.form.ComboBox.superclass.setValue.call(this, text);
18925         this.value = v;
18926     },
18927     /**
18928      * @property {Object} the last set data for the element
18929      */
18930     
18931     lastData : false,
18932     /**
18933      * Sets the value of the field based on a object which is related to the record format for the store.
18934      * @param {Object} value the value to set as. or false on reset?
18935      */
18936     setFromData : function(o){
18937         var dv = ''; // display value
18938         var vv = ''; // value value..
18939         this.lastData = o;
18940         if (this.displayField) {
18941             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18942         } else {
18943             // this is an error condition!!!
18944             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18945         }
18946         
18947         if(this.valueField){
18948             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18949         }
18950         if(this.hiddenField){
18951             this.hiddenField.value = vv;
18952             
18953             this.lastSelectionText = dv;
18954             Roo.form.ComboBox.superclass.setValue.call(this, dv);
18955             this.value = vv;
18956             return;
18957         }
18958         // no hidden field.. - we store the value in 'value', but still display
18959         // display field!!!!
18960         this.lastSelectionText = dv;
18961         Roo.form.ComboBox.superclass.setValue.call(this, dv);
18962         this.value = vv;
18963         
18964         
18965     },
18966     // private
18967     reset : function(){
18968         // overridden so that last data is reset..
18969         this.setValue(this.resetValue);
18970         this.originalValue = this.getValue();
18971         this.clearInvalid();
18972         this.lastData = false;
18973         if (this.view) {
18974             this.view.clearSelections();
18975         }
18976     },
18977     // private
18978     findRecord : function(prop, value){
18979         var record;
18980         if(this.store.getCount() > 0){
18981             this.store.each(function(r){
18982                 if(r.data[prop] == value){
18983                     record = r;
18984                     return false;
18985                 }
18986                 return true;
18987             });
18988         }
18989         return record;
18990     },
18991     
18992     getName: function()
18993     {
18994         // returns hidden if it's set..
18995         if (!this.rendered) {return ''};
18996         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18997         
18998     },
18999     // private
19000     onViewMove : function(e, t){
19001         this.inKeyMode = false;
19002     },
19003
19004     // private
19005     onViewOver : function(e, t){
19006         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19007             return;
19008         }
19009         var item = this.view.findItemFromChild(t);
19010         if(item){
19011             var index = this.view.indexOf(item);
19012             this.select(index, false);
19013         }
19014     },
19015
19016     // private
19017     onViewClick : function(doFocus)
19018     {
19019         var index = this.view.getSelectedIndexes()[0];
19020         var r = this.store.getAt(index);
19021         if(r){
19022             this.onSelect(r, index);
19023         }
19024         if(doFocus !== false && !this.blockFocus){
19025             this.el.focus();
19026         }
19027     },
19028
19029     // private
19030     restrictHeight : function(){
19031         this.innerList.dom.style.height = '';
19032         var inner = this.innerList.dom;
19033         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19034         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19035         this.list.beginUpdate();
19036         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19037         this.list.alignTo(this.el, this.listAlign);
19038         this.list.endUpdate();
19039     },
19040
19041     // private
19042     onEmptyResults : function(){
19043         this.collapse();
19044     },
19045
19046     /**
19047      * Returns true if the dropdown list is expanded, else false.
19048      */
19049     isExpanded : function(){
19050         return this.list.isVisible();
19051     },
19052
19053     /**
19054      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19055      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19056      * @param {String} value The data value of the item to select
19057      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19058      * selected item if it is not currently in view (defaults to true)
19059      * @return {Boolean} True if the value matched an item in the list, else false
19060      */
19061     selectByValue : function(v, scrollIntoView){
19062         if(v !== undefined && v !== null){
19063             var r = this.findRecord(this.valueField || this.displayField, v);
19064             if(r){
19065                 this.select(this.store.indexOf(r), scrollIntoView);
19066                 return true;
19067             }
19068         }
19069         return false;
19070     },
19071
19072     /**
19073      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19074      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19075      * @param {Number} index The zero-based index of the list item to select
19076      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19077      * selected item if it is not currently in view (defaults to true)
19078      */
19079     select : function(index, scrollIntoView){
19080         this.selectedIndex = index;
19081         this.view.select(index);
19082         if(scrollIntoView !== false){
19083             var el = this.view.getNode(index);
19084             if(el){
19085                 this.innerList.scrollChildIntoView(el, false);
19086             }
19087         }
19088     },
19089
19090     // private
19091     selectNext : function(){
19092         var ct = this.store.getCount();
19093         if(ct > 0){
19094             if(this.selectedIndex == -1){
19095                 this.select(0);
19096             }else if(this.selectedIndex < ct-1){
19097                 this.select(this.selectedIndex+1);
19098             }
19099         }
19100     },
19101
19102     // private
19103     selectPrev : function(){
19104         var ct = this.store.getCount();
19105         if(ct > 0){
19106             if(this.selectedIndex == -1){
19107                 this.select(0);
19108             }else if(this.selectedIndex != 0){
19109                 this.select(this.selectedIndex-1);
19110             }
19111         }
19112     },
19113
19114     // private
19115     onKeyUp : function(e){
19116         if(this.editable !== false && !e.isSpecialKey()){
19117             this.lastKey = e.getKey();
19118             this.dqTask.delay(this.queryDelay);
19119         }
19120     },
19121
19122     // private
19123     validateBlur : function(){
19124         return !this.list || !this.list.isVisible();   
19125     },
19126
19127     // private
19128     initQuery : function(){
19129         this.doQuery(this.getRawValue());
19130     },
19131
19132     // private
19133     doForce : function(){
19134         if(this.el.dom.value.length > 0){
19135             this.el.dom.value =
19136                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19137              
19138         }
19139     },
19140
19141     /**
19142      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19143      * query allowing the query action to be canceled if needed.
19144      * @param {String} query The SQL query to execute
19145      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19146      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19147      * saved in the current store (defaults to false)
19148      */
19149     doQuery : function(q, forceAll){
19150         if(q === undefined || q === null){
19151             q = '';
19152         }
19153         var qe = {
19154             query: q,
19155             forceAll: forceAll,
19156             combo: this,
19157             cancel:false
19158         };
19159         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19160             return false;
19161         }
19162         q = qe.query;
19163         forceAll = qe.forceAll;
19164         if(forceAll === true || (q.length >= this.minChars)){
19165             if(this.lastQuery != q || this.alwaysQuery){
19166                 this.lastQuery = q;
19167                 if(this.mode == 'local'){
19168                     this.selectedIndex = -1;
19169                     if(forceAll){
19170                         this.store.clearFilter();
19171                     }else{
19172                         this.store.filter(this.displayField, q);
19173                     }
19174                     this.onLoad();
19175                 }else{
19176                     this.store.baseParams[this.queryParam] = q;
19177                     this.store.load({
19178                         params: this.getParams(q)
19179                     });
19180                     this.expand();
19181                 }
19182             }else{
19183                 this.selectedIndex = -1;
19184                 this.onLoad();   
19185             }
19186         }
19187     },
19188
19189     // private
19190     getParams : function(q){
19191         var p = {};
19192         //p[this.queryParam] = q;
19193         if(this.pageSize){
19194             p.start = 0;
19195             p.limit = this.pageSize;
19196         }
19197         return p;
19198     },
19199
19200     /**
19201      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19202      */
19203     collapse : function(){
19204         if(!this.isExpanded()){
19205             return;
19206         }
19207         this.list.hide();
19208         Roo.get(document).un('mousedown', this.collapseIf, this);
19209         Roo.get(document).un('mousewheel', this.collapseIf, this);
19210         if (!this.editable) {
19211             Roo.get(document).un('keydown', this.listKeyPress, this);
19212         }
19213         this.fireEvent('collapse', this);
19214     },
19215
19216     // private
19217     collapseIf : function(e){
19218         if(!e.within(this.wrap) && !e.within(this.list)){
19219             this.collapse();
19220         }
19221     },
19222
19223     /**
19224      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19225      */
19226     expand : function(){
19227         if(this.isExpanded() || !this.hasFocus){
19228             return;
19229         }
19230         this.list.alignTo(this.el, this.listAlign);
19231         this.list.show();
19232         Roo.get(document).on('mousedown', this.collapseIf, this);
19233         Roo.get(document).on('mousewheel', this.collapseIf, this);
19234         if (!this.editable) {
19235             Roo.get(document).on('keydown', this.listKeyPress, this);
19236         }
19237         
19238         this.fireEvent('expand', this);
19239     },
19240
19241     // private
19242     // Implements the default empty TriggerField.onTriggerClick function
19243     onTriggerClick : function(){
19244         if(this.disabled){
19245             return;
19246         }
19247         if(this.isExpanded()){
19248             this.collapse();
19249             if (!this.blockFocus) {
19250                 this.el.focus();
19251             }
19252             
19253         }else {
19254             this.hasFocus = true;
19255             if(this.triggerAction == 'all') {
19256                 this.doQuery(this.allQuery, true);
19257             } else {
19258                 this.doQuery(this.getRawValue());
19259             }
19260             if (!this.blockFocus) {
19261                 this.el.focus();
19262             }
19263         }
19264     },
19265     listKeyPress : function(e)
19266     {
19267         //Roo.log('listkeypress');
19268         // scroll to first matching element based on key pres..
19269         if (e.isSpecialKey()) {
19270             return false;
19271         }
19272         var k = String.fromCharCode(e.getKey()).toUpperCase();
19273         //Roo.log(k);
19274         var match  = false;
19275         var csel = this.view.getSelectedNodes();
19276         var cselitem = false;
19277         if (csel.length) {
19278             var ix = this.view.indexOf(csel[0]);
19279             cselitem  = this.store.getAt(ix);
19280             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19281                 cselitem = false;
19282             }
19283             
19284         }
19285         
19286         this.store.each(function(v) { 
19287             if (cselitem) {
19288                 // start at existing selection.
19289                 if (cselitem.id == v.id) {
19290                     cselitem = false;
19291                 }
19292                 return;
19293             }
19294                 
19295             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19296                 match = this.store.indexOf(v);
19297                 return false;
19298             }
19299         }, this);
19300         
19301         if (match === false) {
19302             return true; // no more action?
19303         }
19304         // scroll to?
19305         this.view.select(match);
19306         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19307         sn.scrollIntoView(sn.dom.parentNode, false);
19308     } 
19309
19310     /** 
19311     * @cfg {Boolean} grow 
19312     * @hide 
19313     */
19314     /** 
19315     * @cfg {Number} growMin 
19316     * @hide 
19317     */
19318     /** 
19319     * @cfg {Number} growMax 
19320     * @hide 
19321     */
19322     /**
19323      * @hide
19324      * @method autoSize
19325      */
19326 });/*
19327  * Copyright(c) 2010-2012, Roo J Solutions Limited
19328  *
19329  * Licence LGPL
19330  *
19331  */
19332
19333 /**
19334  * @class Roo.form.ComboBoxArray
19335  * @extends Roo.form.TextField
19336  * A facebook style adder... for lists of email / people / countries  etc...
19337  * pick multiple items from a combo box, and shows each one.
19338  *
19339  *  Fred [x]  Brian [x]  [Pick another |v]
19340  *
19341  *
19342  *  For this to work: it needs various extra information
19343  *    - normal combo problay has
19344  *      name, hiddenName
19345  *    + displayField, valueField
19346  *
19347  *    For our purpose...
19348  *
19349  *
19350  *   If we change from 'extends' to wrapping...
19351  *   
19352  *  
19353  *
19354  
19355  
19356  * @constructor
19357  * Create a new ComboBoxArray.
19358  * @param {Object} config Configuration options
19359  */
19360  
19361
19362 Roo.form.ComboBoxArray = function(config)
19363 {
19364     this.addEvents({
19365         /**
19366          * @event beforeremove
19367          * Fires before remove the value from the list
19368              * @param {Roo.form.ComboBoxArray} _self This combo box array
19369              * @param {Roo.form.ComboBoxArray.Item} item removed item
19370              */
19371         'beforeremove' : true,
19372         /**
19373          * @event remove
19374          * Fires when remove the value from the list
19375              * @param {Roo.form.ComboBoxArray} _self This combo box array
19376              * @param {Roo.form.ComboBoxArray.Item} item removed item
19377              */
19378         'remove' : true
19379         
19380         
19381     });
19382     
19383     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19384     
19385     this.items = new Roo.util.MixedCollection(false);
19386     
19387     // construct the child combo...
19388     
19389     
19390     
19391     
19392    
19393     
19394 }
19395
19396  
19397 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19398
19399     /**
19400      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19401      */
19402     
19403     lastData : false,
19404     
19405     // behavies liek a hiddne field
19406     inputType:      'hidden',
19407     /**
19408      * @cfg {Number} width The width of the box that displays the selected element
19409      */ 
19410     width:          300,
19411
19412     
19413     
19414     /**
19415      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19416      */
19417     name : false,
19418     /**
19419      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19420      */
19421     hiddenName : false,
19422       /**
19423      * @cfg {String} seperator    The value seperator normally ',' 
19424      */
19425     seperator : ',',
19426     
19427     // private the array of items that are displayed..
19428     items  : false,
19429     // private - the hidden field el.
19430     hiddenEl : false,
19431     // private - the filed el..
19432     el : false,
19433     
19434     //validateValue : function() { return true; }, // all values are ok!
19435     //onAddClick: function() { },
19436     
19437     onRender : function(ct, position) 
19438     {
19439         
19440         // create the standard hidden element
19441         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19442         
19443         
19444         // give fake names to child combo;
19445         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19446         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19447         
19448         this.combo = Roo.factory(this.combo, Roo.form);
19449         this.combo.onRender(ct, position);
19450         if (typeof(this.combo.width) != 'undefined') {
19451             this.combo.onResize(this.combo.width,0);
19452         }
19453         
19454         this.combo.initEvents();
19455         
19456         // assigned so form know we need to do this..
19457         this.store          = this.combo.store;
19458         this.valueField     = this.combo.valueField;
19459         this.displayField   = this.combo.displayField ;
19460         
19461         
19462         this.combo.wrap.addClass('x-cbarray-grp');
19463         
19464         var cbwrap = this.combo.wrap.createChild(
19465             {tag: 'div', cls: 'x-cbarray-cb'},
19466             this.combo.el.dom
19467         );
19468         
19469              
19470         this.hiddenEl = this.combo.wrap.createChild({
19471             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19472         });
19473         this.el = this.combo.wrap.createChild({
19474             tag: 'input',  type:'hidden' , name: this.name, value : ''
19475         });
19476          //   this.el.dom.removeAttribute("name");
19477         
19478         
19479         this.outerWrap = this.combo.wrap;
19480         this.wrap = cbwrap;
19481         
19482         this.outerWrap.setWidth(this.width);
19483         this.outerWrap.dom.removeChild(this.el.dom);
19484         
19485         this.wrap.dom.appendChild(this.el.dom);
19486         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19487         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19488         
19489         this.combo.trigger.setStyle('position','relative');
19490         this.combo.trigger.setStyle('left', '0px');
19491         this.combo.trigger.setStyle('top', '2px');
19492         
19493         this.combo.el.setStyle('vertical-align', 'text-bottom');
19494         
19495         //this.trigger.setStyle('vertical-align', 'top');
19496         
19497         // this should use the code from combo really... on('add' ....)
19498         if (this.adder) {
19499             
19500         
19501             this.adder = this.outerWrap.createChild(
19502                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19503             var _t = this;
19504             this.adder.on('click', function(e) {
19505                 _t.fireEvent('adderclick', this, e);
19506             }, _t);
19507         }
19508         //var _t = this;
19509         //this.adder.on('click', this.onAddClick, _t);
19510         
19511         
19512         this.combo.on('select', function(cb, rec, ix) {
19513             this.addItem(rec.data);
19514             
19515             cb.setValue('');
19516             cb.el.dom.value = '';
19517             //cb.lastData = rec.data;
19518             // add to list
19519             
19520         }, this);
19521         
19522         
19523     },
19524     
19525     
19526     getName: function()
19527     {
19528         // returns hidden if it's set..
19529         if (!this.rendered) {return ''};
19530         return  this.hiddenName ? this.hiddenName : this.name;
19531         
19532     },
19533     
19534     
19535     onResize: function(w, h){
19536         
19537         return;
19538         // not sure if this is needed..
19539         //this.combo.onResize(w,h);
19540         
19541         if(typeof w != 'number'){
19542             // we do not handle it!?!?
19543             return;
19544         }
19545         var tw = this.combo.trigger.getWidth();
19546         tw += this.addicon ? this.addicon.getWidth() : 0;
19547         tw += this.editicon ? this.editicon.getWidth() : 0;
19548         var x = w - tw;
19549         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19550             
19551         this.combo.trigger.setStyle('left', '0px');
19552         
19553         if(this.list && this.listWidth === undefined){
19554             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19555             this.list.setWidth(lw);
19556             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19557         }
19558         
19559     
19560         
19561     },
19562     
19563     addItem: function(rec)
19564     {
19565         var valueField = this.combo.valueField;
19566         var displayField = this.combo.displayField;
19567         
19568         if (this.items.indexOfKey(rec[valueField]) > -1) {
19569             //console.log("GOT " + rec.data.id);
19570             return;
19571         }
19572         
19573         var x = new Roo.form.ComboBoxArray.Item({
19574             //id : rec[this.idField],
19575             data : rec,
19576             displayField : displayField ,
19577             tipField : displayField ,
19578             cb : this
19579         });
19580         // use the 
19581         this.items.add(rec[valueField],x);
19582         // add it before the element..
19583         this.updateHiddenEl();
19584         x.render(this.outerWrap, this.wrap.dom);
19585         // add the image handler..
19586     },
19587     
19588     updateHiddenEl : function()
19589     {
19590         this.validate();
19591         if (!this.hiddenEl) {
19592             return;
19593         }
19594         var ar = [];
19595         var idField = this.combo.valueField;
19596         
19597         this.items.each(function(f) {
19598             ar.push(f.data[idField]);
19599         });
19600         this.hiddenEl.dom.value = ar.join(this.seperator);
19601         this.validate();
19602     },
19603     
19604     reset : function()
19605     {
19606         this.items.clear();
19607         
19608         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19609            el.remove();
19610         });
19611         
19612         this.el.dom.value = '';
19613         if (this.hiddenEl) {
19614             this.hiddenEl.dom.value = '';
19615         }
19616         
19617     },
19618     getValue: function()
19619     {
19620         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19621     },
19622     setValue: function(v) // not a valid action - must use addItems..
19623     {
19624         
19625         this.reset();
19626          
19627         if (this.store.isLocal && (typeof(v) == 'string')) {
19628             // then we can use the store to find the values..
19629             // comma seperated at present.. this needs to allow JSON based encoding..
19630             this.hiddenEl.value  = v;
19631             var v_ar = [];
19632             Roo.each(v.split(this.seperator), function(k) {
19633                 Roo.log("CHECK " + this.valueField + ',' + k);
19634                 var li = this.store.query(this.valueField, k);
19635                 if (!li.length) {
19636                     return;
19637                 }
19638                 var add = {};
19639                 add[this.valueField] = k;
19640                 add[this.displayField] = li.item(0).data[this.displayField];
19641                 
19642                 this.addItem(add);
19643             }, this) 
19644              
19645         }
19646         if (typeof(v) == 'object' ) {
19647             // then let's assume it's an array of objects..
19648             Roo.each(v, function(l) {
19649                 var add = l;
19650                 if (typeof(l) == 'string') {
19651                     add = {};
19652                     add[this.valueField] = l;
19653                     add[this.displayField] = l
19654                 }
19655                 this.addItem(add);
19656             }, this);
19657              
19658         }
19659         
19660         
19661     },
19662     setFromData: function(v)
19663     {
19664         // this recieves an object, if setValues is called.
19665         this.reset();
19666         this.el.dom.value = v[this.displayField];
19667         this.hiddenEl.dom.value = v[this.valueField];
19668         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19669             return;
19670         }
19671         var kv = v[this.valueField];
19672         var dv = v[this.displayField];
19673         kv = typeof(kv) != 'string' ? '' : kv;
19674         dv = typeof(dv) != 'string' ? '' : dv;
19675         
19676         
19677         var keys = kv.split(this.seperator);
19678         var display = dv.split(this.seperator);
19679         for (var i = 0 ; i < keys.length; i++) {
19680             add = {};
19681             add[this.valueField] = keys[i];
19682             add[this.displayField] = display[i];
19683             this.addItem(add);
19684         }
19685       
19686         
19687     },
19688     
19689     /**
19690      * Validates the combox array value
19691      * @return {Boolean} True if the value is valid, else false
19692      */
19693     validate : function(){
19694         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19695             this.clearInvalid();
19696             return true;
19697         }
19698         return false;
19699     },
19700     
19701     validateValue : function(value){
19702         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19703         
19704     },
19705     
19706     /*@
19707      * overide
19708      * 
19709      */
19710     isDirty : function() {
19711         if(this.disabled) {
19712             return false;
19713         }
19714         
19715         try {
19716             var d = Roo.decode(String(this.originalValue));
19717         } catch (e) {
19718             return String(this.getValue()) !== String(this.originalValue);
19719         }
19720         
19721         var originalValue = [];
19722         
19723         for (var i = 0; i < d.length; i++){
19724             originalValue.push(d[i][this.valueField]);
19725         }
19726         
19727         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19728         
19729     }
19730     
19731 });
19732
19733
19734
19735 /**
19736  * @class Roo.form.ComboBoxArray.Item
19737  * @extends Roo.BoxComponent
19738  * A selected item in the list
19739  *  Fred [x]  Brian [x]  [Pick another |v]
19740  * 
19741  * @constructor
19742  * Create a new item.
19743  * @param {Object} config Configuration options
19744  */
19745  
19746 Roo.form.ComboBoxArray.Item = function(config) {
19747     config.id = Roo.id();
19748     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19749 }
19750
19751 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19752     data : {},
19753     cb: false,
19754     displayField : false,
19755     tipField : false,
19756     
19757     
19758     defaultAutoCreate : {
19759         tag: 'div',
19760         cls: 'x-cbarray-item',
19761         cn : [ 
19762             { tag: 'div' },
19763             {
19764                 tag: 'img',
19765                 width:16,
19766                 height : 16,
19767                 src : Roo.BLANK_IMAGE_URL ,
19768                 align: 'center'
19769             }
19770         ]
19771         
19772     },
19773     
19774  
19775     onRender : function(ct, position)
19776     {
19777         Roo.form.Field.superclass.onRender.call(this, ct, position);
19778         
19779         if(!this.el){
19780             var cfg = this.getAutoCreate();
19781             this.el = ct.createChild(cfg, position);
19782         }
19783         
19784         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19785         
19786         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19787             this.cb.renderer(this.data) :
19788             String.format('{0}',this.data[this.displayField]);
19789         
19790             
19791         this.el.child('div').dom.setAttribute('qtip',
19792                         String.format('{0}',this.data[this.tipField])
19793         );
19794         
19795         this.el.child('img').on('click', this.remove, this);
19796         
19797     },
19798    
19799     remove : function()
19800     {
19801         if(this.cb.disabled){
19802             return;
19803         }
19804         
19805         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19806             this.cb.items.remove(this);
19807             this.el.child('img').un('click', this.remove, this);
19808             this.el.remove();
19809             this.cb.updateHiddenEl();
19810
19811             this.cb.fireEvent('remove', this.cb, this);
19812         }
19813         
19814     }
19815 });/*
19816  * RooJS Library 1.1.1
19817  * Copyright(c) 2008-2011  Alan Knowles
19818  *
19819  * License - LGPL
19820  */
19821  
19822
19823 /**
19824  * @class Roo.form.ComboNested
19825  * @extends Roo.form.ComboBox
19826  * A combobox for that allows selection of nested items in a list,
19827  * eg.
19828  *
19829  *  Book
19830  *    -> red
19831  *    -> green
19832  *  Table
19833  *    -> square
19834  *      ->red
19835  *      ->green
19836  *    -> rectangle
19837  *      ->green
19838  *      
19839  * 
19840  * @constructor
19841  * Create a new ComboNested
19842  * @param {Object} config Configuration options
19843  */
19844 Roo.form.ComboNested = function(config){
19845     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19846     // should verify some data...
19847     // like
19848     // hiddenName = required..
19849     // displayField = required
19850     // valudField == required
19851     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19852     var _t = this;
19853     Roo.each(req, function(e) {
19854         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19855             throw "Roo.form.ComboNested : missing value for: " + e;
19856         }
19857     });
19858      
19859     
19860 };
19861
19862 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19863    
19864     /*
19865      * @config {Number} max Number of columns to show
19866      */
19867     
19868     maxColumns : 3,
19869    
19870     list : null, // the outermost div..
19871     innerLists : null, // the
19872     views : null,
19873     stores : null,
19874     // private
19875     loadingChildren : false,
19876     
19877     onRender : function(ct, position)
19878     {
19879         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19880         
19881         if(this.hiddenName){
19882             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19883                     'before', true);
19884             this.hiddenField.value =
19885                 this.hiddenValue !== undefined ? this.hiddenValue :
19886                 this.value !== undefined ? this.value : '';
19887
19888             // prevent input submission
19889             this.el.dom.removeAttribute('name');
19890              
19891              
19892         }
19893         
19894         if(Roo.isGecko){
19895             this.el.dom.setAttribute('autocomplete', 'off');
19896         }
19897
19898         var cls = 'x-combo-list';
19899
19900         this.list = new Roo.Layer({
19901             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19902         });
19903
19904         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19905         this.list.setWidth(lw);
19906         this.list.swallowEvent('mousewheel');
19907         this.assetHeight = 0;
19908
19909         if(this.title){
19910             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19911             this.assetHeight += this.header.getHeight();
19912         }
19913         this.innerLists = [];
19914         this.views = [];
19915         this.stores = [];
19916         for (var i =0 ; i < this.maxColumns; i++) {
19917             this.onRenderList( cls, i);
19918         }
19919         
19920         // always needs footer, as we are going to have an 'OK' button.
19921         this.footer = this.list.createChild({cls:cls+'-ft'});
19922         this.pageTb = new Roo.Toolbar(this.footer);  
19923         var _this = this;
19924         this.pageTb.add(  {
19925             
19926             text: 'Done',
19927             handler: function()
19928             {
19929                 _this.collapse();
19930             }
19931         });
19932         
19933         if ( this.allowBlank && !this.disableClear) {
19934             
19935             this.pageTb.add(new Roo.Toolbar.Fill(), {
19936                 cls: 'x-btn-icon x-btn-clear',
19937                 text: '&#160;',
19938                 handler: function()
19939                 {
19940                     _this.collapse();
19941                     _this.clearValue();
19942                     _this.onSelect(false, -1);
19943                 }
19944             });
19945         }
19946         if (this.footer) {
19947             this.assetHeight += this.footer.getHeight();
19948         }
19949         
19950     },
19951     onRenderList : function (  cls, i)
19952     {
19953         
19954         var lw = Math.floor(
19955                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19956         );
19957         
19958         this.list.setWidth(lw); // default to '1'
19959
19960         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19961         //il.on('mouseover', this.onViewOver, this, { list:  i });
19962         //il.on('mousemove', this.onViewMove, this, { list:  i });
19963         il.setWidth(lw);
19964         il.setStyle({ 'overflow-x' : 'hidden'});
19965
19966         if(!this.tpl){
19967             this.tpl = new Roo.Template({
19968                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19969                 isEmpty: function (value, allValues) {
19970                     //Roo.log(value);
19971                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19972                     return dl ? 'has-children' : 'no-children'
19973                 }
19974             });
19975         }
19976         
19977         var store  = this.store;
19978         if (i > 0) {
19979             store  = new Roo.data.SimpleStore({
19980                 //fields : this.store.reader.meta.fields,
19981                 reader : this.store.reader,
19982                 data : [ ]
19983             });
19984         }
19985         this.stores[i]  = store;
19986                   
19987         var view = this.views[i] = new Roo.View(
19988             il,
19989             this.tpl,
19990             {
19991                 singleSelect:true,
19992                 store: store,
19993                 selectedClass: this.selectedClass
19994             }
19995         );
19996         view.getEl().setWidth(lw);
19997         view.getEl().setStyle({
19998             position: i < 1 ? 'relative' : 'absolute',
19999             top: 0,
20000             left: (i * lw ) + 'px',
20001             display : i > 0 ? 'none' : 'block'
20002         });
20003         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20004         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20005         //view.on('click', this.onViewClick, this, { list : i });
20006
20007         store.on('beforeload', this.onBeforeLoad, this);
20008         store.on('load',  this.onLoad, this, { list  : i});
20009         store.on('loadexception', this.onLoadException, this);
20010
20011         // hide the other vies..
20012         
20013         
20014         
20015     },
20016       
20017     restrictHeight : function()
20018     {
20019         var mh = 0;
20020         Roo.each(this.innerLists, function(il,i) {
20021             var el = this.views[i].getEl();
20022             el.dom.style.height = '';
20023             var inner = el.dom;
20024             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20025             // only adjust heights on other ones..
20026             mh = Math.max(h, mh);
20027             if (i < 1) {
20028                 
20029                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20030                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20031                
20032             }
20033             
20034             
20035         }, this);
20036         
20037         this.list.beginUpdate();
20038         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20039         this.list.alignTo(this.el, this.listAlign);
20040         this.list.endUpdate();
20041         
20042     },
20043      
20044     
20045     // -- store handlers..
20046     // private
20047     onBeforeLoad : function()
20048     {
20049         if(!this.hasFocus){
20050             return;
20051         }
20052         this.innerLists[0].update(this.loadingText ?
20053                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20054         this.restrictHeight();
20055         this.selectedIndex = -1;
20056     },
20057     // private
20058     onLoad : function(a,b,c,d)
20059     {
20060         if (!this.loadingChildren) {
20061             // then we are loading the top level. - hide the children
20062             for (var i = 1;i < this.views.length; i++) {
20063                 this.views[i].getEl().setStyle({ display : 'none' });
20064             }
20065             var lw = Math.floor(
20066                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20067             );
20068         
20069              this.list.setWidth(lw); // default to '1'
20070
20071             
20072         }
20073         if(!this.hasFocus){
20074             return;
20075         }
20076         
20077         if(this.store.getCount() > 0) {
20078             this.expand();
20079             this.restrictHeight();   
20080         } else {
20081             this.onEmptyResults();
20082         }
20083         
20084         if (!this.loadingChildren) {
20085             this.selectActive();
20086         }
20087         /*
20088         this.stores[1].loadData([]);
20089         this.stores[2].loadData([]);
20090         this.views
20091         */    
20092     
20093         //this.el.focus();
20094     },
20095     
20096     
20097     // private
20098     onLoadException : function()
20099     {
20100         this.collapse();
20101         Roo.log(this.store.reader.jsonData);
20102         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20103             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20104         }
20105         
20106         
20107     },
20108     // no cleaning of leading spaces on blur here.
20109     cleanLeadingSpace : function(e) { },
20110     
20111
20112     onSelectChange : function (view, sels, opts )
20113     {
20114         var ix = view.getSelectedIndexes();
20115          
20116         if (opts.list > this.maxColumns - 2) {
20117             if (view.store.getCount()<  1) {
20118                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20119
20120             } else  {
20121                 if (ix.length) {
20122                     // used to clear ?? but if we are loading unselected 
20123                     this.setFromData(view.store.getAt(ix[0]).data);
20124                 }
20125                 
20126             }
20127             
20128             return;
20129         }
20130         
20131         if (!ix.length) {
20132             // this get's fired when trigger opens..
20133            // this.setFromData({});
20134             var str = this.stores[opts.list+1];
20135             str.data.clear(); // removeall wihtout the fire events..
20136             return;
20137         }
20138         
20139         var rec = view.store.getAt(ix[0]);
20140          
20141         this.setFromData(rec.data);
20142         this.fireEvent('select', this, rec, ix[0]);
20143         
20144         var lw = Math.floor(
20145              (
20146                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20147              ) / this.maxColumns
20148         );
20149         this.loadingChildren = true;
20150         this.stores[opts.list+1].loadDataFromChildren( rec );
20151         this.loadingChildren = false;
20152         var dl = this.stores[opts.list+1]. getTotalCount();
20153         
20154         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20155         
20156         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20157         for (var i = opts.list+2; i < this.views.length;i++) {
20158             this.views[i].getEl().setStyle({ display : 'none' });
20159         }
20160         
20161         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20162         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20163         
20164         if (this.isLoading) {
20165            // this.selectActive(opts.list);
20166         }
20167          
20168     },
20169     
20170     
20171     
20172     
20173     onDoubleClick : function()
20174     {
20175         this.collapse(); //??
20176     },
20177     
20178      
20179     
20180     
20181     
20182     // private
20183     recordToStack : function(store, prop, value, stack)
20184     {
20185         var cstore = new Roo.data.SimpleStore({
20186             //fields : this.store.reader.meta.fields, // we need array reader.. for
20187             reader : this.store.reader,
20188             data : [ ]
20189         });
20190         var _this = this;
20191         var record  = false;
20192         var srec = false;
20193         if(store.getCount() < 1){
20194             return false;
20195         }
20196         store.each(function(r){
20197             if(r.data[prop] == value){
20198                 record = r;
20199             srec = r;
20200                 return false;
20201             }
20202             if (r.data.cn && r.data.cn.length) {
20203                 cstore.loadDataFromChildren( r);
20204                 var cret = _this.recordToStack(cstore, prop, value, stack);
20205                 if (cret !== false) {
20206                     record = cret;
20207                     srec = r;
20208                     return false;
20209                 }
20210             }
20211              
20212             return true;
20213         });
20214         if (record == false) {
20215             return false
20216         }
20217         stack.unshift(srec);
20218         return record;
20219     },
20220     
20221     /*
20222      * find the stack of stores that match our value.
20223      *
20224      * 
20225      */
20226     
20227     selectActive : function ()
20228     {
20229         // if store is not loaded, then we will need to wait for that to happen first.
20230         var stack = [];
20231         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20232         for (var i = 0; i < stack.length; i++ ) {
20233             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20234         }
20235         
20236     }
20237         
20238          
20239     
20240     
20241     
20242     
20243 });/*
20244  * Based on:
20245  * Ext JS Library 1.1.1
20246  * Copyright(c) 2006-2007, Ext JS, LLC.
20247  *
20248  * Originally Released Under LGPL - original licence link has changed is not relivant.
20249  *
20250  * Fork - LGPL
20251  * <script type="text/javascript">
20252  */
20253 /**
20254  * @class Roo.form.Checkbox
20255  * @extends Roo.form.Field
20256  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20257  * @constructor
20258  * Creates a new Checkbox
20259  * @param {Object} config Configuration options
20260  */
20261 Roo.form.Checkbox = function(config){
20262     Roo.form.Checkbox.superclass.constructor.call(this, config);
20263     this.addEvents({
20264         /**
20265          * @event check
20266          * Fires when the checkbox is checked or unchecked.
20267              * @param {Roo.form.Checkbox} this This checkbox
20268              * @param {Boolean} checked The new checked value
20269              */
20270         check : true
20271     });
20272 };
20273
20274 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20275     /**
20276      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20277      */
20278     focusClass : undefined,
20279     /**
20280      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20281      */
20282     fieldClass: "x-form-field",
20283     /**
20284      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20285      */
20286     checked: false,
20287     /**
20288      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20289      * {tag: "input", type: "checkbox", autocomplete: "off"})
20290      */
20291     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20292     /**
20293      * @cfg {String} boxLabel The text that appears beside the checkbox
20294      */
20295     boxLabel : "",
20296     /**
20297      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20298      */  
20299     inputValue : '1',
20300     /**
20301      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20302      */
20303      valueOff: '0', // value when not checked..
20304
20305     actionMode : 'viewEl', 
20306     //
20307     // private
20308     itemCls : 'x-menu-check-item x-form-item',
20309     groupClass : 'x-menu-group-item',
20310     inputType : 'hidden',
20311     
20312     
20313     inSetChecked: false, // check that we are not calling self...
20314     
20315     inputElement: false, // real input element?
20316     basedOn: false, // ????
20317     
20318     isFormField: true, // not sure where this is needed!!!!
20319
20320     onResize : function(){
20321         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20322         if(!this.boxLabel){
20323             this.el.alignTo(this.wrap, 'c-c');
20324         }
20325     },
20326
20327     initEvents : function(){
20328         Roo.form.Checkbox.superclass.initEvents.call(this);
20329         this.el.on("click", this.onClick,  this);
20330         this.el.on("change", this.onClick,  this);
20331     },
20332
20333
20334     getResizeEl : function(){
20335         return this.wrap;
20336     },
20337
20338     getPositionEl : function(){
20339         return this.wrap;
20340     },
20341
20342     // private
20343     onRender : function(ct, position){
20344         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20345         /*
20346         if(this.inputValue !== undefined){
20347             this.el.dom.value = this.inputValue;
20348         }
20349         */
20350         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20351         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20352         var viewEl = this.wrap.createChild({ 
20353             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20354         this.viewEl = viewEl;   
20355         this.wrap.on('click', this.onClick,  this); 
20356         
20357         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20358         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20359         
20360         
20361         
20362         if(this.boxLabel){
20363             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20364         //    viewEl.on('click', this.onClick,  this); 
20365         }
20366         //if(this.checked){
20367             this.setChecked(this.checked);
20368         //}else{
20369             //this.checked = this.el.dom;
20370         //}
20371
20372     },
20373
20374     // private
20375     initValue : Roo.emptyFn,
20376
20377     /**
20378      * Returns the checked state of the checkbox.
20379      * @return {Boolean} True if checked, else false
20380      */
20381     getValue : function(){
20382         if(this.el){
20383             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20384         }
20385         return this.valueOff;
20386         
20387     },
20388
20389         // private
20390     onClick : function(){ 
20391         if (this.disabled) {
20392             return;
20393         }
20394         this.setChecked(!this.checked);
20395
20396         //if(this.el.dom.checked != this.checked){
20397         //    this.setValue(this.el.dom.checked);
20398        // }
20399     },
20400
20401     /**
20402      * Sets the checked state of the checkbox.
20403      * On is always based on a string comparison between inputValue and the param.
20404      * @param {Boolean/String} value - the value to set 
20405      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20406      */
20407     setValue : function(v,suppressEvent){
20408         
20409         
20410         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20411         //if(this.el && this.el.dom){
20412         //    this.el.dom.checked = this.checked;
20413         //    this.el.dom.defaultChecked = this.checked;
20414         //}
20415         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20416         //this.fireEvent("check", this, this.checked);
20417     },
20418     // private..
20419     setChecked : function(state,suppressEvent)
20420     {
20421         if (this.inSetChecked) {
20422             this.checked = state;
20423             return;
20424         }
20425         
20426     
20427         if(this.wrap){
20428             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20429         }
20430         this.checked = state;
20431         if(suppressEvent !== true){
20432             this.fireEvent('check', this, state);
20433         }
20434         this.inSetChecked = true;
20435         this.el.dom.value = state ? this.inputValue : this.valueOff;
20436         this.inSetChecked = false;
20437         
20438     },
20439     // handle setting of hidden value by some other method!!?!?
20440     setFromHidden: function()
20441     {
20442         if(!this.el){
20443             return;
20444         }
20445         //console.log("SET FROM HIDDEN");
20446         //alert('setFrom hidden');
20447         this.setValue(this.el.dom.value);
20448     },
20449     
20450     onDestroy : function()
20451     {
20452         if(this.viewEl){
20453             Roo.get(this.viewEl).remove();
20454         }
20455          
20456         Roo.form.Checkbox.superclass.onDestroy.call(this);
20457     },
20458     
20459     setBoxLabel : function(str)
20460     {
20461         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20462     }
20463
20464 });/*
20465  * Based on:
20466  * Ext JS Library 1.1.1
20467  * Copyright(c) 2006-2007, Ext JS, LLC.
20468  *
20469  * Originally Released Under LGPL - original licence link has changed is not relivant.
20470  *
20471  * Fork - LGPL
20472  * <script type="text/javascript">
20473  */
20474  
20475 /**
20476  * @class Roo.form.Radio
20477  * @extends Roo.form.Checkbox
20478  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20479  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20480  * @constructor
20481  * Creates a new Radio
20482  * @param {Object} config Configuration options
20483  */
20484 Roo.form.Radio = function(){
20485     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20486 };
20487 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20488     inputType: 'radio',
20489
20490     /**
20491      * If this radio is part of a group, it will return the selected value
20492      * @return {String}
20493      */
20494     getGroupValue : function(){
20495         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20496     },
20497     
20498     
20499     onRender : function(ct, position){
20500         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20501         
20502         if(this.inputValue !== undefined){
20503             this.el.dom.value = this.inputValue;
20504         }
20505          
20506         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20507         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20508         //var viewEl = this.wrap.createChild({ 
20509         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20510         //this.viewEl = viewEl;   
20511         //this.wrap.on('click', this.onClick,  this); 
20512         
20513         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20514         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20515         
20516         
20517         
20518         if(this.boxLabel){
20519             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20520         //    viewEl.on('click', this.onClick,  this); 
20521         }
20522          if(this.checked){
20523             this.el.dom.checked =   'checked' ;
20524         }
20525          
20526     } 
20527     
20528     
20529 });//<script type="text/javascript">
20530
20531 /*
20532  * Based  Ext JS Library 1.1.1
20533  * Copyright(c) 2006-2007, Ext JS, LLC.
20534  * LGPL
20535  *
20536  */
20537  
20538 /**
20539  * @class Roo.HtmlEditorCore
20540  * @extends Roo.Component
20541  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20542  *
20543  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20544  */
20545
20546 Roo.HtmlEditorCore = function(config){
20547     
20548     
20549     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20550     
20551     
20552     this.addEvents({
20553         /**
20554          * @event initialize
20555          * Fires when the editor is fully initialized (including the iframe)
20556          * @param {Roo.HtmlEditorCore} this
20557          */
20558         initialize: true,
20559         /**
20560          * @event activate
20561          * Fires when the editor is first receives the focus. Any insertion must wait
20562          * until after this event.
20563          * @param {Roo.HtmlEditorCore} this
20564          */
20565         activate: true,
20566          /**
20567          * @event beforesync
20568          * Fires before the textarea is updated with content from the editor iframe. Return false
20569          * to cancel the sync.
20570          * @param {Roo.HtmlEditorCore} this
20571          * @param {String} html
20572          */
20573         beforesync: true,
20574          /**
20575          * @event beforepush
20576          * Fires before the iframe editor is updated with content from the textarea. Return false
20577          * to cancel the push.
20578          * @param {Roo.HtmlEditorCore} this
20579          * @param {String} html
20580          */
20581         beforepush: true,
20582          /**
20583          * @event sync
20584          * Fires when the textarea is updated with content from the editor iframe.
20585          * @param {Roo.HtmlEditorCore} this
20586          * @param {String} html
20587          */
20588         sync: true,
20589          /**
20590          * @event push
20591          * Fires when the iframe editor is updated with content from the textarea.
20592          * @param {Roo.HtmlEditorCore} this
20593          * @param {String} html
20594          */
20595         push: true,
20596         
20597         /**
20598          * @event editorevent
20599          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20600          * @param {Roo.HtmlEditorCore} this
20601          */
20602         editorevent: true
20603         
20604     });
20605     
20606     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20607     
20608     // defaults : white / black...
20609     this.applyBlacklists();
20610     
20611     
20612     
20613 };
20614
20615
20616 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20617
20618
20619      /**
20620      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20621      */
20622     
20623     owner : false,
20624     
20625      /**
20626      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20627      *                        Roo.resizable.
20628      */
20629     resizable : false,
20630      /**
20631      * @cfg {Number} height (in pixels)
20632      */   
20633     height: 300,
20634    /**
20635      * @cfg {Number} width (in pixels)
20636      */   
20637     width: 500,
20638     
20639     /**
20640      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20641      * 
20642      */
20643     stylesheets: false,
20644     
20645     /**
20646      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
20647      */
20648     allowComments: false,
20649     // id of frame..
20650     frameId: false,
20651     
20652     // private properties
20653     validationEvent : false,
20654     deferHeight: true,
20655     initialized : false,
20656     activated : false,
20657     sourceEditMode : false,
20658     onFocus : Roo.emptyFn,
20659     iframePad:3,
20660     hideMode:'offsets',
20661     
20662     clearUp: true,
20663     
20664     // blacklist + whitelisted elements..
20665     black: false,
20666     white: false,
20667      
20668     bodyCls : '',
20669
20670     /**
20671      * Protected method that will not generally be called directly. It
20672      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20673      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20674      */
20675     getDocMarkup : function(){
20676         // body styles..
20677         var st = '';
20678         
20679         // inherit styels from page...?? 
20680         if (this.stylesheets === false) {
20681             
20682             Roo.get(document.head).select('style').each(function(node) {
20683                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20684             });
20685             
20686             Roo.get(document.head).select('link').each(function(node) { 
20687                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20688             });
20689             
20690         } else if (!this.stylesheets.length) {
20691                 // simple..
20692                 st = '<style type="text/css">' +
20693                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20694                    '</style>';
20695         } else {
20696             for (var i in this.stylesheets) { 
20697                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
20698             }
20699             
20700         }
20701         
20702         st +=  '<style type="text/css">' +
20703             'IMG { cursor: pointer } ' +
20704         '</style>';
20705
20706         var cls = 'roo-htmleditor-body';
20707         
20708         if(this.bodyCls.length){
20709             cls += ' ' + this.bodyCls;
20710         }
20711         
20712         return '<html><head>' + st  +
20713             //<style type="text/css">' +
20714             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20715             //'</style>' +
20716             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
20717     },
20718
20719     // private
20720     onRender : function(ct, position)
20721     {
20722         var _t = this;
20723         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20724         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20725         
20726         
20727         this.el.dom.style.border = '0 none';
20728         this.el.dom.setAttribute('tabIndex', -1);
20729         this.el.addClass('x-hidden hide');
20730         
20731         
20732         
20733         if(Roo.isIE){ // fix IE 1px bogus margin
20734             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20735         }
20736        
20737         
20738         this.frameId = Roo.id();
20739         
20740          
20741         
20742         var iframe = this.owner.wrap.createChild({
20743             tag: 'iframe',
20744             cls: 'form-control', // bootstrap..
20745             id: this.frameId,
20746             name: this.frameId,
20747             frameBorder : 'no',
20748             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20749         }, this.el
20750         );
20751         
20752         
20753         this.iframe = iframe.dom;
20754
20755          this.assignDocWin();
20756         
20757         this.doc.designMode = 'on';
20758        
20759         this.doc.open();
20760         this.doc.write(this.getDocMarkup());
20761         this.doc.close();
20762
20763         
20764         var task = { // must defer to wait for browser to be ready
20765             run : function(){
20766                 //console.log("run task?" + this.doc.readyState);
20767                 this.assignDocWin();
20768                 if(this.doc.body || this.doc.readyState == 'complete'){
20769                     try {
20770                         this.doc.designMode="on";
20771                     } catch (e) {
20772                         return;
20773                     }
20774                     Roo.TaskMgr.stop(task);
20775                     this.initEditor.defer(10, this);
20776                 }
20777             },
20778             interval : 10,
20779             duration: 10000,
20780             scope: this
20781         };
20782         Roo.TaskMgr.start(task);
20783
20784     },
20785
20786     // private
20787     onResize : function(w, h)
20788     {
20789          Roo.log('resize: ' +w + ',' + h );
20790         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20791         if(!this.iframe){
20792             return;
20793         }
20794         if(typeof w == 'number'){
20795             
20796             this.iframe.style.width = w + 'px';
20797         }
20798         if(typeof h == 'number'){
20799             
20800             this.iframe.style.height = h + 'px';
20801             if(this.doc){
20802                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20803             }
20804         }
20805         
20806     },
20807
20808     /**
20809      * Toggles the editor between standard and source edit mode.
20810      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20811      */
20812     toggleSourceEdit : function(sourceEditMode){
20813         
20814         this.sourceEditMode = sourceEditMode === true;
20815         
20816         if(this.sourceEditMode){
20817  
20818             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20819             
20820         }else{
20821             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20822             //this.iframe.className = '';
20823             this.deferFocus();
20824         }
20825         //this.setSize(this.owner.wrap.getSize());
20826         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20827     },
20828
20829     
20830   
20831
20832     /**
20833      * Protected method that will not generally be called directly. If you need/want
20834      * custom HTML cleanup, this is the method you should override.
20835      * @param {String} html The HTML to be cleaned
20836      * return {String} The cleaned HTML
20837      */
20838     cleanHtml : function(html){
20839         html = String(html);
20840         if(html.length > 5){
20841             if(Roo.isSafari){ // strip safari nonsense
20842                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20843             }
20844         }
20845         if(html == '&nbsp;'){
20846             html = '';
20847         }
20848         return html;
20849     },
20850
20851     /**
20852      * HTML Editor -> Textarea
20853      * Protected method that will not generally be called directly. Syncs the contents
20854      * of the editor iframe with the textarea.
20855      */
20856     syncValue : function(){
20857         if(this.initialized){
20858             var bd = (this.doc.body || this.doc.documentElement);
20859             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20860             var html = bd.innerHTML;
20861             if(Roo.isSafari){
20862                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20863                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20864                 if(m && m[1]){
20865                     html = '<div style="'+m[0]+'">' + html + '</div>';
20866                 }
20867             }
20868             html = this.cleanHtml(html);
20869             // fix up the special chars.. normaly like back quotes in word...
20870             // however we do not want to do this with chinese..
20871             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20872                 
20873                 var cc = match.charCodeAt();
20874
20875                 // Get the character value, handling surrogate pairs
20876                 if (match.length == 2) {
20877                     // It's a surrogate pair, calculate the Unicode code point
20878                     var high = match.charCodeAt(0) - 0xD800;
20879                     var low  = match.charCodeAt(1) - 0xDC00;
20880                     cc = (high * 0x400) + low + 0x10000;
20881                 }  else if (
20882                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20883                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20884                     (cc >= 0xf900 && cc < 0xfb00 )
20885                 ) {
20886                         return match;
20887                 }  
20888          
20889                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20890                 return "&#" + cc + ";";
20891                 
20892                 
20893             });
20894             
20895             
20896              
20897             if(this.owner.fireEvent('beforesync', this, html) !== false){
20898                 this.el.dom.value = html;
20899                 this.owner.fireEvent('sync', this, html);
20900             }
20901         }
20902     },
20903
20904     /**
20905      * Protected method that will not generally be called directly. Pushes the value of the textarea
20906      * into the iframe editor.
20907      */
20908     pushValue : function(){
20909         if(this.initialized){
20910             var v = this.el.dom.value.trim();
20911             
20912 //            if(v.length < 1){
20913 //                v = '&#160;';
20914 //            }
20915             
20916             if(this.owner.fireEvent('beforepush', this, v) !== false){
20917                 var d = (this.doc.body || this.doc.documentElement);
20918                 d.innerHTML = v;
20919                 this.cleanUpPaste();
20920                 this.el.dom.value = d.innerHTML;
20921                 this.owner.fireEvent('push', this, v);
20922             }
20923         }
20924     },
20925
20926     // private
20927     deferFocus : function(){
20928         this.focus.defer(10, this);
20929     },
20930
20931     // doc'ed in Field
20932     focus : function(){
20933         if(this.win && !this.sourceEditMode){
20934             this.win.focus();
20935         }else{
20936             this.el.focus();
20937         }
20938     },
20939     
20940     assignDocWin: function()
20941     {
20942         var iframe = this.iframe;
20943         
20944          if(Roo.isIE){
20945             this.doc = iframe.contentWindow.document;
20946             this.win = iframe.contentWindow;
20947         } else {
20948 //            if (!Roo.get(this.frameId)) {
20949 //                return;
20950 //            }
20951 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20952 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20953             
20954             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20955                 return;
20956             }
20957             
20958             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20959             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20960         }
20961     },
20962     
20963     // private
20964     initEditor : function(){
20965         //console.log("INIT EDITOR");
20966         this.assignDocWin();
20967         
20968         
20969         
20970         this.doc.designMode="on";
20971         this.doc.open();
20972         this.doc.write(this.getDocMarkup());
20973         this.doc.close();
20974         
20975         var dbody = (this.doc.body || this.doc.documentElement);
20976         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20977         // this copies styles from the containing element into thsi one..
20978         // not sure why we need all of this..
20979         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20980         
20981         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20982         //ss['background-attachment'] = 'fixed'; // w3c
20983         dbody.bgProperties = 'fixed'; // ie
20984         //Roo.DomHelper.applyStyles(dbody, ss);
20985         Roo.EventManager.on(this.doc, {
20986             //'mousedown': this.onEditorEvent,
20987             'mouseup': this.onEditorEvent,
20988             'dblclick': this.onEditorEvent,
20989             'click': this.onEditorEvent,
20990             'keyup': this.onEditorEvent,
20991             buffer:100,
20992             scope: this
20993         });
20994         if(Roo.isGecko){
20995             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20996         }
20997         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20998             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20999         }
21000         this.initialized = true;
21001
21002         this.owner.fireEvent('initialize', this);
21003         this.pushValue();
21004     },
21005
21006     // private
21007     onDestroy : function(){
21008         
21009         
21010         
21011         if(this.rendered){
21012             
21013             //for (var i =0; i < this.toolbars.length;i++) {
21014             //    // fixme - ask toolbars for heights?
21015             //    this.toolbars[i].onDestroy();
21016            // }
21017             
21018             //this.wrap.dom.innerHTML = '';
21019             //this.wrap.remove();
21020         }
21021     },
21022
21023     // private
21024     onFirstFocus : function(){
21025         
21026         this.assignDocWin();
21027         
21028         
21029         this.activated = true;
21030          
21031     
21032         if(Roo.isGecko){ // prevent silly gecko errors
21033             this.win.focus();
21034             var s = this.win.getSelection();
21035             if(!s.focusNode || s.focusNode.nodeType != 3){
21036                 var r = s.getRangeAt(0);
21037                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21038                 r.collapse(true);
21039                 this.deferFocus();
21040             }
21041             try{
21042                 this.execCmd('useCSS', true);
21043                 this.execCmd('styleWithCSS', false);
21044             }catch(e){}
21045         }
21046         this.owner.fireEvent('activate', this);
21047     },
21048
21049     // private
21050     adjustFont: function(btn){
21051         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21052         //if(Roo.isSafari){ // safari
21053         //    adjust *= 2;
21054        // }
21055         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21056         if(Roo.isSafari){ // safari
21057             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21058             v =  (v < 10) ? 10 : v;
21059             v =  (v > 48) ? 48 : v;
21060             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21061             
21062         }
21063         
21064         
21065         v = Math.max(1, v+adjust);
21066         
21067         this.execCmd('FontSize', v  );
21068     },
21069
21070     onEditorEvent : function(e)
21071     {
21072         this.owner.fireEvent('editorevent', this, e);
21073       //  this.updateToolbar();
21074         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21075     },
21076
21077     insertTag : function(tg)
21078     {
21079         // could be a bit smarter... -> wrap the current selected tRoo..
21080         if (tg.toLowerCase() == 'span' ||
21081             tg.toLowerCase() == 'code' ||
21082             tg.toLowerCase() == 'sup' ||
21083             tg.toLowerCase() == 'sub' 
21084             ) {
21085             
21086             range = this.createRange(this.getSelection());
21087             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21088             wrappingNode.appendChild(range.extractContents());
21089             range.insertNode(wrappingNode);
21090
21091             return;
21092             
21093             
21094             
21095         }
21096         this.execCmd("formatblock",   tg);
21097         
21098     },
21099     
21100     insertText : function(txt)
21101     {
21102         
21103         
21104         var range = this.createRange();
21105         range.deleteContents();
21106                //alert(Sender.getAttribute('label'));
21107                
21108         range.insertNode(this.doc.createTextNode(txt));
21109     } ,
21110     
21111      
21112
21113     /**
21114      * Executes a Midas editor command on the editor document and performs necessary focus and
21115      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21116      * @param {String} cmd The Midas command
21117      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21118      */
21119     relayCmd : function(cmd, value){
21120         this.win.focus();
21121         this.execCmd(cmd, value);
21122         this.owner.fireEvent('editorevent', this);
21123         //this.updateToolbar();
21124         this.owner.deferFocus();
21125     },
21126
21127     /**
21128      * Executes a Midas editor command directly on the editor document.
21129      * For visual commands, you should use {@link #relayCmd} instead.
21130      * <b>This should only be called after the editor is initialized.</b>
21131      * @param {String} cmd The Midas command
21132      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21133      */
21134     execCmd : function(cmd, value){
21135         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21136         this.syncValue();
21137     },
21138  
21139  
21140    
21141     /**
21142      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21143      * to insert tRoo.
21144      * @param {String} text | dom node.. 
21145      */
21146     insertAtCursor : function(text)
21147     {
21148         
21149         if(!this.activated){
21150             return;
21151         }
21152         /*
21153         if(Roo.isIE){
21154             this.win.focus();
21155             var r = this.doc.selection.createRange();
21156             if(r){
21157                 r.collapse(true);
21158                 r.pasteHTML(text);
21159                 this.syncValue();
21160                 this.deferFocus();
21161             
21162             }
21163             return;
21164         }
21165         */
21166         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21167             this.win.focus();
21168             
21169             
21170             // from jquery ui (MIT licenced)
21171             var range, node;
21172             var win = this.win;
21173             
21174             if (win.getSelection && win.getSelection().getRangeAt) {
21175                 range = win.getSelection().getRangeAt(0);
21176                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21177                 range.insertNode(node);
21178             } else if (win.document.selection && win.document.selection.createRange) {
21179                 // no firefox support
21180                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21181                 win.document.selection.createRange().pasteHTML(txt);
21182             } else {
21183                 // no firefox support
21184                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21185                 this.execCmd('InsertHTML', txt);
21186             } 
21187             
21188             this.syncValue();
21189             
21190             this.deferFocus();
21191         }
21192     },
21193  // private
21194     mozKeyPress : function(e){
21195         if(e.ctrlKey){
21196             var c = e.getCharCode(), cmd;
21197           
21198             if(c > 0){
21199                 c = String.fromCharCode(c).toLowerCase();
21200                 switch(c){
21201                     case 'b':
21202                         cmd = 'bold';
21203                         break;
21204                     case 'i':
21205                         cmd = 'italic';
21206                         break;
21207                     
21208                     case 'u':
21209                         cmd = 'underline';
21210                         break;
21211                     
21212                     case 'v':
21213                         this.cleanUpPaste.defer(100, this);
21214                         return;
21215                         
21216                 }
21217                 if(cmd){
21218                     this.win.focus();
21219                     this.execCmd(cmd);
21220                     this.deferFocus();
21221                     e.preventDefault();
21222                 }
21223                 
21224             }
21225         }
21226     },
21227
21228     // private
21229     fixKeys : function(){ // load time branching for fastest keydown performance
21230         if(Roo.isIE){
21231             return function(e){
21232                 var k = e.getKey(), r;
21233                 if(k == e.TAB){
21234                     e.stopEvent();
21235                     r = this.doc.selection.createRange();
21236                     if(r){
21237                         r.collapse(true);
21238                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21239                         this.deferFocus();
21240                     }
21241                     return;
21242                 }
21243                 
21244                 if(k == e.ENTER){
21245                     r = this.doc.selection.createRange();
21246                     if(r){
21247                         var target = r.parentElement();
21248                         if(!target || target.tagName.toLowerCase() != 'li'){
21249                             e.stopEvent();
21250                             r.pasteHTML('<br />');
21251                             r.collapse(false);
21252                             r.select();
21253                         }
21254                     }
21255                 }
21256                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21257                     this.cleanUpPaste.defer(100, this);
21258                     return;
21259                 }
21260                 
21261                 
21262             };
21263         }else if(Roo.isOpera){
21264             return function(e){
21265                 var k = e.getKey();
21266                 if(k == e.TAB){
21267                     e.stopEvent();
21268                     this.win.focus();
21269                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21270                     this.deferFocus();
21271                 }
21272                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21273                     this.cleanUpPaste.defer(100, this);
21274                     return;
21275                 }
21276                 
21277             };
21278         }else if(Roo.isSafari){
21279             return function(e){
21280                 var k = e.getKey();
21281                 
21282                 if(k == e.TAB){
21283                     e.stopEvent();
21284                     this.execCmd('InsertText','\t');
21285                     this.deferFocus();
21286                     return;
21287                 }
21288                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21289                     this.cleanUpPaste.defer(100, this);
21290                     return;
21291                 }
21292                 
21293              };
21294         }
21295     }(),
21296     
21297     getAllAncestors: function()
21298     {
21299         var p = this.getSelectedNode();
21300         var a = [];
21301         if (!p) {
21302             a.push(p); // push blank onto stack..
21303             p = this.getParentElement();
21304         }
21305         
21306         
21307         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21308             a.push(p);
21309             p = p.parentNode;
21310         }
21311         a.push(this.doc.body);
21312         return a;
21313     },
21314     lastSel : false,
21315     lastSelNode : false,
21316     
21317     
21318     getSelection : function() 
21319     {
21320         this.assignDocWin();
21321         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21322     },
21323     
21324     getSelectedNode: function() 
21325     {
21326         // this may only work on Gecko!!!
21327         
21328         // should we cache this!!!!
21329         
21330         
21331         
21332          
21333         var range = this.createRange(this.getSelection()).cloneRange();
21334         
21335         if (Roo.isIE) {
21336             var parent = range.parentElement();
21337             while (true) {
21338                 var testRange = range.duplicate();
21339                 testRange.moveToElementText(parent);
21340                 if (testRange.inRange(range)) {
21341                     break;
21342                 }
21343                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21344                     break;
21345                 }
21346                 parent = parent.parentElement;
21347             }
21348             return parent;
21349         }
21350         
21351         // is ancestor a text element.
21352         var ac =  range.commonAncestorContainer;
21353         if (ac.nodeType == 3) {
21354             ac = ac.parentNode;
21355         }
21356         
21357         var ar = ac.childNodes;
21358          
21359         var nodes = [];
21360         var other_nodes = [];
21361         var has_other_nodes = false;
21362         for (var i=0;i<ar.length;i++) {
21363             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21364                 continue;
21365             }
21366             // fullly contained node.
21367             
21368             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21369                 nodes.push(ar[i]);
21370                 continue;
21371             }
21372             
21373             // probably selected..
21374             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21375                 other_nodes.push(ar[i]);
21376                 continue;
21377             }
21378             // outer..
21379             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21380                 continue;
21381             }
21382             
21383             
21384             has_other_nodes = true;
21385         }
21386         if (!nodes.length && other_nodes.length) {
21387             nodes= other_nodes;
21388         }
21389         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21390             return false;
21391         }
21392         
21393         return nodes[0];
21394     },
21395     createRange: function(sel)
21396     {
21397         // this has strange effects when using with 
21398         // top toolbar - not sure if it's a great idea.
21399         //this.editor.contentWindow.focus();
21400         if (typeof sel != "undefined") {
21401             try {
21402                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21403             } catch(e) {
21404                 return this.doc.createRange();
21405             }
21406         } else {
21407             return this.doc.createRange();
21408         }
21409     },
21410     getParentElement: function()
21411     {
21412         
21413         this.assignDocWin();
21414         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21415         
21416         var range = this.createRange(sel);
21417          
21418         try {
21419             var p = range.commonAncestorContainer;
21420             while (p.nodeType == 3) { // text node
21421                 p = p.parentNode;
21422             }
21423             return p;
21424         } catch (e) {
21425             return null;
21426         }
21427     
21428     },
21429     /***
21430      *
21431      * Range intersection.. the hard stuff...
21432      *  '-1' = before
21433      *  '0' = hits..
21434      *  '1' = after.
21435      *         [ -- selected range --- ]
21436      *   [fail]                        [fail]
21437      *
21438      *    basically..
21439      *      if end is before start or  hits it. fail.
21440      *      if start is after end or hits it fail.
21441      *
21442      *   if either hits (but other is outside. - then it's not 
21443      *   
21444      *    
21445      **/
21446     
21447     
21448     // @see http://www.thismuchiknow.co.uk/?p=64.
21449     rangeIntersectsNode : function(range, node)
21450     {
21451         var nodeRange = node.ownerDocument.createRange();
21452         try {
21453             nodeRange.selectNode(node);
21454         } catch (e) {
21455             nodeRange.selectNodeContents(node);
21456         }
21457     
21458         var rangeStartRange = range.cloneRange();
21459         rangeStartRange.collapse(true);
21460     
21461         var rangeEndRange = range.cloneRange();
21462         rangeEndRange.collapse(false);
21463     
21464         var nodeStartRange = nodeRange.cloneRange();
21465         nodeStartRange.collapse(true);
21466     
21467         var nodeEndRange = nodeRange.cloneRange();
21468         nodeEndRange.collapse(false);
21469     
21470         return rangeStartRange.compareBoundaryPoints(
21471                  Range.START_TO_START, nodeEndRange) == -1 &&
21472                rangeEndRange.compareBoundaryPoints(
21473                  Range.START_TO_START, nodeStartRange) == 1;
21474         
21475          
21476     },
21477     rangeCompareNode : function(range, node)
21478     {
21479         var nodeRange = node.ownerDocument.createRange();
21480         try {
21481             nodeRange.selectNode(node);
21482         } catch (e) {
21483             nodeRange.selectNodeContents(node);
21484         }
21485         
21486         
21487         range.collapse(true);
21488     
21489         nodeRange.collapse(true);
21490      
21491         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21492         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21493          
21494         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21495         
21496         var nodeIsBefore   =  ss == 1;
21497         var nodeIsAfter    = ee == -1;
21498         
21499         if (nodeIsBefore && nodeIsAfter) {
21500             return 0; // outer
21501         }
21502         if (!nodeIsBefore && nodeIsAfter) {
21503             return 1; //right trailed.
21504         }
21505         
21506         if (nodeIsBefore && !nodeIsAfter) {
21507             return 2;  // left trailed.
21508         }
21509         // fully contined.
21510         return 3;
21511     },
21512
21513     // private? - in a new class?
21514     cleanUpPaste :  function()
21515     {
21516         // cleans up the whole document..
21517         Roo.log('cleanuppaste');
21518         
21519         this.cleanUpChildren(this.doc.body);
21520         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21521         if (clean != this.doc.body.innerHTML) {
21522             this.doc.body.innerHTML = clean;
21523         }
21524         
21525     },
21526     
21527     cleanWordChars : function(input) {// change the chars to hex code
21528         var he = Roo.HtmlEditorCore;
21529         
21530         var output = input;
21531         Roo.each(he.swapCodes, function(sw) { 
21532             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21533             
21534             output = output.replace(swapper, sw[1]);
21535         });
21536         
21537         return output;
21538     },
21539     
21540     
21541     cleanUpChildren : function (n)
21542     {
21543         if (!n.childNodes.length) {
21544             return;
21545         }
21546         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21547            this.cleanUpChild(n.childNodes[i]);
21548         }
21549     },
21550     
21551     
21552         
21553     
21554     cleanUpChild : function (node)
21555     {
21556         var ed = this;
21557         //console.log(node);
21558         if (node.nodeName == "#text") {
21559             // clean up silly Windows -- stuff?
21560             return; 
21561         }
21562         if (node.nodeName == "#comment") {
21563             if (!this.allowComments) {
21564                 node.parentNode.removeChild(node);
21565             }
21566             // clean up silly Windows -- stuff?
21567             return; 
21568         }
21569         var lcname = node.tagName.toLowerCase();
21570         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21571         // whitelist of tags..
21572         
21573         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21574             // remove node.
21575             node.parentNode.removeChild(node);
21576             return;
21577             
21578         }
21579         
21580         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21581         
21582         // spans with no attributes - just remove them..
21583         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21584             remove_keep_children = true;
21585         }
21586         
21587         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21588         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21589         
21590         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21591         //    remove_keep_children = true;
21592         //}
21593         
21594         if (remove_keep_children) {
21595             this.cleanUpChildren(node);
21596             // inserts everything just before this node...
21597             while (node.childNodes.length) {
21598                 var cn = node.childNodes[0];
21599                 node.removeChild(cn);
21600                 node.parentNode.insertBefore(cn, node);
21601             }
21602             node.parentNode.removeChild(node);
21603             return;
21604         }
21605         
21606         if (!node.attributes || !node.attributes.length) {
21607             
21608           
21609             
21610             
21611             this.cleanUpChildren(node);
21612             return;
21613         }
21614         
21615         function cleanAttr(n,v)
21616         {
21617             
21618             if (v.match(/^\./) || v.match(/^\//)) {
21619                 return;
21620             }
21621             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21622                 return;
21623             }
21624             if (v.match(/^#/)) {
21625                 return;
21626             }
21627             if (v.match(/^\{/)) { // allow template editing.
21628                 return;
21629             }
21630 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21631             node.removeAttribute(n);
21632             
21633         }
21634         
21635         var cwhite = this.cwhite;
21636         var cblack = this.cblack;
21637             
21638         function cleanStyle(n,v)
21639         {
21640             if (v.match(/expression/)) { //XSS?? should we even bother..
21641                 node.removeAttribute(n);
21642                 return;
21643             }
21644             
21645             var parts = v.split(/;/);
21646             var clean = [];
21647             
21648             Roo.each(parts, function(p) {
21649                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21650                 if (!p.length) {
21651                     return true;
21652                 }
21653                 var l = p.split(':').shift().replace(/\s+/g,'');
21654                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21655                 
21656                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21657 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21658                     //node.removeAttribute(n);
21659                     return true;
21660                 }
21661                 //Roo.log()
21662                 // only allow 'c whitelisted system attributes'
21663                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21664 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21665                     //node.removeAttribute(n);
21666                     return true;
21667                 }
21668                 
21669                 
21670                  
21671                 
21672                 clean.push(p);
21673                 return true;
21674             });
21675             if (clean.length) { 
21676                 node.setAttribute(n, clean.join(';'));
21677             } else {
21678                 node.removeAttribute(n);
21679             }
21680             
21681         }
21682         
21683         
21684         for (var i = node.attributes.length-1; i > -1 ; i--) {
21685             var a = node.attributes[i];
21686             //console.log(a);
21687             
21688             if (a.name.toLowerCase().substr(0,2)=='on')  {
21689                 node.removeAttribute(a.name);
21690                 continue;
21691             }
21692             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21693                 node.removeAttribute(a.name);
21694                 continue;
21695             }
21696             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21697                 cleanAttr(a.name,a.value); // fixme..
21698                 continue;
21699             }
21700             if (a.name == 'style') {
21701                 cleanStyle(a.name,a.value);
21702                 continue;
21703             }
21704             /// clean up MS crap..
21705             // tecnically this should be a list of valid class'es..
21706             
21707             
21708             if (a.name == 'class') {
21709                 if (a.value.match(/^Mso/)) {
21710                     node.removeAttribute('class');
21711                 }
21712                 
21713                 if (a.value.match(/^body$/)) {
21714                     node.removeAttribute('class');
21715                 }
21716                 continue;
21717             }
21718             
21719             // style cleanup!?
21720             // class cleanup?
21721             
21722         }
21723         
21724         
21725         this.cleanUpChildren(node);
21726         
21727         
21728     },
21729     
21730     /**
21731      * Clean up MS wordisms...
21732      */
21733     cleanWord : function(node)
21734     {
21735         if (!node) {
21736             this.cleanWord(this.doc.body);
21737             return;
21738         }
21739         
21740         if(
21741                 node.nodeName == 'SPAN' &&
21742                 !node.hasAttributes() &&
21743                 node.childNodes.length == 1 &&
21744                 node.firstChild.nodeName == "#text"  
21745         ) {
21746             var textNode = node.firstChild;
21747             node.removeChild(textNode);
21748             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21749                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21750             }
21751             node.parentNode.insertBefore(textNode, node);
21752             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21753                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21754             }
21755             node.parentNode.removeChild(node);
21756         }
21757         
21758         if (node.nodeName == "#text") {
21759             // clean up silly Windows -- stuff?
21760             return; 
21761         }
21762         if (node.nodeName == "#comment") {
21763             node.parentNode.removeChild(node);
21764             // clean up silly Windows -- stuff?
21765             return; 
21766         }
21767         
21768         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21769             node.parentNode.removeChild(node);
21770             return;
21771         }
21772         //Roo.log(node.tagName);
21773         // remove - but keep children..
21774         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21775             //Roo.log('-- removed');
21776             while (node.childNodes.length) {
21777                 var cn = node.childNodes[0];
21778                 node.removeChild(cn);
21779                 node.parentNode.insertBefore(cn, node);
21780                 // move node to parent - and clean it..
21781                 this.cleanWord(cn);
21782             }
21783             node.parentNode.removeChild(node);
21784             /// no need to iterate chidlren = it's got none..
21785             //this.iterateChildren(node, this.cleanWord);
21786             return;
21787         }
21788         // clean styles
21789         if (node.className.length) {
21790             
21791             var cn = node.className.split(/\W+/);
21792             var cna = [];
21793             Roo.each(cn, function(cls) {
21794                 if (cls.match(/Mso[a-zA-Z]+/)) {
21795                     return;
21796                 }
21797                 cna.push(cls);
21798             });
21799             node.className = cna.length ? cna.join(' ') : '';
21800             if (!cna.length) {
21801                 node.removeAttribute("class");
21802             }
21803         }
21804         
21805         if (node.hasAttribute("lang")) {
21806             node.removeAttribute("lang");
21807         }
21808         
21809         if (node.hasAttribute("style")) {
21810             
21811             var styles = node.getAttribute("style").split(";");
21812             var nstyle = [];
21813             Roo.each(styles, function(s) {
21814                 if (!s.match(/:/)) {
21815                     return;
21816                 }
21817                 var kv = s.split(":");
21818                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21819                     return;
21820                 }
21821                 // what ever is left... we allow.
21822                 nstyle.push(s);
21823             });
21824             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21825             if (!nstyle.length) {
21826                 node.removeAttribute('style');
21827             }
21828         }
21829         this.iterateChildren(node, this.cleanWord);
21830         
21831         
21832         
21833     },
21834     /**
21835      * iterateChildren of a Node, calling fn each time, using this as the scole..
21836      * @param {DomNode} node node to iterate children of.
21837      * @param {Function} fn method of this class to call on each item.
21838      */
21839     iterateChildren : function(node, fn)
21840     {
21841         if (!node.childNodes.length) {
21842                 return;
21843         }
21844         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21845            fn.call(this, node.childNodes[i])
21846         }
21847     },
21848     
21849     
21850     /**
21851      * cleanTableWidths.
21852      *
21853      * Quite often pasting from word etc.. results in tables with column and widths.
21854      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21855      *
21856      */
21857     cleanTableWidths : function(node)
21858     {
21859          
21860          
21861         if (!node) {
21862             this.cleanTableWidths(this.doc.body);
21863             return;
21864         }
21865         
21866         // ignore list...
21867         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21868             return; 
21869         }
21870         Roo.log(node.tagName);
21871         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21872             this.iterateChildren(node, this.cleanTableWidths);
21873             return;
21874         }
21875         if (node.hasAttribute('width')) {
21876             node.removeAttribute('width');
21877         }
21878         
21879          
21880         if (node.hasAttribute("style")) {
21881             // pretty basic...
21882             
21883             var styles = node.getAttribute("style").split(";");
21884             var nstyle = [];
21885             Roo.each(styles, function(s) {
21886                 if (!s.match(/:/)) {
21887                     return;
21888                 }
21889                 var kv = s.split(":");
21890                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21891                     return;
21892                 }
21893                 // what ever is left... we allow.
21894                 nstyle.push(s);
21895             });
21896             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21897             if (!nstyle.length) {
21898                 node.removeAttribute('style');
21899             }
21900         }
21901         
21902         this.iterateChildren(node, this.cleanTableWidths);
21903         
21904         
21905     },
21906     
21907     
21908     
21909     
21910     domToHTML : function(currentElement, depth, nopadtext) {
21911         
21912         depth = depth || 0;
21913         nopadtext = nopadtext || false;
21914     
21915         if (!currentElement) {
21916             return this.domToHTML(this.doc.body);
21917         }
21918         
21919         //Roo.log(currentElement);
21920         var j;
21921         var allText = false;
21922         var nodeName = currentElement.nodeName;
21923         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21924         
21925         if  (nodeName == '#text') {
21926             
21927             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21928         }
21929         
21930         
21931         var ret = '';
21932         if (nodeName != 'BODY') {
21933              
21934             var i = 0;
21935             // Prints the node tagName, such as <A>, <IMG>, etc
21936             if (tagName) {
21937                 var attr = [];
21938                 for(i = 0; i < currentElement.attributes.length;i++) {
21939                     // quoting?
21940                     var aname = currentElement.attributes.item(i).name;
21941                     if (!currentElement.attributes.item(i).value.length) {
21942                         continue;
21943                     }
21944                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21945                 }
21946                 
21947                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21948             } 
21949             else {
21950                 
21951                 // eack
21952             }
21953         } else {
21954             tagName = false;
21955         }
21956         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21957             return ret;
21958         }
21959         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21960             nopadtext = true;
21961         }
21962         
21963         
21964         // Traverse the tree
21965         i = 0;
21966         var currentElementChild = currentElement.childNodes.item(i);
21967         var allText = true;
21968         var innerHTML  = '';
21969         lastnode = '';
21970         while (currentElementChild) {
21971             // Formatting code (indent the tree so it looks nice on the screen)
21972             var nopad = nopadtext;
21973             if (lastnode == 'SPAN') {
21974                 nopad  = true;
21975             }
21976             // text
21977             if  (currentElementChild.nodeName == '#text') {
21978                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21979                 toadd = nopadtext ? toadd : toadd.trim();
21980                 if (!nopad && toadd.length > 80) {
21981                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21982                 }
21983                 innerHTML  += toadd;
21984                 
21985                 i++;
21986                 currentElementChild = currentElement.childNodes.item(i);
21987                 lastNode = '';
21988                 continue;
21989             }
21990             allText = false;
21991             
21992             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21993                 
21994             // Recursively traverse the tree structure of the child node
21995             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21996             lastnode = currentElementChild.nodeName;
21997             i++;
21998             currentElementChild=currentElement.childNodes.item(i);
21999         }
22000         
22001         ret += innerHTML;
22002         
22003         if (!allText) {
22004                 // The remaining code is mostly for formatting the tree
22005             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22006         }
22007         
22008         
22009         if (tagName) {
22010             ret+= "</"+tagName+">";
22011         }
22012         return ret;
22013         
22014     },
22015         
22016     applyBlacklists : function()
22017     {
22018         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22019         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22020         
22021         this.white = [];
22022         this.black = [];
22023         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22024             if (b.indexOf(tag) > -1) {
22025                 return;
22026             }
22027             this.white.push(tag);
22028             
22029         }, this);
22030         
22031         Roo.each(w, function(tag) {
22032             if (b.indexOf(tag) > -1) {
22033                 return;
22034             }
22035             if (this.white.indexOf(tag) > -1) {
22036                 return;
22037             }
22038             this.white.push(tag);
22039             
22040         }, this);
22041         
22042         
22043         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22044             if (w.indexOf(tag) > -1) {
22045                 return;
22046             }
22047             this.black.push(tag);
22048             
22049         }, this);
22050         
22051         Roo.each(b, function(tag) {
22052             if (w.indexOf(tag) > -1) {
22053                 return;
22054             }
22055             if (this.black.indexOf(tag) > -1) {
22056                 return;
22057             }
22058             this.black.push(tag);
22059             
22060         }, this);
22061         
22062         
22063         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22064         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22065         
22066         this.cwhite = [];
22067         this.cblack = [];
22068         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22069             if (b.indexOf(tag) > -1) {
22070                 return;
22071             }
22072             this.cwhite.push(tag);
22073             
22074         }, this);
22075         
22076         Roo.each(w, function(tag) {
22077             if (b.indexOf(tag) > -1) {
22078                 return;
22079             }
22080             if (this.cwhite.indexOf(tag) > -1) {
22081                 return;
22082             }
22083             this.cwhite.push(tag);
22084             
22085         }, this);
22086         
22087         
22088         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22089             if (w.indexOf(tag) > -1) {
22090                 return;
22091             }
22092             this.cblack.push(tag);
22093             
22094         }, this);
22095         
22096         Roo.each(b, function(tag) {
22097             if (w.indexOf(tag) > -1) {
22098                 return;
22099             }
22100             if (this.cblack.indexOf(tag) > -1) {
22101                 return;
22102             }
22103             this.cblack.push(tag);
22104             
22105         }, this);
22106     },
22107     
22108     setStylesheets : function(stylesheets)
22109     {
22110         if(typeof(stylesheets) == 'string'){
22111             Roo.get(this.iframe.contentDocument.head).createChild({
22112                 tag : 'link',
22113                 rel : 'stylesheet',
22114                 type : 'text/css',
22115                 href : stylesheets
22116             });
22117             
22118             return;
22119         }
22120         var _this = this;
22121      
22122         Roo.each(stylesheets, function(s) {
22123             if(!s.length){
22124                 return;
22125             }
22126             
22127             Roo.get(_this.iframe.contentDocument.head).createChild({
22128                 tag : 'link',
22129                 rel : 'stylesheet',
22130                 type : 'text/css',
22131                 href : s
22132             });
22133         });
22134
22135         
22136     },
22137     
22138     removeStylesheets : function()
22139     {
22140         var _this = this;
22141         
22142         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22143             s.remove();
22144         });
22145     },
22146     
22147     setStyle : function(style)
22148     {
22149         Roo.get(this.iframe.contentDocument.head).createChild({
22150             tag : 'style',
22151             type : 'text/css',
22152             html : style
22153         });
22154
22155         return;
22156     }
22157     
22158     // hide stuff that is not compatible
22159     /**
22160      * @event blur
22161      * @hide
22162      */
22163     /**
22164      * @event change
22165      * @hide
22166      */
22167     /**
22168      * @event focus
22169      * @hide
22170      */
22171     /**
22172      * @event specialkey
22173      * @hide
22174      */
22175     /**
22176      * @cfg {String} fieldClass @hide
22177      */
22178     /**
22179      * @cfg {String} focusClass @hide
22180      */
22181     /**
22182      * @cfg {String} autoCreate @hide
22183      */
22184     /**
22185      * @cfg {String} inputType @hide
22186      */
22187     /**
22188      * @cfg {String} invalidClass @hide
22189      */
22190     /**
22191      * @cfg {String} invalidText @hide
22192      */
22193     /**
22194      * @cfg {String} msgFx @hide
22195      */
22196     /**
22197      * @cfg {String} validateOnBlur @hide
22198      */
22199 });
22200
22201 Roo.HtmlEditorCore.white = [
22202         'area', 'br', 'img', 'input', 'hr', 'wbr',
22203         
22204        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22205        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22206        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22207        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22208        'table',   'ul',         'xmp', 
22209        
22210        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22211       'thead',   'tr', 
22212      
22213       'dir', 'menu', 'ol', 'ul', 'dl',
22214        
22215       'embed',  'object'
22216 ];
22217
22218
22219 Roo.HtmlEditorCore.black = [
22220     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22221         'applet', // 
22222         'base',   'basefont', 'bgsound', 'blink',  'body', 
22223         'frame',  'frameset', 'head',    'html',   'ilayer', 
22224         'iframe', 'layer',  'link',     'meta',    'object',   
22225         'script', 'style' ,'title',  'xml' // clean later..
22226 ];
22227 Roo.HtmlEditorCore.clean = [
22228     'script', 'style', 'title', 'xml'
22229 ];
22230 Roo.HtmlEditorCore.remove = [
22231     'font'
22232 ];
22233 // attributes..
22234
22235 Roo.HtmlEditorCore.ablack = [
22236     'on'
22237 ];
22238     
22239 Roo.HtmlEditorCore.aclean = [ 
22240     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22241 ];
22242
22243 // protocols..
22244 Roo.HtmlEditorCore.pwhite= [
22245         'http',  'https',  'mailto'
22246 ];
22247
22248 // white listed style attributes.
22249 Roo.HtmlEditorCore.cwhite= [
22250       //  'text-align', /// default is to allow most things..
22251       
22252          
22253 //        'font-size'//??
22254 ];
22255
22256 // black listed style attributes.
22257 Roo.HtmlEditorCore.cblack= [
22258       //  'font-size' -- this can be set by the project 
22259 ];
22260
22261
22262 Roo.HtmlEditorCore.swapCodes   =[ 
22263     [    8211, "&#8211;" ], 
22264     [    8212, "&#8212;" ], 
22265     [    8216,  "'" ],  
22266     [    8217, "'" ],  
22267     [    8220, '"' ],  
22268     [    8221, '"' ],  
22269     [    8226, "*" ],  
22270     [    8230, "..." ]
22271 ]; 
22272
22273     //<script type="text/javascript">
22274
22275 /*
22276  * Ext JS Library 1.1.1
22277  * Copyright(c) 2006-2007, Ext JS, LLC.
22278  * Licence LGPL
22279  * 
22280  */
22281  
22282  
22283 Roo.form.HtmlEditor = function(config){
22284     
22285     
22286     
22287     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22288     
22289     if (!this.toolbars) {
22290         this.toolbars = [];
22291     }
22292     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22293     
22294     
22295 };
22296
22297 /**
22298  * @class Roo.form.HtmlEditor
22299  * @extends Roo.form.Field
22300  * Provides a lightweight HTML Editor component.
22301  *
22302  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22303  * 
22304  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22305  * supported by this editor.</b><br/><br/>
22306  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22307  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22308  */
22309 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22310     /**
22311      * @cfg {Boolean} clearUp
22312      */
22313     clearUp : true,
22314       /**
22315      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22316      */
22317     toolbars : false,
22318    
22319      /**
22320      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22321      *                        Roo.resizable.
22322      */
22323     resizable : false,
22324      /**
22325      * @cfg {Number} height (in pixels)
22326      */   
22327     height: 300,
22328    /**
22329      * @cfg {Number} width (in pixels)
22330      */   
22331     width: 500,
22332     
22333     /**
22334      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22335      * 
22336      */
22337     stylesheets: false,
22338     
22339     
22340      /**
22341      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22342      * 
22343      */
22344     cblack: false,
22345     /**
22346      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22347      * 
22348      */
22349     cwhite: false,
22350     
22351      /**
22352      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22353      * 
22354      */
22355     black: false,
22356     /**
22357      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22358      * 
22359      */
22360     white: false,
22361     /**
22362      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22363      */
22364     allowComments: false,
22365     
22366     // id of frame..
22367     frameId: false,
22368     
22369     // private properties
22370     validationEvent : false,
22371     deferHeight: true,
22372     initialized : false,
22373     activated : false,
22374     
22375     onFocus : Roo.emptyFn,
22376     iframePad:3,
22377     hideMode:'offsets',
22378     
22379     actionMode : 'container', // defaults to hiding it...
22380     
22381     defaultAutoCreate : { // modified by initCompnoent..
22382         tag: "textarea",
22383         style:"width:500px;height:300px;",
22384         autocomplete: "new-password"
22385     },
22386
22387     // private
22388     initComponent : function(){
22389         this.addEvents({
22390             /**
22391              * @event initialize
22392              * Fires when the editor is fully initialized (including the iframe)
22393              * @param {HtmlEditor} this
22394              */
22395             initialize: true,
22396             /**
22397              * @event activate
22398              * Fires when the editor is first receives the focus. Any insertion must wait
22399              * until after this event.
22400              * @param {HtmlEditor} this
22401              */
22402             activate: true,
22403              /**
22404              * @event beforesync
22405              * Fires before the textarea is updated with content from the editor iframe. Return false
22406              * to cancel the sync.
22407              * @param {HtmlEditor} this
22408              * @param {String} html
22409              */
22410             beforesync: true,
22411              /**
22412              * @event beforepush
22413              * Fires before the iframe editor is updated with content from the textarea. Return false
22414              * to cancel the push.
22415              * @param {HtmlEditor} this
22416              * @param {String} html
22417              */
22418             beforepush: true,
22419              /**
22420              * @event sync
22421              * Fires when the textarea is updated with content from the editor iframe.
22422              * @param {HtmlEditor} this
22423              * @param {String} html
22424              */
22425             sync: true,
22426              /**
22427              * @event push
22428              * Fires when the iframe editor is updated with content from the textarea.
22429              * @param {HtmlEditor} this
22430              * @param {String} html
22431              */
22432             push: true,
22433              /**
22434              * @event editmodechange
22435              * Fires when the editor switches edit modes
22436              * @param {HtmlEditor} this
22437              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22438              */
22439             editmodechange: true,
22440             /**
22441              * @event editorevent
22442              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22443              * @param {HtmlEditor} this
22444              */
22445             editorevent: true,
22446             /**
22447              * @event firstfocus
22448              * Fires when on first focus - needed by toolbars..
22449              * @param {HtmlEditor} this
22450              */
22451             firstfocus: true,
22452             /**
22453              * @event autosave
22454              * Auto save the htmlEditor value as a file into Events
22455              * @param {HtmlEditor} this
22456              */
22457             autosave: true,
22458             /**
22459              * @event savedpreview
22460              * preview the saved version of htmlEditor
22461              * @param {HtmlEditor} this
22462              */
22463             savedpreview: true,
22464             
22465             /**
22466             * @event stylesheetsclick
22467             * Fires when press the Sytlesheets button
22468             * @param {Roo.HtmlEditorCore} this
22469             */
22470             stylesheetsclick: true
22471         });
22472         this.defaultAutoCreate =  {
22473             tag: "textarea",
22474             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22475             autocomplete: "new-password"
22476         };
22477     },
22478
22479     /**
22480      * Protected method that will not generally be called directly. It
22481      * is called when the editor creates its toolbar. Override this method if you need to
22482      * add custom toolbar buttons.
22483      * @param {HtmlEditor} editor
22484      */
22485     createToolbar : function(editor){
22486         Roo.log("create toolbars");
22487         if (!editor.toolbars || !editor.toolbars.length) {
22488             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22489         }
22490         
22491         for (var i =0 ; i < editor.toolbars.length;i++) {
22492             editor.toolbars[i] = Roo.factory(
22493                     typeof(editor.toolbars[i]) == 'string' ?
22494                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22495                 Roo.form.HtmlEditor);
22496             editor.toolbars[i].init(editor);
22497         }
22498          
22499         
22500     },
22501
22502      
22503     // private
22504     onRender : function(ct, position)
22505     {
22506         var _t = this;
22507         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22508         
22509         this.wrap = this.el.wrap({
22510             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22511         });
22512         
22513         this.editorcore.onRender(ct, position);
22514          
22515         if (this.resizable) {
22516             this.resizeEl = new Roo.Resizable(this.wrap, {
22517                 pinned : true,
22518                 wrap: true,
22519                 dynamic : true,
22520                 minHeight : this.height,
22521                 height: this.height,
22522                 handles : this.resizable,
22523                 width: this.width,
22524                 listeners : {
22525                     resize : function(r, w, h) {
22526                         _t.onResize(w,h); // -something
22527                     }
22528                 }
22529             });
22530             
22531         }
22532         this.createToolbar(this);
22533        
22534         
22535         if(!this.width){
22536             this.setSize(this.wrap.getSize());
22537         }
22538         if (this.resizeEl) {
22539             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22540             // should trigger onReize..
22541         }
22542         
22543         this.keyNav = new Roo.KeyNav(this.el, {
22544             
22545             "tab" : function(e){
22546                 e.preventDefault();
22547                 
22548                 var value = this.getValue();
22549                 
22550                 var start = this.el.dom.selectionStart;
22551                 var end = this.el.dom.selectionEnd;
22552                 
22553                 if(!e.shiftKey){
22554                     
22555                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22556                     this.el.dom.setSelectionRange(end + 1, end + 1);
22557                     return;
22558                 }
22559                 
22560                 var f = value.substring(0, start).split("\t");
22561                 
22562                 if(f.pop().length != 0){
22563                     return;
22564                 }
22565                 
22566                 this.setValue(f.join("\t") + value.substring(end));
22567                 this.el.dom.setSelectionRange(start - 1, start - 1);
22568                 
22569             },
22570             
22571             "home" : function(e){
22572                 e.preventDefault();
22573                 
22574                 var curr = this.el.dom.selectionStart;
22575                 var lines = this.getValue().split("\n");
22576                 
22577                 if(!lines.length){
22578                     return;
22579                 }
22580                 
22581                 if(e.ctrlKey){
22582                     this.el.dom.setSelectionRange(0, 0);
22583                     return;
22584                 }
22585                 
22586                 var pos = 0;
22587                 
22588                 for (var i = 0; i < lines.length;i++) {
22589                     pos += lines[i].length;
22590                     
22591                     if(i != 0){
22592                         pos += 1;
22593                     }
22594                     
22595                     if(pos < curr){
22596                         continue;
22597                     }
22598                     
22599                     pos -= lines[i].length;
22600                     
22601                     break;
22602                 }
22603                 
22604                 if(!e.shiftKey){
22605                     this.el.dom.setSelectionRange(pos, pos);
22606                     return;
22607                 }
22608                 
22609                 this.el.dom.selectionStart = pos;
22610                 this.el.dom.selectionEnd = curr;
22611             },
22612             
22613             "end" : function(e){
22614                 e.preventDefault();
22615                 
22616                 var curr = this.el.dom.selectionStart;
22617                 var lines = this.getValue().split("\n");
22618                 
22619                 if(!lines.length){
22620                     return;
22621                 }
22622                 
22623                 if(e.ctrlKey){
22624                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22625                     return;
22626                 }
22627                 
22628                 var pos = 0;
22629                 
22630                 for (var i = 0; i < lines.length;i++) {
22631                     
22632                     pos += lines[i].length;
22633                     
22634                     if(i != 0){
22635                         pos += 1;
22636                     }
22637                     
22638                     if(pos < curr){
22639                         continue;
22640                     }
22641                     
22642                     break;
22643                 }
22644                 
22645                 if(!e.shiftKey){
22646                     this.el.dom.setSelectionRange(pos, pos);
22647                     return;
22648                 }
22649                 
22650                 this.el.dom.selectionStart = curr;
22651                 this.el.dom.selectionEnd = pos;
22652             },
22653
22654             scope : this,
22655
22656             doRelay : function(foo, bar, hname){
22657                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22658             },
22659
22660             forceKeyDown: true
22661         });
22662         
22663 //        if(this.autosave && this.w){
22664 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22665 //        }
22666     },
22667
22668     // private
22669     onResize : function(w, h)
22670     {
22671         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22672         var ew = false;
22673         var eh = false;
22674         
22675         if(this.el ){
22676             if(typeof w == 'number'){
22677                 var aw = w - this.wrap.getFrameWidth('lr');
22678                 this.el.setWidth(this.adjustWidth('textarea', aw));
22679                 ew = aw;
22680             }
22681             if(typeof h == 'number'){
22682                 var tbh = 0;
22683                 for (var i =0; i < this.toolbars.length;i++) {
22684                     // fixme - ask toolbars for heights?
22685                     tbh += this.toolbars[i].tb.el.getHeight();
22686                     if (this.toolbars[i].footer) {
22687                         tbh += this.toolbars[i].footer.el.getHeight();
22688                     }
22689                 }
22690                 
22691                 
22692                 
22693                 
22694                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22695                 ah -= 5; // knock a few pixes off for look..
22696 //                Roo.log(ah);
22697                 this.el.setHeight(this.adjustWidth('textarea', ah));
22698                 var eh = ah;
22699             }
22700         }
22701         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22702         this.editorcore.onResize(ew,eh);
22703         
22704     },
22705
22706     /**
22707      * Toggles the editor between standard and source edit mode.
22708      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22709      */
22710     toggleSourceEdit : function(sourceEditMode)
22711     {
22712         this.editorcore.toggleSourceEdit(sourceEditMode);
22713         
22714         if(this.editorcore.sourceEditMode){
22715             Roo.log('editor - showing textarea');
22716             
22717 //            Roo.log('in');
22718 //            Roo.log(this.syncValue());
22719             this.editorcore.syncValue();
22720             this.el.removeClass('x-hidden');
22721             this.el.dom.removeAttribute('tabIndex');
22722             this.el.focus();
22723             
22724             for (var i = 0; i < this.toolbars.length; i++) {
22725                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22726                     this.toolbars[i].tb.hide();
22727                     this.toolbars[i].footer.hide();
22728                 }
22729             }
22730             
22731         }else{
22732             Roo.log('editor - hiding textarea');
22733 //            Roo.log('out')
22734 //            Roo.log(this.pushValue()); 
22735             this.editorcore.pushValue();
22736             
22737             this.el.addClass('x-hidden');
22738             this.el.dom.setAttribute('tabIndex', -1);
22739             
22740             for (var i = 0; i < this.toolbars.length; i++) {
22741                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22742                     this.toolbars[i].tb.show();
22743                     this.toolbars[i].footer.show();
22744                 }
22745             }
22746             
22747             //this.deferFocus();
22748         }
22749         
22750         this.setSize(this.wrap.getSize());
22751         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22752         
22753         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22754     },
22755  
22756     // private (for BoxComponent)
22757     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22758
22759     // private (for BoxComponent)
22760     getResizeEl : function(){
22761         return this.wrap;
22762     },
22763
22764     // private (for BoxComponent)
22765     getPositionEl : function(){
22766         return this.wrap;
22767     },
22768
22769     // private
22770     initEvents : function(){
22771         this.originalValue = this.getValue();
22772     },
22773
22774     /**
22775      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22776      * @method
22777      */
22778     markInvalid : Roo.emptyFn,
22779     /**
22780      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22781      * @method
22782      */
22783     clearInvalid : Roo.emptyFn,
22784
22785     setValue : function(v){
22786         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22787         this.editorcore.pushValue();
22788     },
22789
22790      
22791     // private
22792     deferFocus : function(){
22793         this.focus.defer(10, this);
22794     },
22795
22796     // doc'ed in Field
22797     focus : function(){
22798         this.editorcore.focus();
22799         
22800     },
22801       
22802
22803     // private
22804     onDestroy : function(){
22805         
22806         
22807         
22808         if(this.rendered){
22809             
22810             for (var i =0; i < this.toolbars.length;i++) {
22811                 // fixme - ask toolbars for heights?
22812                 this.toolbars[i].onDestroy();
22813             }
22814             
22815             this.wrap.dom.innerHTML = '';
22816             this.wrap.remove();
22817         }
22818     },
22819
22820     // private
22821     onFirstFocus : function(){
22822         //Roo.log("onFirstFocus");
22823         this.editorcore.onFirstFocus();
22824          for (var i =0; i < this.toolbars.length;i++) {
22825             this.toolbars[i].onFirstFocus();
22826         }
22827         
22828     },
22829     
22830     // private
22831     syncValue : function()
22832     {
22833         this.editorcore.syncValue();
22834     },
22835     
22836     pushValue : function()
22837     {
22838         this.editorcore.pushValue();
22839     },
22840     
22841     setStylesheets : function(stylesheets)
22842     {
22843         this.editorcore.setStylesheets(stylesheets);
22844     },
22845     
22846     removeStylesheets : function()
22847     {
22848         this.editorcore.removeStylesheets();
22849     }
22850      
22851     
22852     // hide stuff that is not compatible
22853     /**
22854      * @event blur
22855      * @hide
22856      */
22857     /**
22858      * @event change
22859      * @hide
22860      */
22861     /**
22862      * @event focus
22863      * @hide
22864      */
22865     /**
22866      * @event specialkey
22867      * @hide
22868      */
22869     /**
22870      * @cfg {String} fieldClass @hide
22871      */
22872     /**
22873      * @cfg {String} focusClass @hide
22874      */
22875     /**
22876      * @cfg {String} autoCreate @hide
22877      */
22878     /**
22879      * @cfg {String} inputType @hide
22880      */
22881     /**
22882      * @cfg {String} invalidClass @hide
22883      */
22884     /**
22885      * @cfg {String} invalidText @hide
22886      */
22887     /**
22888      * @cfg {String} msgFx @hide
22889      */
22890     /**
22891      * @cfg {String} validateOnBlur @hide
22892      */
22893 });
22894  
22895     // <script type="text/javascript">
22896 /*
22897  * Based on
22898  * Ext JS Library 1.1.1
22899  * Copyright(c) 2006-2007, Ext JS, LLC.
22900  *  
22901  
22902  */
22903
22904 /**
22905  * @class Roo.form.HtmlEditorToolbar1
22906  * Basic Toolbar
22907  * 
22908  * Usage:
22909  *
22910  new Roo.form.HtmlEditor({
22911     ....
22912     toolbars : [
22913         new Roo.form.HtmlEditorToolbar1({
22914             disable : { fonts: 1 , format: 1, ..., ... , ...],
22915             btns : [ .... ]
22916         })
22917     }
22918      
22919  * 
22920  * @cfg {Object} disable List of elements to disable..
22921  * @cfg {Array} btns List of additional buttons.
22922  * 
22923  * 
22924  * NEEDS Extra CSS? 
22925  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22926  */
22927  
22928 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22929 {
22930     
22931     Roo.apply(this, config);
22932     
22933     // default disabled, based on 'good practice'..
22934     this.disable = this.disable || {};
22935     Roo.applyIf(this.disable, {
22936         fontSize : true,
22937         colors : true,
22938         specialElements : true
22939     });
22940     
22941     
22942     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22943     // dont call parent... till later.
22944 }
22945
22946 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22947     
22948     tb: false,
22949     
22950     rendered: false,
22951     
22952     editor : false,
22953     editorcore : false,
22954     /**
22955      * @cfg {Object} disable  List of toolbar elements to disable
22956          
22957      */
22958     disable : false,
22959     
22960     
22961      /**
22962      * @cfg {String} createLinkText The default text for the create link prompt
22963      */
22964     createLinkText : 'Please enter the URL for the link:',
22965     /**
22966      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22967      */
22968     defaultLinkValue : 'http:/'+'/',
22969    
22970     
22971       /**
22972      * @cfg {Array} fontFamilies An array of available font families
22973      */
22974     fontFamilies : [
22975         'Arial',
22976         'Courier New',
22977         'Tahoma',
22978         'Times New Roman',
22979         'Verdana'
22980     ],
22981     
22982     specialChars : [
22983            "&#169;",
22984           "&#174;",     
22985           "&#8482;",    
22986           "&#163;" ,    
22987          // "&#8212;",    
22988           "&#8230;",    
22989           "&#247;" ,    
22990         //  "&#225;" ,     ?? a acute?
22991            "&#8364;"    , //Euro
22992        //   "&#8220;"    ,
22993         //  "&#8221;"    ,
22994         //  "&#8226;"    ,
22995           "&#176;"  //   , // degrees
22996
22997          // "&#233;"     , // e ecute
22998          // "&#250;"     , // u ecute?
22999     ],
23000     
23001     specialElements : [
23002         {
23003             text: "Insert Table",
23004             xtype: 'MenuItem',
23005             xns : Roo.Menu,
23006             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
23007                 
23008         },
23009         {    
23010             text: "Insert Image",
23011             xtype: 'MenuItem',
23012             xns : Roo.Menu,
23013             ihtml : '<img src="about:blank"/>'
23014             
23015         }
23016         
23017          
23018     ],
23019     
23020     
23021     inputElements : [ 
23022             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
23023             "input:submit", "input:button", "select", "textarea", "label" ],
23024     formats : [
23025         ["p"] ,  
23026         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
23027         ["pre"],[ "code"], 
23028         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23029         ['div'],['span'],
23030         ['sup'],['sub']
23031     ],
23032     
23033     cleanStyles : [
23034         "font-size"
23035     ],
23036      /**
23037      * @cfg {String} defaultFont default font to use.
23038      */
23039     defaultFont: 'tahoma',
23040    
23041     fontSelect : false,
23042     
23043     
23044     formatCombo : false,
23045     
23046     init : function(editor)
23047     {
23048         this.editor = editor;
23049         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23050         var editorcore = this.editorcore;
23051         
23052         var _t = this;
23053         
23054         var fid = editorcore.frameId;
23055         var etb = this;
23056         function btn(id, toggle, handler){
23057             var xid = fid + '-'+ id ;
23058             return {
23059                 id : xid,
23060                 cmd : id,
23061                 cls : 'x-btn-icon x-edit-'+id,
23062                 enableToggle:toggle !== false,
23063                 scope: _t, // was editor...
23064                 handler:handler||_t.relayBtnCmd,
23065                 clickEvent:'mousedown',
23066                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23067                 tabIndex:-1
23068             };
23069         }
23070         
23071         
23072         
23073         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23074         this.tb = tb;
23075          // stop form submits
23076         tb.el.on('click', function(e){
23077             e.preventDefault(); // what does this do?
23078         });
23079
23080         if(!this.disable.font) { // && !Roo.isSafari){
23081             /* why no safari for fonts 
23082             editor.fontSelect = tb.el.createChild({
23083                 tag:'select',
23084                 tabIndex: -1,
23085                 cls:'x-font-select',
23086                 html: this.createFontOptions()
23087             });
23088             
23089             editor.fontSelect.on('change', function(){
23090                 var font = editor.fontSelect.dom.value;
23091                 editor.relayCmd('fontname', font);
23092                 editor.deferFocus();
23093             }, editor);
23094             
23095             tb.add(
23096                 editor.fontSelect.dom,
23097                 '-'
23098             );
23099             */
23100             
23101         };
23102         if(!this.disable.formats){
23103             this.formatCombo = new Roo.form.ComboBox({
23104                 store: new Roo.data.SimpleStore({
23105                     id : 'tag',
23106                     fields: ['tag'],
23107                     data : this.formats // from states.js
23108                 }),
23109                 blockFocus : true,
23110                 name : '',
23111                 //autoCreate : {tag: "div",  size: "20"},
23112                 displayField:'tag',
23113                 typeAhead: false,
23114                 mode: 'local',
23115                 editable : false,
23116                 triggerAction: 'all',
23117                 emptyText:'Add tag',
23118                 selectOnFocus:true,
23119                 width:135,
23120                 listeners : {
23121                     'select': function(c, r, i) {
23122                         editorcore.insertTag(r.get('tag'));
23123                         editor.focus();
23124                     }
23125                 }
23126
23127             });
23128             tb.addField(this.formatCombo);
23129             
23130         }
23131         
23132         if(!this.disable.format){
23133             tb.add(
23134                 btn('bold'),
23135                 btn('italic'),
23136                 btn('underline'),
23137                 btn('strikethrough')
23138             );
23139         };
23140         if(!this.disable.fontSize){
23141             tb.add(
23142                 '-',
23143                 
23144                 
23145                 btn('increasefontsize', false, editorcore.adjustFont),
23146                 btn('decreasefontsize', false, editorcore.adjustFont)
23147             );
23148         };
23149         
23150         
23151         if(!this.disable.colors){
23152             tb.add(
23153                 '-', {
23154                     id:editorcore.frameId +'-forecolor',
23155                     cls:'x-btn-icon x-edit-forecolor',
23156                     clickEvent:'mousedown',
23157                     tooltip: this.buttonTips['forecolor'] || undefined,
23158                     tabIndex:-1,
23159                     menu : new Roo.menu.ColorMenu({
23160                         allowReselect: true,
23161                         focus: Roo.emptyFn,
23162                         value:'000000',
23163                         plain:true,
23164                         selectHandler: function(cp, color){
23165                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23166                             editor.deferFocus();
23167                         },
23168                         scope: editorcore,
23169                         clickEvent:'mousedown'
23170                     })
23171                 }, {
23172                     id:editorcore.frameId +'backcolor',
23173                     cls:'x-btn-icon x-edit-backcolor',
23174                     clickEvent:'mousedown',
23175                     tooltip: this.buttonTips['backcolor'] || undefined,
23176                     tabIndex:-1,
23177                     menu : new Roo.menu.ColorMenu({
23178                         focus: Roo.emptyFn,
23179                         value:'FFFFFF',
23180                         plain:true,
23181                         allowReselect: true,
23182                         selectHandler: function(cp, color){
23183                             if(Roo.isGecko){
23184                                 editorcore.execCmd('useCSS', false);
23185                                 editorcore.execCmd('hilitecolor', color);
23186                                 editorcore.execCmd('useCSS', true);
23187                                 editor.deferFocus();
23188                             }else{
23189                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23190                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23191                                 editor.deferFocus();
23192                             }
23193                         },
23194                         scope:editorcore,
23195                         clickEvent:'mousedown'
23196                     })
23197                 }
23198             );
23199         };
23200         // now add all the items...
23201         
23202
23203         if(!this.disable.alignments){
23204             tb.add(
23205                 '-',
23206                 btn('justifyleft'),
23207                 btn('justifycenter'),
23208                 btn('justifyright')
23209             );
23210         };
23211
23212         //if(!Roo.isSafari){
23213             if(!this.disable.links){
23214                 tb.add(
23215                     '-',
23216                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23217                 );
23218             };
23219
23220             if(!this.disable.lists){
23221                 tb.add(
23222                     '-',
23223                     btn('insertorderedlist'),
23224                     btn('insertunorderedlist')
23225                 );
23226             }
23227             if(!this.disable.sourceEdit){
23228                 tb.add(
23229                     '-',
23230                     btn('sourceedit', true, function(btn){
23231                         this.toggleSourceEdit(btn.pressed);
23232                     })
23233                 );
23234             }
23235         //}
23236         
23237         var smenu = { };
23238         // special menu.. - needs to be tidied up..
23239         if (!this.disable.special) {
23240             smenu = {
23241                 text: "&#169;",
23242                 cls: 'x-edit-none',
23243                 
23244                 menu : {
23245                     items : []
23246                 }
23247             };
23248             for (var i =0; i < this.specialChars.length; i++) {
23249                 smenu.menu.items.push({
23250                     
23251                     html: this.specialChars[i],
23252                     handler: function(a,b) {
23253                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23254                         //editor.insertAtCursor(a.html);
23255                         
23256                     },
23257                     tabIndex:-1
23258                 });
23259             }
23260             
23261             
23262             tb.add(smenu);
23263             
23264             
23265         }
23266         
23267         var cmenu = { };
23268         if (!this.disable.cleanStyles) {
23269             cmenu = {
23270                 cls: 'x-btn-icon x-btn-clear',
23271                 
23272                 menu : {
23273                     items : []
23274                 }
23275             };
23276             for (var i =0; i < this.cleanStyles.length; i++) {
23277                 cmenu.menu.items.push({
23278                     actiontype : this.cleanStyles[i],
23279                     html: 'Remove ' + this.cleanStyles[i],
23280                     handler: function(a,b) {
23281 //                        Roo.log(a);
23282 //                        Roo.log(b);
23283                         var c = Roo.get(editorcore.doc.body);
23284                         c.select('[style]').each(function(s) {
23285                             s.dom.style.removeProperty(a.actiontype);
23286                         });
23287                         editorcore.syncValue();
23288                     },
23289                     tabIndex:-1
23290                 });
23291             }
23292              cmenu.menu.items.push({
23293                 actiontype : 'tablewidths',
23294                 html: 'Remove Table Widths',
23295                 handler: function(a,b) {
23296                     editorcore.cleanTableWidths();
23297                     editorcore.syncValue();
23298                 },
23299                 tabIndex:-1
23300             });
23301             cmenu.menu.items.push({
23302                 actiontype : 'word',
23303                 html: 'Remove MS Word Formating',
23304                 handler: function(a,b) {
23305                     editorcore.cleanWord();
23306                     editorcore.syncValue();
23307                 },
23308                 tabIndex:-1
23309             });
23310             
23311             cmenu.menu.items.push({
23312                 actiontype : 'all',
23313                 html: 'Remove All Styles',
23314                 handler: function(a,b) {
23315                     
23316                     var c = Roo.get(editorcore.doc.body);
23317                     c.select('[style]').each(function(s) {
23318                         s.dom.removeAttribute('style');
23319                     });
23320                     editorcore.syncValue();
23321                 },
23322                 tabIndex:-1
23323             });
23324             
23325             cmenu.menu.items.push({
23326                 actiontype : 'all',
23327                 html: 'Remove All CSS Classes',
23328                 handler: function(a,b) {
23329                     
23330                     var c = Roo.get(editorcore.doc.body);
23331                     c.select('[class]').each(function(s) {
23332                         s.dom.removeAttribute('class');
23333                     });
23334                     editorcore.cleanWord();
23335                     editorcore.syncValue();
23336                 },
23337                 tabIndex:-1
23338             });
23339             
23340              cmenu.menu.items.push({
23341                 actiontype : 'tidy',
23342                 html: 'Tidy HTML Source',
23343                 handler: function(a,b) {
23344                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23345                     editorcore.syncValue();
23346                 },
23347                 tabIndex:-1
23348             });
23349             
23350             
23351             tb.add(cmenu);
23352         }
23353          
23354         if (!this.disable.specialElements) {
23355             var semenu = {
23356                 text: "Other;",
23357                 cls: 'x-edit-none',
23358                 menu : {
23359                     items : []
23360                 }
23361             };
23362             for (var i =0; i < this.specialElements.length; i++) {
23363                 semenu.menu.items.push(
23364                     Roo.apply({ 
23365                         handler: function(a,b) {
23366                             editor.insertAtCursor(this.ihtml);
23367                         }
23368                     }, this.specialElements[i])
23369                 );
23370                     
23371             }
23372             
23373             tb.add(semenu);
23374             
23375             
23376         }
23377          
23378         
23379         if (this.btns) {
23380             for(var i =0; i< this.btns.length;i++) {
23381                 var b = Roo.factory(this.btns[i],Roo.form);
23382                 b.cls =  'x-edit-none';
23383                 
23384                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23385                     b.cls += ' x-init-enable';
23386                 }
23387                 
23388                 b.scope = editorcore;
23389                 tb.add(b);
23390             }
23391         
23392         }
23393         
23394         
23395         
23396         // disable everything...
23397         
23398         this.tb.items.each(function(item){
23399             
23400            if(
23401                 item.id != editorcore.frameId+ '-sourceedit' && 
23402                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23403             ){
23404                 
23405                 item.disable();
23406             }
23407         });
23408         this.rendered = true;
23409         
23410         // the all the btns;
23411         editor.on('editorevent', this.updateToolbar, this);
23412         // other toolbars need to implement this..
23413         //editor.on('editmodechange', this.updateToolbar, this);
23414     },
23415     
23416     
23417     relayBtnCmd : function(btn) {
23418         this.editorcore.relayCmd(btn.cmd);
23419     },
23420     // private used internally
23421     createLink : function(){
23422         Roo.log("create link?");
23423         var url = prompt(this.createLinkText, this.defaultLinkValue);
23424         if(url && url != 'http:/'+'/'){
23425             this.editorcore.relayCmd('createlink', url);
23426         }
23427     },
23428
23429     
23430     /**
23431      * Protected method that will not generally be called directly. It triggers
23432      * a toolbar update by reading the markup state of the current selection in the editor.
23433      */
23434     updateToolbar: function(){
23435
23436         if(!this.editorcore.activated){
23437             this.editor.onFirstFocus();
23438             return;
23439         }
23440
23441         var btns = this.tb.items.map, 
23442             doc = this.editorcore.doc,
23443             frameId = this.editorcore.frameId;
23444
23445         if(!this.disable.font && !Roo.isSafari){
23446             /*
23447             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23448             if(name != this.fontSelect.dom.value){
23449                 this.fontSelect.dom.value = name;
23450             }
23451             */
23452         }
23453         if(!this.disable.format){
23454             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23455             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23456             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23457             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23458         }
23459         if(!this.disable.alignments){
23460             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23461             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23462             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23463         }
23464         if(!Roo.isSafari && !this.disable.lists){
23465             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23466             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23467         }
23468         
23469         var ans = this.editorcore.getAllAncestors();
23470         if (this.formatCombo) {
23471             
23472             
23473             var store = this.formatCombo.store;
23474             this.formatCombo.setValue("");
23475             for (var i =0; i < ans.length;i++) {
23476                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23477                     // select it..
23478                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23479                     break;
23480                 }
23481             }
23482         }
23483         
23484         
23485         
23486         // hides menus... - so this cant be on a menu...
23487         Roo.menu.MenuMgr.hideAll();
23488
23489         //this.editorsyncValue();
23490     },
23491    
23492     
23493     createFontOptions : function(){
23494         var buf = [], fs = this.fontFamilies, ff, lc;
23495         
23496         
23497         
23498         for(var i = 0, len = fs.length; i< len; i++){
23499             ff = fs[i];
23500             lc = ff.toLowerCase();
23501             buf.push(
23502                 '<option value="',lc,'" style="font-family:',ff,';"',
23503                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23504                     ff,
23505                 '</option>'
23506             );
23507         }
23508         return buf.join('');
23509     },
23510     
23511     toggleSourceEdit : function(sourceEditMode){
23512         
23513         Roo.log("toolbar toogle");
23514         if(sourceEditMode === undefined){
23515             sourceEditMode = !this.sourceEditMode;
23516         }
23517         this.sourceEditMode = sourceEditMode === true;
23518         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23519         // just toggle the button?
23520         if(btn.pressed !== this.sourceEditMode){
23521             btn.toggle(this.sourceEditMode);
23522             return;
23523         }
23524         
23525         if(sourceEditMode){
23526             Roo.log("disabling buttons");
23527             this.tb.items.each(function(item){
23528                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23529                     item.disable();
23530                 }
23531             });
23532           
23533         }else{
23534             Roo.log("enabling buttons");
23535             if(this.editorcore.initialized){
23536                 this.tb.items.each(function(item){
23537                     item.enable();
23538                 });
23539             }
23540             
23541         }
23542         Roo.log("calling toggole on editor");
23543         // tell the editor that it's been pressed..
23544         this.editor.toggleSourceEdit(sourceEditMode);
23545        
23546     },
23547      /**
23548      * Object collection of toolbar tooltips for the buttons in the editor. The key
23549      * is the command id associated with that button and the value is a valid QuickTips object.
23550      * For example:
23551 <pre><code>
23552 {
23553     bold : {
23554         title: 'Bold (Ctrl+B)',
23555         text: 'Make the selected text bold.',
23556         cls: 'x-html-editor-tip'
23557     },
23558     italic : {
23559         title: 'Italic (Ctrl+I)',
23560         text: 'Make the selected text italic.',
23561         cls: 'x-html-editor-tip'
23562     },
23563     ...
23564 </code></pre>
23565     * @type Object
23566      */
23567     buttonTips : {
23568         bold : {
23569             title: 'Bold (Ctrl+B)',
23570             text: 'Make the selected text bold.',
23571             cls: 'x-html-editor-tip'
23572         },
23573         italic : {
23574             title: 'Italic (Ctrl+I)',
23575             text: 'Make the selected text italic.',
23576             cls: 'x-html-editor-tip'
23577         },
23578         underline : {
23579             title: 'Underline (Ctrl+U)',
23580             text: 'Underline the selected text.',
23581             cls: 'x-html-editor-tip'
23582         },
23583         strikethrough : {
23584             title: 'Strikethrough',
23585             text: 'Strikethrough the selected text.',
23586             cls: 'x-html-editor-tip'
23587         },
23588         increasefontsize : {
23589             title: 'Grow Text',
23590             text: 'Increase the font size.',
23591             cls: 'x-html-editor-tip'
23592         },
23593         decreasefontsize : {
23594             title: 'Shrink Text',
23595             text: 'Decrease the font size.',
23596             cls: 'x-html-editor-tip'
23597         },
23598         backcolor : {
23599             title: 'Text Highlight Color',
23600             text: 'Change the background color of the selected text.',
23601             cls: 'x-html-editor-tip'
23602         },
23603         forecolor : {
23604             title: 'Font Color',
23605             text: 'Change the color of the selected text.',
23606             cls: 'x-html-editor-tip'
23607         },
23608         justifyleft : {
23609             title: 'Align Text Left',
23610             text: 'Align text to the left.',
23611             cls: 'x-html-editor-tip'
23612         },
23613         justifycenter : {
23614             title: 'Center Text',
23615             text: 'Center text in the editor.',
23616             cls: 'x-html-editor-tip'
23617         },
23618         justifyright : {
23619             title: 'Align Text Right',
23620             text: 'Align text to the right.',
23621             cls: 'x-html-editor-tip'
23622         },
23623         insertunorderedlist : {
23624             title: 'Bullet List',
23625             text: 'Start a bulleted list.',
23626             cls: 'x-html-editor-tip'
23627         },
23628         insertorderedlist : {
23629             title: 'Numbered List',
23630             text: 'Start a numbered list.',
23631             cls: 'x-html-editor-tip'
23632         },
23633         createlink : {
23634             title: 'Hyperlink',
23635             text: 'Make the selected text a hyperlink.',
23636             cls: 'x-html-editor-tip'
23637         },
23638         sourceedit : {
23639             title: 'Source Edit',
23640             text: 'Switch to source editing mode.',
23641             cls: 'x-html-editor-tip'
23642         }
23643     },
23644     // private
23645     onDestroy : function(){
23646         if(this.rendered){
23647             
23648             this.tb.items.each(function(item){
23649                 if(item.menu){
23650                     item.menu.removeAll();
23651                     if(item.menu.el){
23652                         item.menu.el.destroy();
23653                     }
23654                 }
23655                 item.destroy();
23656             });
23657              
23658         }
23659     },
23660     onFirstFocus: function() {
23661         this.tb.items.each(function(item){
23662            item.enable();
23663         });
23664     }
23665 });
23666
23667
23668
23669
23670 // <script type="text/javascript">
23671 /*
23672  * Based on
23673  * Ext JS Library 1.1.1
23674  * Copyright(c) 2006-2007, Ext JS, LLC.
23675  *  
23676  
23677  */
23678
23679  
23680 /**
23681  * @class Roo.form.HtmlEditor.ToolbarContext
23682  * Context Toolbar
23683  * 
23684  * Usage:
23685  *
23686  new Roo.form.HtmlEditor({
23687     ....
23688     toolbars : [
23689         { xtype: 'ToolbarStandard', styles : {} }
23690         { xtype: 'ToolbarContext', disable : {} }
23691     ]
23692 })
23693
23694      
23695  * 
23696  * @config : {Object} disable List of elements to disable.. (not done yet.)
23697  * @config : {Object} styles  Map of styles available.
23698  * 
23699  */
23700
23701 Roo.form.HtmlEditor.ToolbarContext = function(config)
23702 {
23703     
23704     Roo.apply(this, config);
23705     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23706     // dont call parent... till later.
23707     this.styles = this.styles || {};
23708 }
23709
23710  
23711
23712 Roo.form.HtmlEditor.ToolbarContext.types = {
23713     'IMG' : {
23714         width : {
23715             title: "Width",
23716             width: 40
23717         },
23718         height:  {
23719             title: "Height",
23720             width: 40
23721         },
23722         align: {
23723             title: "Align",
23724             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23725             width : 80
23726             
23727         },
23728         border: {
23729             title: "Border",
23730             width: 40
23731         },
23732         alt: {
23733             title: "Alt",
23734             width: 120
23735         },
23736         src : {
23737             title: "Src",
23738             width: 220
23739         }
23740         
23741     },
23742     'A' : {
23743         name : {
23744             title: "Name",
23745             width: 50
23746         },
23747         target:  {
23748             title: "Target",
23749             width: 120
23750         },
23751         href:  {
23752             title: "Href",
23753             width: 220
23754         } // border?
23755         
23756     },
23757     'TABLE' : {
23758         rows : {
23759             title: "Rows",
23760             width: 20
23761         },
23762         cols : {
23763             title: "Cols",
23764             width: 20
23765         },
23766         width : {
23767             title: "Width",
23768             width: 40
23769         },
23770         height : {
23771             title: "Height",
23772             width: 40
23773         },
23774         border : {
23775             title: "Border",
23776             width: 20
23777         }
23778     },
23779     'TD' : {
23780         width : {
23781             title: "Width",
23782             width: 40
23783         },
23784         height : {
23785             title: "Height",
23786             width: 40
23787         },   
23788         align: {
23789             title: "Align",
23790             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23791             width: 80
23792         },
23793         valign: {
23794             title: "Valign",
23795             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23796             width: 80
23797         },
23798         colspan: {
23799             title: "Colspan",
23800             width: 20
23801             
23802         },
23803          'font-family'  : {
23804             title : "Font",
23805             style : 'fontFamily',
23806             displayField: 'display',
23807             optname : 'font-family',
23808             width: 140
23809         }
23810     },
23811     'INPUT' : {
23812         name : {
23813             title: "name",
23814             width: 120
23815         },
23816         value : {
23817             title: "Value",
23818             width: 120
23819         },
23820         width : {
23821             title: "Width",
23822             width: 40
23823         }
23824     },
23825     'LABEL' : {
23826         'for' : {
23827             title: "For",
23828             width: 120
23829         }
23830     },
23831     'TEXTAREA' : {
23832           name : {
23833             title: "name",
23834             width: 120
23835         },
23836         rows : {
23837             title: "Rows",
23838             width: 20
23839         },
23840         cols : {
23841             title: "Cols",
23842             width: 20
23843         }
23844     },
23845     'SELECT' : {
23846         name : {
23847             title: "name",
23848             width: 120
23849         },
23850         selectoptions : {
23851             title: "Options",
23852             width: 200
23853         }
23854     },
23855     
23856     // should we really allow this??
23857     // should this just be 
23858     'BODY' : {
23859         title : {
23860             title: "Title",
23861             width: 200,
23862             disabled : true
23863         }
23864     },
23865     'SPAN' : {
23866         'font-family'  : {
23867             title : "Font",
23868             style : 'fontFamily',
23869             displayField: 'display',
23870             optname : 'font-family',
23871             width: 140
23872         }
23873     },
23874     'DIV' : {
23875         'font-family'  : {
23876             title : "Font",
23877             style : 'fontFamily',
23878             displayField: 'display',
23879             optname : 'font-family',
23880             width: 140
23881         }
23882     },
23883      'P' : {
23884         'font-family'  : {
23885             title : "Font",
23886             style : 'fontFamily',
23887             displayField: 'display',
23888             optname : 'font-family',
23889             width: 140
23890         }
23891     },
23892     
23893     '*' : {
23894         // empty..
23895     }
23896
23897 };
23898
23899 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23900 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23901
23902 Roo.form.HtmlEditor.ToolbarContext.options = {
23903         'font-family'  : [ 
23904                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23905                 [ 'Courier New', 'Courier New'],
23906                 [ 'Tahoma', 'Tahoma'],
23907                 [ 'Times New Roman,serif', 'Times'],
23908                 [ 'Verdana','Verdana' ]
23909         ]
23910 };
23911
23912 // fixme - these need to be configurable..
23913  
23914
23915 //Roo.form.HtmlEditor.ToolbarContext.types
23916
23917
23918 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23919     
23920     tb: false,
23921     
23922     rendered: false,
23923     
23924     editor : false,
23925     editorcore : false,
23926     /**
23927      * @cfg {Object} disable  List of toolbar elements to disable
23928          
23929      */
23930     disable : false,
23931     /**
23932      * @cfg {Object} styles List of styles 
23933      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23934      *
23935      * These must be defined in the page, so they get rendered correctly..
23936      * .headline { }
23937      * TD.underline { }
23938      * 
23939      */
23940     styles : false,
23941     
23942     options: false,
23943     
23944     toolbars : false,
23945     
23946     init : function(editor)
23947     {
23948         this.editor = editor;
23949         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23950         var editorcore = this.editorcore;
23951         
23952         var fid = editorcore.frameId;
23953         var etb = this;
23954         function btn(id, toggle, handler){
23955             var xid = fid + '-'+ id ;
23956             return {
23957                 id : xid,
23958                 cmd : id,
23959                 cls : 'x-btn-icon x-edit-'+id,
23960                 enableToggle:toggle !== false,
23961                 scope: editorcore, // was editor...
23962                 handler:handler||editorcore.relayBtnCmd,
23963                 clickEvent:'mousedown',
23964                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23965                 tabIndex:-1
23966             };
23967         }
23968         // create a new element.
23969         var wdiv = editor.wrap.createChild({
23970                 tag: 'div'
23971             }, editor.wrap.dom.firstChild.nextSibling, true);
23972         
23973         // can we do this more than once??
23974         
23975          // stop form submits
23976       
23977  
23978         // disable everything...
23979         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23980         this.toolbars = {};
23981            
23982         for (var i in  ty) {
23983           
23984             this.toolbars[i] = this.buildToolbar(ty[i],i);
23985         }
23986         this.tb = this.toolbars.BODY;
23987         this.tb.el.show();
23988         this.buildFooter();
23989         this.footer.show();
23990         editor.on('hide', function( ) { this.footer.hide() }, this);
23991         editor.on('show', function( ) { this.footer.show() }, this);
23992         
23993          
23994         this.rendered = true;
23995         
23996         // the all the btns;
23997         editor.on('editorevent', this.updateToolbar, this);
23998         // other toolbars need to implement this..
23999         //editor.on('editmodechange', this.updateToolbar, this);
24000     },
24001     
24002     
24003     
24004     /**
24005      * Protected method that will not generally be called directly. It triggers
24006      * a toolbar update by reading the markup state of the current selection in the editor.
24007      *
24008      * Note you can force an update by calling on('editorevent', scope, false)
24009      */
24010     updateToolbar: function(editor,ev,sel){
24011
24012         //Roo.log(ev);
24013         // capture mouse up - this is handy for selecting images..
24014         // perhaps should go somewhere else...
24015         if(!this.editorcore.activated){
24016              this.editor.onFirstFocus();
24017             return;
24018         }
24019         
24020         
24021         
24022         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24023         // selectNode - might want to handle IE?
24024         if (ev &&
24025             (ev.type == 'mouseup' || ev.type == 'click' ) &&
24026             ev.target && ev.target.tagName == 'IMG') {
24027             // they have click on an image...
24028             // let's see if we can change the selection...
24029             sel = ev.target;
24030          
24031               var nodeRange = sel.ownerDocument.createRange();
24032             try {
24033                 nodeRange.selectNode(sel);
24034             } catch (e) {
24035                 nodeRange.selectNodeContents(sel);
24036             }
24037             //nodeRange.collapse(true);
24038             var s = this.editorcore.win.getSelection();
24039             s.removeAllRanges();
24040             s.addRange(nodeRange);
24041         }  
24042         
24043       
24044         var updateFooter = sel ? false : true;
24045         
24046         
24047         var ans = this.editorcore.getAllAncestors();
24048         
24049         // pick
24050         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24051         
24052         if (!sel) { 
24053             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24054             sel = sel ? sel : this.editorcore.doc.body;
24055             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24056             
24057         }
24058         // pick a menu that exists..
24059         var tn = sel.tagName.toUpperCase();
24060         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24061         
24062         tn = sel.tagName.toUpperCase();
24063         
24064         var lastSel = this.tb.selectedNode;
24065         
24066         this.tb.selectedNode = sel;
24067         
24068         // if current menu does not match..
24069         
24070         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24071                 
24072             this.tb.el.hide();
24073             ///console.log("show: " + tn);
24074             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24075             this.tb.el.show();
24076             // update name
24077             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24078             
24079             
24080             // update attributes
24081             if (this.tb.fields) {
24082                 this.tb.fields.each(function(e) {
24083                     if (e.stylename) {
24084                         e.setValue(sel.style[e.stylename]);
24085                         return;
24086                     } 
24087                    e.setValue(sel.getAttribute(e.attrname));
24088                 });
24089             }
24090             
24091             var hasStyles = false;
24092             for(var i in this.styles) {
24093                 hasStyles = true;
24094                 break;
24095             }
24096             
24097             // update styles
24098             if (hasStyles) { 
24099                 var st = this.tb.fields.item(0);
24100                 
24101                 st.store.removeAll();
24102                
24103                 
24104                 var cn = sel.className.split(/\s+/);
24105                 
24106                 var avs = [];
24107                 if (this.styles['*']) {
24108                     
24109                     Roo.each(this.styles['*'], function(v) {
24110                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24111                     });
24112                 }
24113                 if (this.styles[tn]) { 
24114                     Roo.each(this.styles[tn], function(v) {
24115                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24116                     });
24117                 }
24118                 
24119                 st.store.loadData(avs);
24120                 st.collapse();
24121                 st.setValue(cn);
24122             }
24123             // flag our selected Node.
24124             this.tb.selectedNode = sel;
24125            
24126            
24127             Roo.menu.MenuMgr.hideAll();
24128
24129         }
24130         
24131         if (!updateFooter) {
24132             //this.footDisp.dom.innerHTML = ''; 
24133             return;
24134         }
24135         // update the footer
24136         //
24137         var html = '';
24138         
24139         this.footerEls = ans.reverse();
24140         Roo.each(this.footerEls, function(a,i) {
24141             if (!a) { return; }
24142             html += html.length ? ' &gt; '  :  '';
24143             
24144             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24145             
24146         });
24147        
24148         // 
24149         var sz = this.footDisp.up('td').getSize();
24150         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24151         this.footDisp.dom.style.marginLeft = '5px';
24152         
24153         this.footDisp.dom.style.overflow = 'hidden';
24154         
24155         this.footDisp.dom.innerHTML = html;
24156             
24157         //this.editorsyncValue();
24158     },
24159      
24160     
24161    
24162        
24163     // private
24164     onDestroy : function(){
24165         if(this.rendered){
24166             
24167             this.tb.items.each(function(item){
24168                 if(item.menu){
24169                     item.menu.removeAll();
24170                     if(item.menu.el){
24171                         item.menu.el.destroy();
24172                     }
24173                 }
24174                 item.destroy();
24175             });
24176              
24177         }
24178     },
24179     onFirstFocus: function() {
24180         // need to do this for all the toolbars..
24181         this.tb.items.each(function(item){
24182            item.enable();
24183         });
24184     },
24185     buildToolbar: function(tlist, nm)
24186     {
24187         var editor = this.editor;
24188         var editorcore = this.editorcore;
24189          // create a new element.
24190         var wdiv = editor.wrap.createChild({
24191                 tag: 'div'
24192             }, editor.wrap.dom.firstChild.nextSibling, true);
24193         
24194        
24195         var tb = new Roo.Toolbar(wdiv);
24196         // add the name..
24197         
24198         tb.add(nm+ ":&nbsp;");
24199         
24200         var styles = [];
24201         for(var i in this.styles) {
24202             styles.push(i);
24203         }
24204         
24205         // styles...
24206         if (styles && styles.length) {
24207             
24208             // this needs a multi-select checkbox...
24209             tb.addField( new Roo.form.ComboBox({
24210                 store: new Roo.data.SimpleStore({
24211                     id : 'val',
24212                     fields: ['val', 'selected'],
24213                     data : [] 
24214                 }),
24215                 name : '-roo-edit-className',
24216                 attrname : 'className',
24217                 displayField: 'val',
24218                 typeAhead: false,
24219                 mode: 'local',
24220                 editable : false,
24221                 triggerAction: 'all',
24222                 emptyText:'Select Style',
24223                 selectOnFocus:true,
24224                 width: 130,
24225                 listeners : {
24226                     'select': function(c, r, i) {
24227                         // initial support only for on class per el..
24228                         tb.selectedNode.className =  r ? r.get('val') : '';
24229                         editorcore.syncValue();
24230                     }
24231                 }
24232     
24233             }));
24234         }
24235         
24236         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24237         var tbops = tbc.options;
24238         
24239         for (var i in tlist) {
24240             
24241             var item = tlist[i];
24242             tb.add(item.title + ":&nbsp;");
24243             
24244             
24245             //optname == used so you can configure the options available..
24246             var opts = item.opts ? item.opts : false;
24247             if (item.optname) {
24248                 opts = tbops[item.optname];
24249            
24250             }
24251             
24252             if (opts) {
24253                 // opts == pulldown..
24254                 tb.addField( new Roo.form.ComboBox({
24255                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24256                         id : 'val',
24257                         fields: ['val', 'display'],
24258                         data : opts  
24259                     }),
24260                     name : '-roo-edit-' + i,
24261                     attrname : i,
24262                     stylename : item.style ? item.style : false,
24263                     displayField: item.displayField ? item.displayField : 'val',
24264                     valueField :  'val',
24265                     typeAhead: false,
24266                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24267                     editable : false,
24268                     triggerAction: 'all',
24269                     emptyText:'Select',
24270                     selectOnFocus:true,
24271                     width: item.width ? item.width  : 130,
24272                     listeners : {
24273                         'select': function(c, r, i) {
24274                             if (c.stylename) {
24275                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24276                                 return;
24277                             }
24278                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24279                         }
24280                     }
24281
24282                 }));
24283                 continue;
24284                     
24285                  
24286                 
24287                 tb.addField( new Roo.form.TextField({
24288                     name: i,
24289                     width: 100,
24290                     //allowBlank:false,
24291                     value: ''
24292                 }));
24293                 continue;
24294             }
24295             tb.addField( new Roo.form.TextField({
24296                 name: '-roo-edit-' + i,
24297                 attrname : i,
24298                 
24299                 width: item.width,
24300                 //allowBlank:true,
24301                 value: '',
24302                 listeners: {
24303                     'change' : function(f, nv, ov) {
24304                         tb.selectedNode.setAttribute(f.attrname, nv);
24305                         editorcore.syncValue();
24306                     }
24307                 }
24308             }));
24309              
24310         }
24311         
24312         var _this = this;
24313         
24314         if(nm == 'BODY'){
24315             tb.addSeparator();
24316         
24317             tb.addButton( {
24318                 text: 'Stylesheets',
24319
24320                 listeners : {
24321                     click : function ()
24322                     {
24323                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24324                     }
24325                 }
24326             });
24327         }
24328         
24329         tb.addFill();
24330         tb.addButton( {
24331             text: 'Remove Tag',
24332     
24333             listeners : {
24334                 click : function ()
24335                 {
24336                     // remove
24337                     // undo does not work.
24338                      
24339                     var sn = tb.selectedNode;
24340                     
24341                     var pn = sn.parentNode;
24342                     
24343                     var stn =  sn.childNodes[0];
24344                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24345                     while (sn.childNodes.length) {
24346                         var node = sn.childNodes[0];
24347                         sn.removeChild(node);
24348                         //Roo.log(node);
24349                         pn.insertBefore(node, sn);
24350                         
24351                     }
24352                     pn.removeChild(sn);
24353                     var range = editorcore.createRange();
24354         
24355                     range.setStart(stn,0);
24356                     range.setEnd(en,0); //????
24357                     //range.selectNode(sel);
24358                     
24359                     
24360                     var selection = editorcore.getSelection();
24361                     selection.removeAllRanges();
24362                     selection.addRange(range);
24363                     
24364                     
24365                     
24366                     //_this.updateToolbar(null, null, pn);
24367                     _this.updateToolbar(null, null, null);
24368                     _this.footDisp.dom.innerHTML = ''; 
24369                 }
24370             }
24371             
24372                     
24373                 
24374             
24375         });
24376         
24377         
24378         tb.el.on('click', function(e){
24379             e.preventDefault(); // what does this do?
24380         });
24381         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24382         tb.el.hide();
24383         tb.name = nm;
24384         // dont need to disable them... as they will get hidden
24385         return tb;
24386          
24387         
24388     },
24389     buildFooter : function()
24390     {
24391         
24392         var fel = this.editor.wrap.createChild();
24393         this.footer = new Roo.Toolbar(fel);
24394         // toolbar has scrolly on left / right?
24395         var footDisp= new Roo.Toolbar.Fill();
24396         var _t = this;
24397         this.footer.add(
24398             {
24399                 text : '&lt;',
24400                 xtype: 'Button',
24401                 handler : function() {
24402                     _t.footDisp.scrollTo('left',0,true)
24403                 }
24404             }
24405         );
24406         this.footer.add( footDisp );
24407         this.footer.add( 
24408             {
24409                 text : '&gt;',
24410                 xtype: 'Button',
24411                 handler : function() {
24412                     // no animation..
24413                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24414                 }
24415             }
24416         );
24417         var fel = Roo.get(footDisp.el);
24418         fel.addClass('x-editor-context');
24419         this.footDispWrap = fel; 
24420         this.footDispWrap.overflow  = 'hidden';
24421         
24422         this.footDisp = fel.createChild();
24423         this.footDispWrap.on('click', this.onContextClick, this)
24424         
24425         
24426     },
24427     onContextClick : function (ev,dom)
24428     {
24429         ev.preventDefault();
24430         var  cn = dom.className;
24431         //Roo.log(cn);
24432         if (!cn.match(/x-ed-loc-/)) {
24433             return;
24434         }
24435         var n = cn.split('-').pop();
24436         var ans = this.footerEls;
24437         var sel = ans[n];
24438         
24439          // pick
24440         var range = this.editorcore.createRange();
24441         
24442         range.selectNodeContents(sel);
24443         //range.selectNode(sel);
24444         
24445         
24446         var selection = this.editorcore.getSelection();
24447         selection.removeAllRanges();
24448         selection.addRange(range);
24449         
24450         
24451         
24452         this.updateToolbar(null, null, sel);
24453         
24454         
24455     }
24456     
24457     
24458     
24459     
24460     
24461 });
24462
24463
24464
24465
24466
24467 /*
24468  * Based on:
24469  * Ext JS Library 1.1.1
24470  * Copyright(c) 2006-2007, Ext JS, LLC.
24471  *
24472  * Originally Released Under LGPL - original licence link has changed is not relivant.
24473  *
24474  * Fork - LGPL
24475  * <script type="text/javascript">
24476  */
24477  
24478 /**
24479  * @class Roo.form.BasicForm
24480  * @extends Roo.util.Observable
24481  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24482  * @constructor
24483  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24484  * @param {Object} config Configuration options
24485  */
24486 Roo.form.BasicForm = function(el, config){
24487     this.allItems = [];
24488     this.childForms = [];
24489     Roo.apply(this, config);
24490     /*
24491      * The Roo.form.Field items in this form.
24492      * @type MixedCollection
24493      */
24494      
24495      
24496     this.items = new Roo.util.MixedCollection(false, function(o){
24497         return o.id || (o.id = Roo.id());
24498     });
24499     this.addEvents({
24500         /**
24501          * @event beforeaction
24502          * Fires before any action is performed. Return false to cancel the action.
24503          * @param {Form} this
24504          * @param {Action} action The action to be performed
24505          */
24506         beforeaction: true,
24507         /**
24508          * @event actionfailed
24509          * Fires when an action fails.
24510          * @param {Form} this
24511          * @param {Action} action The action that failed
24512          */
24513         actionfailed : true,
24514         /**
24515          * @event actioncomplete
24516          * Fires when an action is completed.
24517          * @param {Form} this
24518          * @param {Action} action The action that completed
24519          */
24520         actioncomplete : true
24521     });
24522     if(el){
24523         this.initEl(el);
24524     }
24525     Roo.form.BasicForm.superclass.constructor.call(this);
24526     
24527     Roo.form.BasicForm.popover.apply();
24528 };
24529
24530 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24531     /**
24532      * @cfg {String} method
24533      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24534      */
24535     /**
24536      * @cfg {DataReader} reader
24537      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24538      * This is optional as there is built-in support for processing JSON.
24539      */
24540     /**
24541      * @cfg {DataReader} errorReader
24542      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24543      * This is completely optional as there is built-in support for processing JSON.
24544      */
24545     /**
24546      * @cfg {String} url
24547      * The URL to use for form actions if one isn't supplied in the action options.
24548      */
24549     /**
24550      * @cfg {Boolean} fileUpload
24551      * Set to true if this form is a file upload.
24552      */
24553      
24554     /**
24555      * @cfg {Object} baseParams
24556      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24557      */
24558      /**
24559      
24560     /**
24561      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24562      */
24563     timeout: 30,
24564
24565     // private
24566     activeAction : null,
24567
24568     /**
24569      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24570      * or setValues() data instead of when the form was first created.
24571      */
24572     trackResetOnLoad : false,
24573     
24574     
24575     /**
24576      * childForms - used for multi-tab forms
24577      * @type {Array}
24578      */
24579     childForms : false,
24580     
24581     /**
24582      * allItems - full list of fields.
24583      * @type {Array}
24584      */
24585     allItems : false,
24586     
24587     /**
24588      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24589      * element by passing it or its id or mask the form itself by passing in true.
24590      * @type Mixed
24591      */
24592     waitMsgTarget : false,
24593     
24594     /**
24595      * @type Boolean
24596      */
24597     disableMask : false,
24598     
24599     /**
24600      * @cfg {Boolean} errorMask (true|false) default false
24601      */
24602     errorMask : false,
24603     
24604     /**
24605      * @cfg {Number} maskOffset Default 100
24606      */
24607     maskOffset : 100,
24608
24609     // private
24610     initEl : function(el){
24611         this.el = Roo.get(el);
24612         this.id = this.el.id || Roo.id();
24613         this.el.on('submit', this.onSubmit, this);
24614         this.el.addClass('x-form');
24615     },
24616
24617     // private
24618     onSubmit : function(e){
24619         e.stopEvent();
24620     },
24621
24622     /**
24623      * Returns true if client-side validation on the form is successful.
24624      * @return Boolean
24625      */
24626     isValid : function(){
24627         var valid = true;
24628         var target = false;
24629         this.items.each(function(f){
24630             if(f.validate()){
24631                 return;
24632             }
24633             
24634             valid = false;
24635                 
24636             if(!target && f.el.isVisible(true)){
24637                 target = f;
24638             }
24639         });
24640         
24641         if(this.errorMask && !valid){
24642             Roo.form.BasicForm.popover.mask(this, target);
24643         }
24644         
24645         return valid;
24646     },
24647     /**
24648      * Returns array of invalid form fields.
24649      * @return Array
24650      */
24651     
24652     invalidFields : function()
24653     {
24654         var ret = [];
24655         this.items.each(function(f){
24656             if(f.validate()){
24657                 return;
24658             }
24659             ret.push(f);
24660             
24661         });
24662         
24663         return ret;
24664     },
24665     
24666     
24667     /**
24668      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24669      * @return Boolean
24670      */
24671     isDirty : function(){
24672         var dirty = false;
24673         this.items.each(function(f){
24674            if(f.isDirty()){
24675                dirty = true;
24676                return false;
24677            }
24678         });
24679         return dirty;
24680     },
24681     
24682     /**
24683      * Returns true if any fields in this form have changed since their original load. (New version)
24684      * @return Boolean
24685      */
24686     
24687     hasChanged : function()
24688     {
24689         var dirty = false;
24690         this.items.each(function(f){
24691            if(f.hasChanged()){
24692                dirty = true;
24693                return false;
24694            }
24695         });
24696         return dirty;
24697         
24698     },
24699     /**
24700      * Resets all hasChanged to 'false' -
24701      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24702      * So hasChanged storage is only to be used for this purpose
24703      * @return Boolean
24704      */
24705     resetHasChanged : function()
24706     {
24707         this.items.each(function(f){
24708            f.resetHasChanged();
24709         });
24710         
24711     },
24712     
24713     
24714     /**
24715      * Performs a predefined action (submit or load) or custom actions you define on this form.
24716      * @param {String} actionName The name of the action type
24717      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24718      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24719      * accept other config options):
24720      * <pre>
24721 Property          Type             Description
24722 ----------------  ---------------  ----------------------------------------------------------------------------------
24723 url               String           The url for the action (defaults to the form's url)
24724 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24725 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24726 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24727                                    validate the form on the client (defaults to false)
24728      * </pre>
24729      * @return {BasicForm} this
24730      */
24731     doAction : function(action, options){
24732         if(typeof action == 'string'){
24733             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24734         }
24735         if(this.fireEvent('beforeaction', this, action) !== false){
24736             this.beforeAction(action);
24737             action.run.defer(100, action);
24738         }
24739         return this;
24740     },
24741
24742     /**
24743      * Shortcut to do a submit action.
24744      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24745      * @return {BasicForm} this
24746      */
24747     submit : function(options){
24748         this.doAction('submit', options);
24749         return this;
24750     },
24751
24752     /**
24753      * Shortcut to do a load action.
24754      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24755      * @return {BasicForm} this
24756      */
24757     load : function(options){
24758         this.doAction('load', options);
24759         return this;
24760     },
24761
24762     /**
24763      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24764      * @param {Record} record The record to edit
24765      * @return {BasicForm} this
24766      */
24767     updateRecord : function(record){
24768         record.beginEdit();
24769         var fs = record.fields;
24770         fs.each(function(f){
24771             var field = this.findField(f.name);
24772             if(field){
24773                 record.set(f.name, field.getValue());
24774             }
24775         }, this);
24776         record.endEdit();
24777         return this;
24778     },
24779
24780     /**
24781      * Loads an Roo.data.Record into this form.
24782      * @param {Record} record The record to load
24783      * @return {BasicForm} this
24784      */
24785     loadRecord : function(record){
24786         this.setValues(record.data);
24787         return this;
24788     },
24789
24790     // private
24791     beforeAction : function(action){
24792         var o = action.options;
24793         
24794         if(!this.disableMask) {
24795             if(this.waitMsgTarget === true){
24796                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24797             }else if(this.waitMsgTarget){
24798                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24799                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24800             }else {
24801                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24802             }
24803         }
24804         
24805          
24806     },
24807
24808     // private
24809     afterAction : function(action, success){
24810         this.activeAction = null;
24811         var o = action.options;
24812         
24813         if(!this.disableMask) {
24814             if(this.waitMsgTarget === true){
24815                 this.el.unmask();
24816             }else if(this.waitMsgTarget){
24817                 this.waitMsgTarget.unmask();
24818             }else{
24819                 Roo.MessageBox.updateProgress(1);
24820                 Roo.MessageBox.hide();
24821             }
24822         }
24823         
24824         if(success){
24825             if(o.reset){
24826                 this.reset();
24827             }
24828             Roo.callback(o.success, o.scope, [this, action]);
24829             this.fireEvent('actioncomplete', this, action);
24830             
24831         }else{
24832             
24833             // failure condition..
24834             // we have a scenario where updates need confirming.
24835             // eg. if a locking scenario exists..
24836             // we look for { errors : { needs_confirm : true }} in the response.
24837             if (
24838                 (typeof(action.result) != 'undefined')  &&
24839                 (typeof(action.result.errors) != 'undefined')  &&
24840                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24841            ){
24842                 var _t = this;
24843                 Roo.MessageBox.confirm(
24844                     "Change requires confirmation",
24845                     action.result.errorMsg,
24846                     function(r) {
24847                         if (r != 'yes') {
24848                             return;
24849                         }
24850                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24851                     }
24852                     
24853                 );
24854                 
24855                 
24856                 
24857                 return;
24858             }
24859             
24860             Roo.callback(o.failure, o.scope, [this, action]);
24861             // show an error message if no failed handler is set..
24862             if (!this.hasListener('actionfailed')) {
24863                 Roo.MessageBox.alert("Error",
24864                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24865                         action.result.errorMsg :
24866                         "Saving Failed, please check your entries or try again"
24867                 );
24868             }
24869             
24870             this.fireEvent('actionfailed', this, action);
24871         }
24872         
24873     },
24874
24875     /**
24876      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24877      * @param {String} id The value to search for
24878      * @return Field
24879      */
24880     findField : function(id){
24881         var field = this.items.get(id);
24882         if(!field){
24883             this.items.each(function(f){
24884                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24885                     field = f;
24886                     return false;
24887                 }
24888             });
24889         }
24890         return field || null;
24891     },
24892
24893     /**
24894      * Add a secondary form to this one, 
24895      * Used to provide tabbed forms. One form is primary, with hidden values 
24896      * which mirror the elements from the other forms.
24897      * 
24898      * @param {Roo.form.Form} form to add.
24899      * 
24900      */
24901     addForm : function(form)
24902     {
24903        
24904         if (this.childForms.indexOf(form) > -1) {
24905             // already added..
24906             return;
24907         }
24908         this.childForms.push(form);
24909         var n = '';
24910         Roo.each(form.allItems, function (fe) {
24911             
24912             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24913             if (this.findField(n)) { // already added..
24914                 return;
24915             }
24916             var add = new Roo.form.Hidden({
24917                 name : n
24918             });
24919             add.render(this.el);
24920             
24921             this.add( add );
24922         }, this);
24923         
24924     },
24925     /**
24926      * Mark fields in this form invalid in bulk.
24927      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24928      * @return {BasicForm} this
24929      */
24930     markInvalid : function(errors){
24931         if(errors instanceof Array){
24932             for(var i = 0, len = errors.length; i < len; i++){
24933                 var fieldError = errors[i];
24934                 var f = this.findField(fieldError.id);
24935                 if(f){
24936                     f.markInvalid(fieldError.msg);
24937                 }
24938             }
24939         }else{
24940             var field, id;
24941             for(id in errors){
24942                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24943                     field.markInvalid(errors[id]);
24944                 }
24945             }
24946         }
24947         Roo.each(this.childForms || [], function (f) {
24948             f.markInvalid(errors);
24949         });
24950         
24951         return this;
24952     },
24953
24954     /**
24955      * Set values for fields in this form in bulk.
24956      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24957      * @return {BasicForm} this
24958      */
24959     setValues : function(values){
24960         if(values instanceof Array){ // array of objects
24961             for(var i = 0, len = values.length; i < len; i++){
24962                 var v = values[i];
24963                 var f = this.findField(v.id);
24964                 if(f){
24965                     f.setValue(v.value);
24966                     if(this.trackResetOnLoad){
24967                         f.originalValue = f.getValue();
24968                     }
24969                 }
24970             }
24971         }else{ // object hash
24972             var field, id;
24973             for(id in values){
24974                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24975                     
24976                     if (field.setFromData && 
24977                         field.valueField && 
24978                         field.displayField &&
24979                         // combos' with local stores can 
24980                         // be queried via setValue()
24981                         // to set their value..
24982                         (field.store && !field.store.isLocal)
24983                         ) {
24984                         // it's a combo
24985                         var sd = { };
24986                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24987                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24988                         field.setFromData(sd);
24989                         
24990                     } else {
24991                         field.setValue(values[id]);
24992                     }
24993                     
24994                     
24995                     if(this.trackResetOnLoad){
24996                         field.originalValue = field.getValue();
24997                     }
24998                 }
24999             }
25000         }
25001         this.resetHasChanged();
25002         
25003         
25004         Roo.each(this.childForms || [], function (f) {
25005             f.setValues(values);
25006             f.resetHasChanged();
25007         });
25008                 
25009         return this;
25010     },
25011  
25012     /**
25013      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25014      * they are returned as an array.
25015      * @param {Boolean} asString
25016      * @return {Object}
25017      */
25018     getValues : function(asString){
25019         if (this.childForms) {
25020             // copy values from the child forms
25021             Roo.each(this.childForms, function (f) {
25022                 this.setValues(f.getValues());
25023             }, this);
25024         }
25025         
25026         // use formdata
25027         if (typeof(FormData) != 'undefined' && asString !== true) {
25028             // this relies on a 'recent' version of chrome apparently...
25029             try {
25030                 var fd = (new FormData(this.el.dom)).entries();
25031                 var ret = {};
25032                 var ent = fd.next();
25033                 while (!ent.done) {
25034                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25035                     ent = fd.next();
25036                 };
25037                 return ret;
25038             } catch(e) {
25039                 
25040             }
25041             
25042         }
25043         
25044         
25045         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25046         if(asString === true){
25047             return fs;
25048         }
25049         return Roo.urlDecode(fs);
25050     },
25051     
25052     /**
25053      * Returns the fields in this form as an object with key/value pairs. 
25054      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25055      * @return {Object}
25056      */
25057     getFieldValues : function(with_hidden)
25058     {
25059         if (this.childForms) {
25060             // copy values from the child forms
25061             // should this call getFieldValues - probably not as we do not currently copy
25062             // hidden fields when we generate..
25063             Roo.each(this.childForms, function (f) {
25064                 this.setValues(f.getValues());
25065             }, this);
25066         }
25067         
25068         var ret = {};
25069         this.items.each(function(f){
25070             if (!f.getName()) {
25071                 return;
25072             }
25073             var v = f.getValue();
25074             if (f.inputType =='radio') {
25075                 if (typeof(ret[f.getName()]) == 'undefined') {
25076                     ret[f.getName()] = ''; // empty..
25077                 }
25078                 
25079                 if (!f.el.dom.checked) {
25080                     return;
25081                     
25082                 }
25083                 v = f.el.dom.value;
25084                 
25085             }
25086             
25087             // not sure if this supported any more..
25088             if ((typeof(v) == 'object') && f.getRawValue) {
25089                 v = f.getRawValue() ; // dates..
25090             }
25091             // combo boxes where name != hiddenName...
25092             if (f.name != f.getName()) {
25093                 ret[f.name] = f.getRawValue();
25094             }
25095             ret[f.getName()] = v;
25096         });
25097         
25098         return ret;
25099     },
25100
25101     /**
25102      * Clears all invalid messages in this form.
25103      * @return {BasicForm} this
25104      */
25105     clearInvalid : function(){
25106         this.items.each(function(f){
25107            f.clearInvalid();
25108         });
25109         
25110         Roo.each(this.childForms || [], function (f) {
25111             f.clearInvalid();
25112         });
25113         
25114         
25115         return this;
25116     },
25117
25118     /**
25119      * Resets this form.
25120      * @return {BasicForm} this
25121      */
25122     reset : function(){
25123         this.items.each(function(f){
25124             f.reset();
25125         });
25126         
25127         Roo.each(this.childForms || [], function (f) {
25128             f.reset();
25129         });
25130         this.resetHasChanged();
25131         
25132         return this;
25133     },
25134
25135     /**
25136      * Add Roo.form components to this form.
25137      * @param {Field} field1
25138      * @param {Field} field2 (optional)
25139      * @param {Field} etc (optional)
25140      * @return {BasicForm} this
25141      */
25142     add : function(){
25143         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25144         return this;
25145     },
25146
25147
25148     /**
25149      * Removes a field from the items collection (does NOT remove its markup).
25150      * @param {Field} field
25151      * @return {BasicForm} this
25152      */
25153     remove : function(field){
25154         this.items.remove(field);
25155         return this;
25156     },
25157
25158     /**
25159      * Looks at the fields in this form, checks them for an id attribute,
25160      * and calls applyTo on the existing dom element with that id.
25161      * @return {BasicForm} this
25162      */
25163     render : function(){
25164         this.items.each(function(f){
25165             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25166                 f.applyTo(f.id);
25167             }
25168         });
25169         return this;
25170     },
25171
25172     /**
25173      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25174      * @param {Object} values
25175      * @return {BasicForm} this
25176      */
25177     applyToFields : function(o){
25178         this.items.each(function(f){
25179            Roo.apply(f, o);
25180         });
25181         return this;
25182     },
25183
25184     /**
25185      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25186      * @param {Object} values
25187      * @return {BasicForm} this
25188      */
25189     applyIfToFields : function(o){
25190         this.items.each(function(f){
25191            Roo.applyIf(f, o);
25192         });
25193         return this;
25194     }
25195 });
25196
25197 // back compat
25198 Roo.BasicForm = Roo.form.BasicForm;
25199
25200 Roo.apply(Roo.form.BasicForm, {
25201     
25202     popover : {
25203         
25204         padding : 5,
25205         
25206         isApplied : false,
25207         
25208         isMasked : false,
25209         
25210         form : false,
25211         
25212         target : false,
25213         
25214         intervalID : false,
25215         
25216         maskEl : false,
25217         
25218         apply : function()
25219         {
25220             if(this.isApplied){
25221                 return;
25222             }
25223             
25224             this.maskEl = {
25225                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25226                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25227                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25228                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25229             };
25230             
25231             this.maskEl.top.enableDisplayMode("block");
25232             this.maskEl.left.enableDisplayMode("block");
25233             this.maskEl.bottom.enableDisplayMode("block");
25234             this.maskEl.right.enableDisplayMode("block");
25235             
25236             Roo.get(document.body).on('click', function(){
25237                 this.unmask();
25238             }, this);
25239             
25240             Roo.get(document.body).on('touchstart', function(){
25241                 this.unmask();
25242             }, this);
25243             
25244             this.isApplied = true
25245         },
25246         
25247         mask : function(form, target)
25248         {
25249             this.form = form;
25250             
25251             this.target = target;
25252             
25253             if(!this.form.errorMask || !target.el){
25254                 return;
25255             }
25256             
25257             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25258             
25259             var ot = this.target.el.calcOffsetsTo(scrollable);
25260             
25261             var scrollTo = ot[1] - this.form.maskOffset;
25262             
25263             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25264             
25265             scrollable.scrollTo('top', scrollTo);
25266             
25267             var el = this.target.wrap || this.target.el;
25268             
25269             var box = el.getBox();
25270             
25271             this.maskEl.top.setStyle('position', 'absolute');
25272             this.maskEl.top.setStyle('z-index', 10000);
25273             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25274             this.maskEl.top.setLeft(0);
25275             this.maskEl.top.setTop(0);
25276             this.maskEl.top.show();
25277             
25278             this.maskEl.left.setStyle('position', 'absolute');
25279             this.maskEl.left.setStyle('z-index', 10000);
25280             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25281             this.maskEl.left.setLeft(0);
25282             this.maskEl.left.setTop(box.y - this.padding);
25283             this.maskEl.left.show();
25284
25285             this.maskEl.bottom.setStyle('position', 'absolute');
25286             this.maskEl.bottom.setStyle('z-index', 10000);
25287             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25288             this.maskEl.bottom.setLeft(0);
25289             this.maskEl.bottom.setTop(box.bottom + this.padding);
25290             this.maskEl.bottom.show();
25291
25292             this.maskEl.right.setStyle('position', 'absolute');
25293             this.maskEl.right.setStyle('z-index', 10000);
25294             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25295             this.maskEl.right.setLeft(box.right + this.padding);
25296             this.maskEl.right.setTop(box.y - this.padding);
25297             this.maskEl.right.show();
25298
25299             this.intervalID = window.setInterval(function() {
25300                 Roo.form.BasicForm.popover.unmask();
25301             }, 10000);
25302
25303             window.onwheel = function(){ return false;};
25304             
25305             (function(){ this.isMasked = true; }).defer(500, this);
25306             
25307         },
25308         
25309         unmask : function()
25310         {
25311             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25312                 return;
25313             }
25314             
25315             this.maskEl.top.setStyle('position', 'absolute');
25316             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25317             this.maskEl.top.hide();
25318
25319             this.maskEl.left.setStyle('position', 'absolute');
25320             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25321             this.maskEl.left.hide();
25322
25323             this.maskEl.bottom.setStyle('position', 'absolute');
25324             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25325             this.maskEl.bottom.hide();
25326
25327             this.maskEl.right.setStyle('position', 'absolute');
25328             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25329             this.maskEl.right.hide();
25330             
25331             window.onwheel = function(){ return true;};
25332             
25333             if(this.intervalID){
25334                 window.clearInterval(this.intervalID);
25335                 this.intervalID = false;
25336             }
25337             
25338             this.isMasked = false;
25339             
25340         }
25341         
25342     }
25343     
25344 });/*
25345  * Based on:
25346  * Ext JS Library 1.1.1
25347  * Copyright(c) 2006-2007, Ext JS, LLC.
25348  *
25349  * Originally Released Under LGPL - original licence link has changed is not relivant.
25350  *
25351  * Fork - LGPL
25352  * <script type="text/javascript">
25353  */
25354
25355 /**
25356  * @class Roo.form.Form
25357  * @extends Roo.form.BasicForm
25358  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
25359  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25360  * @constructor
25361  * @param {Object} config Configuration options
25362  */
25363 Roo.form.Form = function(config){
25364     var xitems =  [];
25365     if (config.items) {
25366         xitems = config.items;
25367         delete config.items;
25368     }
25369    
25370     
25371     Roo.form.Form.superclass.constructor.call(this, null, config);
25372     this.url = this.url || this.action;
25373     if(!this.root){
25374         this.root = new Roo.form.Layout(Roo.applyIf({
25375             id: Roo.id()
25376         }, config));
25377     }
25378     this.active = this.root;
25379     /**
25380      * Array of all the buttons that have been added to this form via {@link addButton}
25381      * @type Array
25382      */
25383     this.buttons = [];
25384     this.allItems = [];
25385     this.addEvents({
25386         /**
25387          * @event clientvalidation
25388          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25389          * @param {Form} this
25390          * @param {Boolean} valid true if the form has passed client-side validation
25391          */
25392         clientvalidation: true,
25393         /**
25394          * @event rendered
25395          * Fires when the form is rendered
25396          * @param {Roo.form.Form} form
25397          */
25398         rendered : true
25399     });
25400     
25401     if (this.progressUrl) {
25402             // push a hidden field onto the list of fields..
25403             this.addxtype( {
25404                     xns: Roo.form, 
25405                     xtype : 'Hidden', 
25406                     name : 'UPLOAD_IDENTIFIER' 
25407             });
25408         }
25409         
25410     
25411     Roo.each(xitems, this.addxtype, this);
25412     
25413 };
25414
25415 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25416      /**
25417      * @cfg {Roo.Button} buttons[] buttons at bottom of form
25418      */
25419     
25420     /**
25421      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25422      */
25423     /**
25424      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25425      */
25426     /**
25427      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25428      */
25429     buttonAlign:'center',
25430
25431     /**
25432      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25433      */
25434     minButtonWidth:75,
25435
25436     /**
25437      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25438      * This property cascades to child containers if not set.
25439      */
25440     labelAlign:'left',
25441
25442     /**
25443      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25444      * fires a looping event with that state. This is required to bind buttons to the valid
25445      * state using the config value formBind:true on the button.
25446      */
25447     monitorValid : false,
25448
25449     /**
25450      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25451      */
25452     monitorPoll : 200,
25453     
25454     /**
25455      * @cfg {String} progressUrl - Url to return progress data 
25456      */
25457     
25458     progressUrl : false,
25459     /**
25460      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25461      * sending a formdata with extra parameters - eg uploaded elements.
25462      */
25463     
25464     formData : false,
25465     
25466     /**
25467      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25468      * fields are added and the column is closed. If no fields are passed the column remains open
25469      * until end() is called.
25470      * @param {Object} config The config to pass to the column
25471      * @param {Field} field1 (optional)
25472      * @param {Field} field2 (optional)
25473      * @param {Field} etc (optional)
25474      * @return Column The column container object
25475      */
25476     column : function(c){
25477         var col = new Roo.form.Column(c);
25478         this.start(col);
25479         if(arguments.length > 1){ // duplicate code required because of Opera
25480             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25481             this.end();
25482         }
25483         return col;
25484     },
25485
25486     /**
25487      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25488      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25489      * until end() is called.
25490      * @param {Object} config The config to pass to the fieldset
25491      * @param {Field} field1 (optional)
25492      * @param {Field} field2 (optional)
25493      * @param {Field} etc (optional)
25494      * @return FieldSet The fieldset container object
25495      */
25496     fieldset : function(c){
25497         var fs = new Roo.form.FieldSet(c);
25498         this.start(fs);
25499         if(arguments.length > 1){ // duplicate code required because of Opera
25500             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25501             this.end();
25502         }
25503         return fs;
25504     },
25505
25506     /**
25507      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25508      * fields are added and the container is closed. If no fields are passed the container remains open
25509      * until end() is called.
25510      * @param {Object} config The config to pass to the Layout
25511      * @param {Field} field1 (optional)
25512      * @param {Field} field2 (optional)
25513      * @param {Field} etc (optional)
25514      * @return Layout The container object
25515      */
25516     container : function(c){
25517         var l = new Roo.form.Layout(c);
25518         this.start(l);
25519         if(arguments.length > 1){ // duplicate code required because of Opera
25520             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25521             this.end();
25522         }
25523         return l;
25524     },
25525
25526     /**
25527      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25528      * @param {Object} container A Roo.form.Layout or subclass of Layout
25529      * @return {Form} this
25530      */
25531     start : function(c){
25532         // cascade label info
25533         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25534         this.active.stack.push(c);
25535         c.ownerCt = this.active;
25536         this.active = c;
25537         return this;
25538     },
25539
25540     /**
25541      * Closes the current open container
25542      * @return {Form} this
25543      */
25544     end : function(){
25545         if(this.active == this.root){
25546             return this;
25547         }
25548         this.active = this.active.ownerCt;
25549         return this;
25550     },
25551
25552     /**
25553      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25554      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25555      * as the label of the field.
25556      * @param {Field} field1
25557      * @param {Field} field2 (optional)
25558      * @param {Field} etc. (optional)
25559      * @return {Form} this
25560      */
25561     add : function(){
25562         this.active.stack.push.apply(this.active.stack, arguments);
25563         this.allItems.push.apply(this.allItems,arguments);
25564         var r = [];
25565         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25566             if(a[i].isFormField){
25567                 r.push(a[i]);
25568             }
25569         }
25570         if(r.length > 0){
25571             Roo.form.Form.superclass.add.apply(this, r);
25572         }
25573         return this;
25574     },
25575     
25576
25577     
25578     
25579     
25580      /**
25581      * Find any element that has been added to a form, using it's ID or name
25582      * This can include framesets, columns etc. along with regular fields..
25583      * @param {String} id - id or name to find.
25584      
25585      * @return {Element} e - or false if nothing found.
25586      */
25587     findbyId : function(id)
25588     {
25589         var ret = false;
25590         if (!id) {
25591             return ret;
25592         }
25593         Roo.each(this.allItems, function(f){
25594             if (f.id == id || f.name == id ){
25595                 ret = f;
25596                 return false;
25597             }
25598         });
25599         return ret;
25600     },
25601
25602     
25603     
25604     /**
25605      * Render this form into the passed container. This should only be called once!
25606      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25607      * @return {Form} this
25608      */
25609     render : function(ct)
25610     {
25611         
25612         
25613         
25614         ct = Roo.get(ct);
25615         var o = this.autoCreate || {
25616             tag: 'form',
25617             method : this.method || 'POST',
25618             id : this.id || Roo.id()
25619         };
25620         this.initEl(ct.createChild(o));
25621
25622         this.root.render(this.el);
25623         
25624        
25625              
25626         this.items.each(function(f){
25627             f.render('x-form-el-'+f.id);
25628         });
25629
25630         if(this.buttons.length > 0){
25631             // tables are required to maintain order and for correct IE layout
25632             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25633                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25634                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25635             }}, null, true);
25636             var tr = tb.getElementsByTagName('tr')[0];
25637             for(var i = 0, len = this.buttons.length; i < len; i++) {
25638                 var b = this.buttons[i];
25639                 var td = document.createElement('td');
25640                 td.className = 'x-form-btn-td';
25641                 b.render(tr.appendChild(td));
25642             }
25643         }
25644         if(this.monitorValid){ // initialize after render
25645             this.startMonitoring();
25646         }
25647         this.fireEvent('rendered', this);
25648         return this;
25649     },
25650
25651     /**
25652      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25653      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25654      * object or a valid Roo.DomHelper element config
25655      * @param {Function} handler The function called when the button is clicked
25656      * @param {Object} scope (optional) The scope of the handler function
25657      * @return {Roo.Button}
25658      */
25659     addButton : function(config, handler, scope){
25660         var bc = {
25661             handler: handler,
25662             scope: scope,
25663             minWidth: this.minButtonWidth,
25664             hideParent:true
25665         };
25666         if(typeof config == "string"){
25667             bc.text = config;
25668         }else{
25669             Roo.apply(bc, config);
25670         }
25671         var btn = new Roo.Button(null, bc);
25672         this.buttons.push(btn);
25673         return btn;
25674     },
25675
25676      /**
25677      * Adds a series of form elements (using the xtype property as the factory method.
25678      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25679      * @param {Object} config 
25680      */
25681     
25682     addxtype : function()
25683     {
25684         var ar = Array.prototype.slice.call(arguments, 0);
25685         var ret = false;
25686         for(var i = 0; i < ar.length; i++) {
25687             if (!ar[i]) {
25688                 continue; // skip -- if this happends something invalid got sent, we 
25689                 // should ignore it, as basically that interface element will not show up
25690                 // and that should be pretty obvious!!
25691             }
25692             
25693             if (Roo.form[ar[i].xtype]) {
25694                 ar[i].form = this;
25695                 var fe = Roo.factory(ar[i], Roo.form);
25696                 if (!ret) {
25697                     ret = fe;
25698                 }
25699                 fe.form = this;
25700                 if (fe.store) {
25701                     fe.store.form = this;
25702                 }
25703                 if (fe.isLayout) {  
25704                          
25705                     this.start(fe);
25706                     this.allItems.push(fe);
25707                     if (fe.items && fe.addxtype) {
25708                         fe.addxtype.apply(fe, fe.items);
25709                         delete fe.items;
25710                     }
25711                      this.end();
25712                     continue;
25713                 }
25714                 
25715                 
25716                  
25717                 this.add(fe);
25718               //  console.log('adding ' + ar[i].xtype);
25719             }
25720             if (ar[i].xtype == 'Button') {  
25721                 //console.log('adding button');
25722                 //console.log(ar[i]);
25723                 this.addButton(ar[i]);
25724                 this.allItems.push(fe);
25725                 continue;
25726             }
25727             
25728             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25729                 alert('end is not supported on xtype any more, use items');
25730             //    this.end();
25731             //    //console.log('adding end');
25732             }
25733             
25734         }
25735         return ret;
25736     },
25737     
25738     /**
25739      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25740      * option "monitorValid"
25741      */
25742     startMonitoring : function(){
25743         if(!this.bound){
25744             this.bound = true;
25745             Roo.TaskMgr.start({
25746                 run : this.bindHandler,
25747                 interval : this.monitorPoll || 200,
25748                 scope: this
25749             });
25750         }
25751     },
25752
25753     /**
25754      * Stops monitoring of the valid state of this form
25755      */
25756     stopMonitoring : function(){
25757         this.bound = false;
25758     },
25759
25760     // private
25761     bindHandler : function(){
25762         if(!this.bound){
25763             return false; // stops binding
25764         }
25765         var valid = true;
25766         this.items.each(function(f){
25767             if(!f.isValid(true)){
25768                 valid = false;
25769                 return false;
25770             }
25771         });
25772         for(var i = 0, len = this.buttons.length; i < len; i++){
25773             var btn = this.buttons[i];
25774             if(btn.formBind === true && btn.disabled === valid){
25775                 btn.setDisabled(!valid);
25776             }
25777         }
25778         this.fireEvent('clientvalidation', this, valid);
25779     }
25780     
25781     
25782     
25783     
25784     
25785     
25786     
25787     
25788 });
25789
25790
25791 // back compat
25792 Roo.Form = Roo.form.Form;
25793 /*
25794  * Based on:
25795  * Ext JS Library 1.1.1
25796  * Copyright(c) 2006-2007, Ext JS, LLC.
25797  *
25798  * Originally Released Under LGPL - original licence link has changed is not relivant.
25799  *
25800  * Fork - LGPL
25801  * <script type="text/javascript">
25802  */
25803
25804 // as we use this in bootstrap.
25805 Roo.namespace('Roo.form');
25806  /**
25807  * @class Roo.form.Action
25808  * Internal Class used to handle form actions
25809  * @constructor
25810  * @param {Roo.form.BasicForm} el The form element or its id
25811  * @param {Object} config Configuration options
25812  */
25813
25814  
25815  
25816 // define the action interface
25817 Roo.form.Action = function(form, options){
25818     this.form = form;
25819     this.options = options || {};
25820 };
25821 /**
25822  * Client Validation Failed
25823  * @const 
25824  */
25825 Roo.form.Action.CLIENT_INVALID = 'client';
25826 /**
25827  * Server Validation Failed
25828  * @const 
25829  */
25830 Roo.form.Action.SERVER_INVALID = 'server';
25831  /**
25832  * Connect to Server Failed
25833  * @const 
25834  */
25835 Roo.form.Action.CONNECT_FAILURE = 'connect';
25836 /**
25837  * Reading Data from Server Failed
25838  * @const 
25839  */
25840 Roo.form.Action.LOAD_FAILURE = 'load';
25841
25842 Roo.form.Action.prototype = {
25843     type : 'default',
25844     failureType : undefined,
25845     response : undefined,
25846     result : undefined,
25847
25848     // interface method
25849     run : function(options){
25850
25851     },
25852
25853     // interface method
25854     success : function(response){
25855
25856     },
25857
25858     // interface method
25859     handleResponse : function(response){
25860
25861     },
25862
25863     // default connection failure
25864     failure : function(response){
25865         
25866         this.response = response;
25867         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25868         this.form.afterAction(this, false);
25869     },
25870
25871     processResponse : function(response){
25872         this.response = response;
25873         if(!response.responseText){
25874             return true;
25875         }
25876         this.result = this.handleResponse(response);
25877         return this.result;
25878     },
25879
25880     // utility functions used internally
25881     getUrl : function(appendParams){
25882         var url = this.options.url || this.form.url || this.form.el.dom.action;
25883         if(appendParams){
25884             var p = this.getParams();
25885             if(p){
25886                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25887             }
25888         }
25889         return url;
25890     },
25891
25892     getMethod : function(){
25893         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25894     },
25895
25896     getParams : function(){
25897         var bp = this.form.baseParams;
25898         var p = this.options.params;
25899         if(p){
25900             if(typeof p == "object"){
25901                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25902             }else if(typeof p == 'string' && bp){
25903                 p += '&' + Roo.urlEncode(bp);
25904             }
25905         }else if(bp){
25906             p = Roo.urlEncode(bp);
25907         }
25908         return p;
25909     },
25910
25911     createCallback : function(){
25912         return {
25913             success: this.success,
25914             failure: this.failure,
25915             scope: this,
25916             timeout: (this.form.timeout*1000),
25917             upload: this.form.fileUpload ? this.success : undefined
25918         };
25919     }
25920 };
25921
25922 Roo.form.Action.Submit = function(form, options){
25923     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25924 };
25925
25926 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25927     type : 'submit',
25928
25929     haveProgress : false,
25930     uploadComplete : false,
25931     
25932     // uploadProgress indicator.
25933     uploadProgress : function()
25934     {
25935         if (!this.form.progressUrl) {
25936             return;
25937         }
25938         
25939         if (!this.haveProgress) {
25940             Roo.MessageBox.progress("Uploading", "Uploading");
25941         }
25942         if (this.uploadComplete) {
25943            Roo.MessageBox.hide();
25944            return;
25945         }
25946         
25947         this.haveProgress = true;
25948    
25949         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25950         
25951         var c = new Roo.data.Connection();
25952         c.request({
25953             url : this.form.progressUrl,
25954             params: {
25955                 id : uid
25956             },
25957             method: 'GET',
25958             success : function(req){
25959                //console.log(data);
25960                 var rdata = false;
25961                 var edata;
25962                 try  {
25963                    rdata = Roo.decode(req.responseText)
25964                 } catch (e) {
25965                     Roo.log("Invalid data from server..");
25966                     Roo.log(edata);
25967                     return;
25968                 }
25969                 if (!rdata || !rdata.success) {
25970                     Roo.log(rdata);
25971                     Roo.MessageBox.alert(Roo.encode(rdata));
25972                     return;
25973                 }
25974                 var data = rdata.data;
25975                 
25976                 if (this.uploadComplete) {
25977                    Roo.MessageBox.hide();
25978                    return;
25979                 }
25980                    
25981                 if (data){
25982                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25983                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25984                     );
25985                 }
25986                 this.uploadProgress.defer(2000,this);
25987             },
25988        
25989             failure: function(data) {
25990                 Roo.log('progress url failed ');
25991                 Roo.log(data);
25992             },
25993             scope : this
25994         });
25995            
25996     },
25997     
25998     
25999     run : function()
26000     {
26001         // run get Values on the form, so it syncs any secondary forms.
26002         this.form.getValues();
26003         
26004         var o = this.options;
26005         var method = this.getMethod();
26006         var isPost = method == 'POST';
26007         if(o.clientValidation === false || this.form.isValid()){
26008             
26009             if (this.form.progressUrl) {
26010                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26011                     (new Date() * 1) + '' + Math.random());
26012                     
26013             } 
26014             
26015             
26016             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26017                 form:this.form.el.dom,
26018                 url:this.getUrl(!isPost),
26019                 method: method,
26020                 params:isPost ? this.getParams() : null,
26021                 isUpload: this.form.fileUpload,
26022                 formData : this.form.formData
26023             }));
26024             
26025             this.uploadProgress();
26026
26027         }else if (o.clientValidation !== false){ // client validation failed
26028             this.failureType = Roo.form.Action.CLIENT_INVALID;
26029             this.form.afterAction(this, false);
26030         }
26031     },
26032
26033     success : function(response)
26034     {
26035         this.uploadComplete= true;
26036         if (this.haveProgress) {
26037             Roo.MessageBox.hide();
26038         }
26039         
26040         
26041         var result = this.processResponse(response);
26042         if(result === true || result.success){
26043             this.form.afterAction(this, true);
26044             return;
26045         }
26046         if(result.errors){
26047             this.form.markInvalid(result.errors);
26048             this.failureType = Roo.form.Action.SERVER_INVALID;
26049         }
26050         this.form.afterAction(this, false);
26051     },
26052     failure : function(response)
26053     {
26054         this.uploadComplete= true;
26055         if (this.haveProgress) {
26056             Roo.MessageBox.hide();
26057         }
26058         
26059         this.response = response;
26060         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26061         this.form.afterAction(this, false);
26062     },
26063     
26064     handleResponse : function(response){
26065         if(this.form.errorReader){
26066             var rs = this.form.errorReader.read(response);
26067             var errors = [];
26068             if(rs.records){
26069                 for(var i = 0, len = rs.records.length; i < len; i++) {
26070                     var r = rs.records[i];
26071                     errors[i] = r.data;
26072                 }
26073             }
26074             if(errors.length < 1){
26075                 errors = null;
26076             }
26077             return {
26078                 success : rs.success,
26079                 errors : errors
26080             };
26081         }
26082         var ret = false;
26083         try {
26084             ret = Roo.decode(response.responseText);
26085         } catch (e) {
26086             ret = {
26087                 success: false,
26088                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26089                 errors : []
26090             };
26091         }
26092         return ret;
26093         
26094     }
26095 });
26096
26097
26098 Roo.form.Action.Load = function(form, options){
26099     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26100     this.reader = this.form.reader;
26101 };
26102
26103 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26104     type : 'load',
26105
26106     run : function(){
26107         
26108         Roo.Ajax.request(Roo.apply(
26109                 this.createCallback(), {
26110                     method:this.getMethod(),
26111                     url:this.getUrl(false),
26112                     params:this.getParams()
26113         }));
26114     },
26115
26116     success : function(response){
26117         
26118         var result = this.processResponse(response);
26119         if(result === true || !result.success || !result.data){
26120             this.failureType = Roo.form.Action.LOAD_FAILURE;
26121             this.form.afterAction(this, false);
26122             return;
26123         }
26124         this.form.clearInvalid();
26125         this.form.setValues(result.data);
26126         this.form.afterAction(this, true);
26127     },
26128
26129     handleResponse : function(response){
26130         if(this.form.reader){
26131             var rs = this.form.reader.read(response);
26132             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26133             return {
26134                 success : rs.success,
26135                 data : data
26136             };
26137         }
26138         return Roo.decode(response.responseText);
26139     }
26140 });
26141
26142 Roo.form.Action.ACTION_TYPES = {
26143     'load' : Roo.form.Action.Load,
26144     'submit' : Roo.form.Action.Submit
26145 };/*
26146  * Based on:
26147  * Ext JS Library 1.1.1
26148  * Copyright(c) 2006-2007, Ext JS, LLC.
26149  *
26150  * Originally Released Under LGPL - original licence link has changed is not relivant.
26151  *
26152  * Fork - LGPL
26153  * <script type="text/javascript">
26154  */
26155  
26156 /**
26157  * @class Roo.form.Layout
26158  * @extends Roo.Component
26159  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26160  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26161  * @constructor
26162  * @param {Object} config Configuration options
26163  */
26164 Roo.form.Layout = function(config){
26165     var xitems = [];
26166     if (config.items) {
26167         xitems = config.items;
26168         delete config.items;
26169     }
26170     Roo.form.Layout.superclass.constructor.call(this, config);
26171     this.stack = [];
26172     Roo.each(xitems, this.addxtype, this);
26173      
26174 };
26175
26176 Roo.extend(Roo.form.Layout, Roo.Component, {
26177     /**
26178      * @cfg {String/Object} autoCreate
26179      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26180      */
26181     /**
26182      * @cfg {String/Object/Function} style
26183      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26184      * a function which returns such a specification.
26185      */
26186     /**
26187      * @cfg {String} labelAlign
26188      * Valid values are "left," "top" and "right" (defaults to "left")
26189      */
26190     /**
26191      * @cfg {Number} labelWidth
26192      * Fixed width in pixels of all field labels (defaults to undefined)
26193      */
26194     /**
26195      * @cfg {Boolean} clear
26196      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26197      */
26198     clear : true,
26199     /**
26200      * @cfg {String} labelSeparator
26201      * The separator to use after field labels (defaults to ':')
26202      */
26203     labelSeparator : ':',
26204     /**
26205      * @cfg {Boolean} hideLabels
26206      * True to suppress the display of field labels in this layout (defaults to false)
26207      */
26208     hideLabels : false,
26209
26210     // private
26211     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26212     
26213     isLayout : true,
26214     
26215     // private
26216     onRender : function(ct, position){
26217         if(this.el){ // from markup
26218             this.el = Roo.get(this.el);
26219         }else {  // generate
26220             var cfg = this.getAutoCreate();
26221             this.el = ct.createChild(cfg, position);
26222         }
26223         if(this.style){
26224             this.el.applyStyles(this.style);
26225         }
26226         if(this.labelAlign){
26227             this.el.addClass('x-form-label-'+this.labelAlign);
26228         }
26229         if(this.hideLabels){
26230             this.labelStyle = "display:none";
26231             this.elementStyle = "padding-left:0;";
26232         }else{
26233             if(typeof this.labelWidth == 'number'){
26234                 this.labelStyle = "width:"+this.labelWidth+"px;";
26235                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26236             }
26237             if(this.labelAlign == 'top'){
26238                 this.labelStyle = "width:auto;";
26239                 this.elementStyle = "padding-left:0;";
26240             }
26241         }
26242         var stack = this.stack;
26243         var slen = stack.length;
26244         if(slen > 0){
26245             if(!this.fieldTpl){
26246                 var t = new Roo.Template(
26247                     '<div class="x-form-item {5}">',
26248                         '<label for="{0}" style="{2}">{1}{4}</label>',
26249                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26250                         '</div>',
26251                     '</div><div class="x-form-clear-left"></div>'
26252                 );
26253                 t.disableFormats = true;
26254                 t.compile();
26255                 Roo.form.Layout.prototype.fieldTpl = t;
26256             }
26257             for(var i = 0; i < slen; i++) {
26258                 if(stack[i].isFormField){
26259                     this.renderField(stack[i]);
26260                 }else{
26261                     this.renderComponent(stack[i]);
26262                 }
26263             }
26264         }
26265         if(this.clear){
26266             this.el.createChild({cls:'x-form-clear'});
26267         }
26268     },
26269
26270     // private
26271     renderField : function(f){
26272         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26273                f.id, //0
26274                f.fieldLabel, //1
26275                f.labelStyle||this.labelStyle||'', //2
26276                this.elementStyle||'', //3
26277                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26278                f.itemCls||this.itemCls||''  //5
26279        ], true).getPrevSibling());
26280     },
26281
26282     // private
26283     renderComponent : function(c){
26284         c.render(c.isLayout ? this.el : this.el.createChild());    
26285     },
26286     /**
26287      * Adds a object form elements (using the xtype property as the factory method.)
26288      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26289      * @param {Object} config 
26290      */
26291     addxtype : function(o)
26292     {
26293         // create the lement.
26294         o.form = this.form;
26295         var fe = Roo.factory(o, Roo.form);
26296         this.form.allItems.push(fe);
26297         this.stack.push(fe);
26298         
26299         if (fe.isFormField) {
26300             this.form.items.add(fe);
26301         }
26302          
26303         return fe;
26304     }
26305 });
26306
26307 /**
26308  * @class Roo.form.Column
26309  * @extends Roo.form.Layout
26310  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26311  * @constructor
26312  * @param {Object} config Configuration options
26313  */
26314 Roo.form.Column = function(config){
26315     Roo.form.Column.superclass.constructor.call(this, config);
26316 };
26317
26318 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26319     /**
26320      * @cfg {Number/String} width
26321      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26322      */
26323     /**
26324      * @cfg {String/Object} autoCreate
26325      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26326      */
26327
26328     // private
26329     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26330
26331     // private
26332     onRender : function(ct, position){
26333         Roo.form.Column.superclass.onRender.call(this, ct, position);
26334         if(this.width){
26335             this.el.setWidth(this.width);
26336         }
26337     }
26338 });
26339
26340
26341 /**
26342  * @class Roo.form.Row
26343  * @extends Roo.form.Layout
26344  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26345  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26346  * @constructor
26347  * @param {Object} config Configuration options
26348  */
26349
26350  
26351 Roo.form.Row = function(config){
26352     Roo.form.Row.superclass.constructor.call(this, config);
26353 };
26354  
26355 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26356       /**
26357      * @cfg {Number/String} width
26358      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26359      */
26360     /**
26361      * @cfg {Number/String} height
26362      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26363      */
26364     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26365     
26366     padWidth : 20,
26367     // private
26368     onRender : function(ct, position){
26369         //console.log('row render');
26370         if(!this.rowTpl){
26371             var t = new Roo.Template(
26372                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26373                     '<label for="{0}" style="{2}">{1}{4}</label>',
26374                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26375                     '</div>',
26376                 '</div>'
26377             );
26378             t.disableFormats = true;
26379             t.compile();
26380             Roo.form.Layout.prototype.rowTpl = t;
26381         }
26382         this.fieldTpl = this.rowTpl;
26383         
26384         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26385         var labelWidth = 100;
26386         
26387         if ((this.labelAlign != 'top')) {
26388             if (typeof this.labelWidth == 'number') {
26389                 labelWidth = this.labelWidth
26390             }
26391             this.padWidth =  20 + labelWidth;
26392             
26393         }
26394         
26395         Roo.form.Column.superclass.onRender.call(this, ct, position);
26396         if(this.width){
26397             this.el.setWidth(this.width);
26398         }
26399         if(this.height){
26400             this.el.setHeight(this.height);
26401         }
26402     },
26403     
26404     // private
26405     renderField : function(f){
26406         f.fieldEl = this.fieldTpl.append(this.el, [
26407                f.id, f.fieldLabel,
26408                f.labelStyle||this.labelStyle||'',
26409                this.elementStyle||'',
26410                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26411                f.itemCls||this.itemCls||'',
26412                f.width ? f.width + this.padWidth : 160 + this.padWidth
26413        ],true);
26414     }
26415 });
26416  
26417
26418 /**
26419  * @class Roo.form.FieldSet
26420  * @extends Roo.form.Layout
26421  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26422  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26423  * @constructor
26424  * @param {Object} config Configuration options
26425  */
26426 Roo.form.FieldSet = function(config){
26427     Roo.form.FieldSet.superclass.constructor.call(this, config);
26428 };
26429
26430 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26431     /**
26432      * @cfg {String} legend
26433      * The text to display as the legend for the FieldSet (defaults to '')
26434      */
26435     /**
26436      * @cfg {String/Object} autoCreate
26437      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26438      */
26439
26440     // private
26441     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26442
26443     // private
26444     onRender : function(ct, position){
26445         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26446         if(this.legend){
26447             this.setLegend(this.legend);
26448         }
26449     },
26450
26451     // private
26452     setLegend : function(text){
26453         if(this.rendered){
26454             this.el.child('legend').update(text);
26455         }
26456     }
26457 });/*
26458  * Based on:
26459  * Ext JS Library 1.1.1
26460  * Copyright(c) 2006-2007, Ext JS, LLC.
26461  *
26462  * Originally Released Under LGPL - original licence link has changed is not relivant.
26463  *
26464  * Fork - LGPL
26465  * <script type="text/javascript">
26466  */
26467 /**
26468  * @class Roo.form.VTypes
26469  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26470  * @singleton
26471  */
26472 Roo.form.VTypes = function(){
26473     // closure these in so they are only created once.
26474     var alpha = /^[a-zA-Z_]+$/;
26475     var alphanum = /^[a-zA-Z0-9_]+$/;
26476     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26477     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26478
26479     // All these messages and functions are configurable
26480     return {
26481         /**
26482          * The function used to validate email addresses
26483          * @param {String} value The email address
26484          */
26485         'email' : function(v){
26486             return email.test(v);
26487         },
26488         /**
26489          * The error text to display when the email validation function returns false
26490          * @type String
26491          */
26492         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26493         /**
26494          * The keystroke filter mask to be applied on email input
26495          * @type RegExp
26496          */
26497         'emailMask' : /[a-z0-9_\.\-@]/i,
26498
26499         /**
26500          * The function used to validate URLs
26501          * @param {String} value The URL
26502          */
26503         'url' : function(v){
26504             return url.test(v);
26505         },
26506         /**
26507          * The error text to display when the url validation function returns false
26508          * @type String
26509          */
26510         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26511         
26512         /**
26513          * The function used to validate alpha values
26514          * @param {String} value The value
26515          */
26516         'alpha' : function(v){
26517             return alpha.test(v);
26518         },
26519         /**
26520          * The error text to display when the alpha validation function returns false
26521          * @type String
26522          */
26523         'alphaText' : 'This field should only contain letters and _',
26524         /**
26525          * The keystroke filter mask to be applied on alpha input
26526          * @type RegExp
26527          */
26528         'alphaMask' : /[a-z_]/i,
26529
26530         /**
26531          * The function used to validate alphanumeric values
26532          * @param {String} value The value
26533          */
26534         'alphanum' : function(v){
26535             return alphanum.test(v);
26536         },
26537         /**
26538          * The error text to display when the alphanumeric validation function returns false
26539          * @type String
26540          */
26541         'alphanumText' : 'This field should only contain letters, numbers and _',
26542         /**
26543          * The keystroke filter mask to be applied on alphanumeric input
26544          * @type RegExp
26545          */
26546         'alphanumMask' : /[a-z0-9_]/i
26547     };
26548 }();//<script type="text/javascript">
26549
26550 /**
26551  * @class Roo.form.FCKeditor
26552  * @extends Roo.form.TextArea
26553  * Wrapper around the FCKEditor http://www.fckeditor.net
26554  * @constructor
26555  * Creates a new FCKeditor
26556  * @param {Object} config Configuration options
26557  */
26558 Roo.form.FCKeditor = function(config){
26559     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26560     this.addEvents({
26561          /**
26562          * @event editorinit
26563          * Fired when the editor is initialized - you can add extra handlers here..
26564          * @param {FCKeditor} this
26565          * @param {Object} the FCK object.
26566          */
26567         editorinit : true
26568     });
26569     
26570     
26571 };
26572 Roo.form.FCKeditor.editors = { };
26573 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26574 {
26575     //defaultAutoCreate : {
26576     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26577     //},
26578     // private
26579     /**
26580      * @cfg {Object} fck options - see fck manual for details.
26581      */
26582     fckconfig : false,
26583     
26584     /**
26585      * @cfg {Object} fck toolbar set (Basic or Default)
26586      */
26587     toolbarSet : 'Basic',
26588     /**
26589      * @cfg {Object} fck BasePath
26590      */ 
26591     basePath : '/fckeditor/',
26592     
26593     
26594     frame : false,
26595     
26596     value : '',
26597     
26598    
26599     onRender : function(ct, position)
26600     {
26601         if(!this.el){
26602             this.defaultAutoCreate = {
26603                 tag: "textarea",
26604                 style:"width:300px;height:60px;",
26605                 autocomplete: "new-password"
26606             };
26607         }
26608         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26609         /*
26610         if(this.grow){
26611             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26612             if(this.preventScrollbars){
26613                 this.el.setStyle("overflow", "hidden");
26614             }
26615             this.el.setHeight(this.growMin);
26616         }
26617         */
26618         //console.log('onrender' + this.getId() );
26619         Roo.form.FCKeditor.editors[this.getId()] = this;
26620          
26621
26622         this.replaceTextarea() ;
26623         
26624     },
26625     
26626     getEditor : function() {
26627         return this.fckEditor;
26628     },
26629     /**
26630      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26631      * @param {Mixed} value The value to set
26632      */
26633     
26634     
26635     setValue : function(value)
26636     {
26637         //console.log('setValue: ' + value);
26638         
26639         if(typeof(value) == 'undefined') { // not sure why this is happending...
26640             return;
26641         }
26642         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26643         
26644         //if(!this.el || !this.getEditor()) {
26645         //    this.value = value;
26646             //this.setValue.defer(100,this,[value]);    
26647         //    return;
26648         //} 
26649         
26650         if(!this.getEditor()) {
26651             return;
26652         }
26653         
26654         this.getEditor().SetData(value);
26655         
26656         //
26657
26658     },
26659
26660     /**
26661      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26662      * @return {Mixed} value The field value
26663      */
26664     getValue : function()
26665     {
26666         
26667         if (this.frame && this.frame.dom.style.display == 'none') {
26668             return Roo.form.FCKeditor.superclass.getValue.call(this);
26669         }
26670         
26671         if(!this.el || !this.getEditor()) {
26672            
26673            // this.getValue.defer(100,this); 
26674             return this.value;
26675         }
26676        
26677         
26678         var value=this.getEditor().GetData();
26679         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26680         return Roo.form.FCKeditor.superclass.getValue.call(this);
26681         
26682
26683     },
26684
26685     /**
26686      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26687      * @return {Mixed} value The field value
26688      */
26689     getRawValue : function()
26690     {
26691         if (this.frame && this.frame.dom.style.display == 'none') {
26692             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26693         }
26694         
26695         if(!this.el || !this.getEditor()) {
26696             //this.getRawValue.defer(100,this); 
26697             return this.value;
26698             return;
26699         }
26700         
26701         
26702         
26703         var value=this.getEditor().GetData();
26704         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26705         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26706          
26707     },
26708     
26709     setSize : function(w,h) {
26710         
26711         
26712         
26713         //if (this.frame && this.frame.dom.style.display == 'none') {
26714         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26715         //    return;
26716         //}
26717         //if(!this.el || !this.getEditor()) {
26718         //    this.setSize.defer(100,this, [w,h]); 
26719         //    return;
26720         //}
26721         
26722         
26723         
26724         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26725         
26726         this.frame.dom.setAttribute('width', w);
26727         this.frame.dom.setAttribute('height', h);
26728         this.frame.setSize(w,h);
26729         
26730     },
26731     
26732     toggleSourceEdit : function(value) {
26733         
26734       
26735          
26736         this.el.dom.style.display = value ? '' : 'none';
26737         this.frame.dom.style.display = value ?  'none' : '';
26738         
26739     },
26740     
26741     
26742     focus: function(tag)
26743     {
26744         if (this.frame.dom.style.display == 'none') {
26745             return Roo.form.FCKeditor.superclass.focus.call(this);
26746         }
26747         if(!this.el || !this.getEditor()) {
26748             this.focus.defer(100,this, [tag]); 
26749             return;
26750         }
26751         
26752         
26753         
26754         
26755         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26756         this.getEditor().Focus();
26757         if (tgs.length) {
26758             if (!this.getEditor().Selection.GetSelection()) {
26759                 this.focus.defer(100,this, [tag]); 
26760                 return;
26761             }
26762             
26763             
26764             var r = this.getEditor().EditorDocument.createRange();
26765             r.setStart(tgs[0],0);
26766             r.setEnd(tgs[0],0);
26767             this.getEditor().Selection.GetSelection().removeAllRanges();
26768             this.getEditor().Selection.GetSelection().addRange(r);
26769             this.getEditor().Focus();
26770         }
26771         
26772     },
26773     
26774     
26775     
26776     replaceTextarea : function()
26777     {
26778         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26779             return ;
26780         }
26781         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26782         //{
26783             // We must check the elements firstly using the Id and then the name.
26784         var oTextarea = document.getElementById( this.getId() );
26785         
26786         var colElementsByName = document.getElementsByName( this.getId() ) ;
26787          
26788         oTextarea.style.display = 'none' ;
26789
26790         if ( oTextarea.tabIndex ) {            
26791             this.TabIndex = oTextarea.tabIndex ;
26792         }
26793         
26794         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26795         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26796         this.frame = Roo.get(this.getId() + '___Frame')
26797     },
26798     
26799     _getConfigHtml : function()
26800     {
26801         var sConfig = '' ;
26802
26803         for ( var o in this.fckconfig ) {
26804             sConfig += sConfig.length > 0  ? '&amp;' : '';
26805             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26806         }
26807
26808         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26809     },
26810     
26811     
26812     _getIFrameHtml : function()
26813     {
26814         var sFile = 'fckeditor.html' ;
26815         /* no idea what this is about..
26816         try
26817         {
26818             if ( (/fcksource=true/i).test( window.top.location.search ) )
26819                 sFile = 'fckeditor.original.html' ;
26820         }
26821         catch (e) { 
26822         */
26823
26824         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26825         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26826         
26827         
26828         var html = '<iframe id="' + this.getId() +
26829             '___Frame" src="' + sLink +
26830             '" width="' + this.width +
26831             '" height="' + this.height + '"' +
26832             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26833             ' frameborder="0" scrolling="no"></iframe>' ;
26834
26835         return html ;
26836     },
26837     
26838     _insertHtmlBefore : function( html, element )
26839     {
26840         if ( element.insertAdjacentHTML )       {
26841             // IE
26842             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26843         } else { // Gecko
26844             var oRange = document.createRange() ;
26845             oRange.setStartBefore( element ) ;
26846             var oFragment = oRange.createContextualFragment( html );
26847             element.parentNode.insertBefore( oFragment, element ) ;
26848         }
26849     }
26850     
26851     
26852   
26853     
26854     
26855     
26856     
26857
26858 });
26859
26860 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26861
26862 function FCKeditor_OnComplete(editorInstance){
26863     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26864     f.fckEditor = editorInstance;
26865     //console.log("loaded");
26866     f.fireEvent('editorinit', f, editorInstance);
26867
26868   
26869
26870  
26871
26872
26873
26874
26875
26876
26877
26878
26879
26880
26881
26882
26883
26884
26885
26886 //<script type="text/javascript">
26887 /**
26888  * @class Roo.form.GridField
26889  * @extends Roo.form.Field
26890  * Embed a grid (or editable grid into a form)
26891  * STATUS ALPHA
26892  * 
26893  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26894  * it needs 
26895  * xgrid.store = Roo.data.Store
26896  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26897  * xgrid.store.reader = Roo.data.JsonReader 
26898  * 
26899  * 
26900  * @constructor
26901  * Creates a new GridField
26902  * @param {Object} config Configuration options
26903  */
26904 Roo.form.GridField = function(config){
26905     Roo.form.GridField.superclass.constructor.call(this, config);
26906      
26907 };
26908
26909 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26910     /**
26911      * @cfg {Number} width  - used to restrict width of grid..
26912      */
26913     width : 100,
26914     /**
26915      * @cfg {Number} height - used to restrict height of grid..
26916      */
26917     height : 50,
26918      /**
26919      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26920          * 
26921          *}
26922      */
26923     xgrid : false, 
26924     /**
26925      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26926      * {tag: "input", type: "checkbox", autocomplete: "off"})
26927      */
26928    // defaultAutoCreate : { tag: 'div' },
26929     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26930     /**
26931      * @cfg {String} addTitle Text to include for adding a title.
26932      */
26933     addTitle : false,
26934     //
26935     onResize : function(){
26936         Roo.form.Field.superclass.onResize.apply(this, arguments);
26937     },
26938
26939     initEvents : function(){
26940         // Roo.form.Checkbox.superclass.initEvents.call(this);
26941         // has no events...
26942        
26943     },
26944
26945
26946     getResizeEl : function(){
26947         return this.wrap;
26948     },
26949
26950     getPositionEl : function(){
26951         return this.wrap;
26952     },
26953
26954     // private
26955     onRender : function(ct, position){
26956         
26957         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26958         var style = this.style;
26959         delete this.style;
26960         
26961         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26962         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26963         this.viewEl = this.wrap.createChild({ tag: 'div' });
26964         if (style) {
26965             this.viewEl.applyStyles(style);
26966         }
26967         if (this.width) {
26968             this.viewEl.setWidth(this.width);
26969         }
26970         if (this.height) {
26971             this.viewEl.setHeight(this.height);
26972         }
26973         //if(this.inputValue !== undefined){
26974         //this.setValue(this.value);
26975         
26976         
26977         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26978         
26979         
26980         this.grid.render();
26981         this.grid.getDataSource().on('remove', this.refreshValue, this);
26982         this.grid.getDataSource().on('update', this.refreshValue, this);
26983         this.grid.on('afteredit', this.refreshValue, this);
26984  
26985     },
26986      
26987     
26988     /**
26989      * Sets the value of the item. 
26990      * @param {String} either an object  or a string..
26991      */
26992     setValue : function(v){
26993         //this.value = v;
26994         v = v || []; // empty set..
26995         // this does not seem smart - it really only affects memoryproxy grids..
26996         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26997             var ds = this.grid.getDataSource();
26998             // assumes a json reader..
26999             var data = {}
27000             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27001             ds.loadData( data);
27002         }
27003         // clear selection so it does not get stale.
27004         if (this.grid.sm) { 
27005             this.grid.sm.clearSelections();
27006         }
27007         
27008         Roo.form.GridField.superclass.setValue.call(this, v);
27009         this.refreshValue();
27010         // should load data in the grid really....
27011     },
27012     
27013     // private
27014     refreshValue: function() {
27015          var val = [];
27016         this.grid.getDataSource().each(function(r) {
27017             val.push(r.data);
27018         });
27019         this.el.dom.value = Roo.encode(val);
27020     }
27021     
27022      
27023     
27024     
27025 });/*
27026  * Based on:
27027  * Ext JS Library 1.1.1
27028  * Copyright(c) 2006-2007, Ext JS, LLC.
27029  *
27030  * Originally Released Under LGPL - original licence link has changed is not relivant.
27031  *
27032  * Fork - LGPL
27033  * <script type="text/javascript">
27034  */
27035 /**
27036  * @class Roo.form.DisplayField
27037  * @extends Roo.form.Field
27038  * A generic Field to display non-editable data.
27039  * @cfg {Boolean} closable (true|false) default false
27040  * @constructor
27041  * Creates a new Display Field item.
27042  * @param {Object} config Configuration options
27043  */
27044 Roo.form.DisplayField = function(config){
27045     Roo.form.DisplayField.superclass.constructor.call(this, config);
27046     
27047     this.addEvents({
27048         /**
27049          * @event close
27050          * Fires after the click the close btn
27051              * @param {Roo.form.DisplayField} this
27052              */
27053         close : true
27054     });
27055 };
27056
27057 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27058     inputType:      'hidden',
27059     allowBlank:     true,
27060     readOnly:         true,
27061     
27062  
27063     /**
27064      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27065      */
27066     focusClass : undefined,
27067     /**
27068      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27069      */
27070     fieldClass: 'x-form-field',
27071     
27072      /**
27073      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27074      */
27075     valueRenderer: undefined,
27076     
27077     width: 100,
27078     /**
27079      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27080      * {tag: "input", type: "checkbox", autocomplete: "off"})
27081      */
27082      
27083  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27084  
27085     closable : false,
27086     
27087     onResize : function(){
27088         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27089         
27090     },
27091
27092     initEvents : function(){
27093         // Roo.form.Checkbox.superclass.initEvents.call(this);
27094         // has no events...
27095         
27096         if(this.closable){
27097             this.closeEl.on('click', this.onClose, this);
27098         }
27099        
27100     },
27101
27102
27103     getResizeEl : function(){
27104         return this.wrap;
27105     },
27106
27107     getPositionEl : function(){
27108         return this.wrap;
27109     },
27110
27111     // private
27112     onRender : function(ct, position){
27113         
27114         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27115         //if(this.inputValue !== undefined){
27116         this.wrap = this.el.wrap();
27117         
27118         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27119         
27120         if(this.closable){
27121             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27122         }
27123         
27124         if (this.bodyStyle) {
27125             this.viewEl.applyStyles(this.bodyStyle);
27126         }
27127         //this.viewEl.setStyle('padding', '2px');
27128         
27129         this.setValue(this.value);
27130         
27131     },
27132 /*
27133     // private
27134     initValue : Roo.emptyFn,
27135
27136   */
27137
27138         // private
27139     onClick : function(){
27140         
27141     },
27142
27143     /**
27144      * Sets the checked state of the checkbox.
27145      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27146      */
27147     setValue : function(v){
27148         this.value = v;
27149         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27150         // this might be called before we have a dom element..
27151         if (!this.viewEl) {
27152             return;
27153         }
27154         this.viewEl.dom.innerHTML = html;
27155         Roo.form.DisplayField.superclass.setValue.call(this, v);
27156
27157     },
27158     
27159     onClose : function(e)
27160     {
27161         e.preventDefault();
27162         
27163         this.fireEvent('close', this);
27164     }
27165 });/*
27166  * 
27167  * Licence- LGPL
27168  * 
27169  */
27170
27171 /**
27172  * @class Roo.form.DayPicker
27173  * @extends Roo.form.Field
27174  * A Day picker show [M] [T] [W] ....
27175  * @constructor
27176  * Creates a new Day Picker
27177  * @param {Object} config Configuration options
27178  */
27179 Roo.form.DayPicker= function(config){
27180     Roo.form.DayPicker.superclass.constructor.call(this, config);
27181      
27182 };
27183
27184 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27185     /**
27186      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27187      */
27188     focusClass : undefined,
27189     /**
27190      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27191      */
27192     fieldClass: "x-form-field",
27193    
27194     /**
27195      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27196      * {tag: "input", type: "checkbox", autocomplete: "off"})
27197      */
27198     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27199     
27200    
27201     actionMode : 'viewEl', 
27202     //
27203     // private
27204  
27205     inputType : 'hidden',
27206     
27207      
27208     inputElement: false, // real input element?
27209     basedOn: false, // ????
27210     
27211     isFormField: true, // not sure where this is needed!!!!
27212
27213     onResize : function(){
27214         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27215         if(!this.boxLabel){
27216             this.el.alignTo(this.wrap, 'c-c');
27217         }
27218     },
27219
27220     initEvents : function(){
27221         Roo.form.Checkbox.superclass.initEvents.call(this);
27222         this.el.on("click", this.onClick,  this);
27223         this.el.on("change", this.onClick,  this);
27224     },
27225
27226
27227     getResizeEl : function(){
27228         return this.wrap;
27229     },
27230
27231     getPositionEl : function(){
27232         return this.wrap;
27233     },
27234
27235     
27236     // private
27237     onRender : function(ct, position){
27238         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27239        
27240         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27241         
27242         var r1 = '<table><tr>';
27243         var r2 = '<tr class="x-form-daypick-icons">';
27244         for (var i=0; i < 7; i++) {
27245             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27246             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27247         }
27248         
27249         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27250         viewEl.select('img').on('click', this.onClick, this);
27251         this.viewEl = viewEl;   
27252         
27253         
27254         // this will not work on Chrome!!!
27255         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27256         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27257         
27258         
27259           
27260
27261     },
27262
27263     // private
27264     initValue : Roo.emptyFn,
27265
27266     /**
27267      * Returns the checked state of the checkbox.
27268      * @return {Boolean} True if checked, else false
27269      */
27270     getValue : function(){
27271         return this.el.dom.value;
27272         
27273     },
27274
27275         // private
27276     onClick : function(e){ 
27277         //this.setChecked(!this.checked);
27278         Roo.get(e.target).toggleClass('x-menu-item-checked');
27279         this.refreshValue();
27280         //if(this.el.dom.checked != this.checked){
27281         //    this.setValue(this.el.dom.checked);
27282        // }
27283     },
27284     
27285     // private
27286     refreshValue : function()
27287     {
27288         var val = '';
27289         this.viewEl.select('img',true).each(function(e,i,n)  {
27290             val += e.is(".x-menu-item-checked") ? String(n) : '';
27291         });
27292         this.setValue(val, true);
27293     },
27294
27295     /**
27296      * Sets the checked state of the checkbox.
27297      * On is always based on a string comparison between inputValue and the param.
27298      * @param {Boolean/String} value - the value to set 
27299      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27300      */
27301     setValue : function(v,suppressEvent){
27302         if (!this.el.dom) {
27303             return;
27304         }
27305         var old = this.el.dom.value ;
27306         this.el.dom.value = v;
27307         if (suppressEvent) {
27308             return ;
27309         }
27310          
27311         // update display..
27312         this.viewEl.select('img',true).each(function(e,i,n)  {
27313             
27314             var on = e.is(".x-menu-item-checked");
27315             var newv = v.indexOf(String(n)) > -1;
27316             if (on != newv) {
27317                 e.toggleClass('x-menu-item-checked');
27318             }
27319             
27320         });
27321         
27322         
27323         this.fireEvent('change', this, v, old);
27324         
27325         
27326     },
27327    
27328     // handle setting of hidden value by some other method!!?!?
27329     setFromHidden: function()
27330     {
27331         if(!this.el){
27332             return;
27333         }
27334         //console.log("SET FROM HIDDEN");
27335         //alert('setFrom hidden');
27336         this.setValue(this.el.dom.value);
27337     },
27338     
27339     onDestroy : function()
27340     {
27341         if(this.viewEl){
27342             Roo.get(this.viewEl).remove();
27343         }
27344          
27345         Roo.form.DayPicker.superclass.onDestroy.call(this);
27346     }
27347
27348 });/*
27349  * RooJS Library 1.1.1
27350  * Copyright(c) 2008-2011  Alan Knowles
27351  *
27352  * License - LGPL
27353  */
27354  
27355
27356 /**
27357  * @class Roo.form.ComboCheck
27358  * @extends Roo.form.ComboBox
27359  * A combobox for multiple select items.
27360  *
27361  * FIXME - could do with a reset button..
27362  * 
27363  * @constructor
27364  * Create a new ComboCheck
27365  * @param {Object} config Configuration options
27366  */
27367 Roo.form.ComboCheck = function(config){
27368     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27369     // should verify some data...
27370     // like
27371     // hiddenName = required..
27372     // displayField = required
27373     // valudField == required
27374     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27375     var _t = this;
27376     Roo.each(req, function(e) {
27377         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27378             throw "Roo.form.ComboCheck : missing value for: " + e;
27379         }
27380     });
27381     
27382     
27383 };
27384
27385 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27386      
27387      
27388     editable : false,
27389      
27390     selectedClass: 'x-menu-item-checked', 
27391     
27392     // private
27393     onRender : function(ct, position){
27394         var _t = this;
27395         
27396         
27397         
27398         if(!this.tpl){
27399             var cls = 'x-combo-list';
27400
27401             
27402             this.tpl =  new Roo.Template({
27403                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27404                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27405                    '<span>{' + this.displayField + '}</span>' +
27406                     '</div>' 
27407                 
27408             });
27409         }
27410  
27411         
27412         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27413         this.view.singleSelect = false;
27414         this.view.multiSelect = true;
27415         this.view.toggleSelect = true;
27416         this.pageTb.add(new Roo.Toolbar.Fill(), {
27417             
27418             text: 'Done',
27419             handler: function()
27420             {
27421                 _t.collapse();
27422             }
27423         });
27424     },
27425     
27426     onViewOver : function(e, t){
27427         // do nothing...
27428         return;
27429         
27430     },
27431     
27432     onViewClick : function(doFocus,index){
27433         return;
27434         
27435     },
27436     select: function () {
27437         //Roo.log("SELECT CALLED");
27438     },
27439      
27440     selectByValue : function(xv, scrollIntoView){
27441         var ar = this.getValueArray();
27442         var sels = [];
27443         
27444         Roo.each(ar, function(v) {
27445             if(v === undefined || v === null){
27446                 return;
27447             }
27448             var r = this.findRecord(this.valueField, v);
27449             if(r){
27450                 sels.push(this.store.indexOf(r))
27451                 
27452             }
27453         },this);
27454         this.view.select(sels);
27455         return false;
27456     },
27457     
27458     
27459     
27460     onSelect : function(record, index){
27461        // Roo.log("onselect Called");
27462        // this is only called by the clear button now..
27463         this.view.clearSelections();
27464         this.setValue('[]');
27465         if (this.value != this.valueBefore) {
27466             this.fireEvent('change', this, this.value, this.valueBefore);
27467             this.valueBefore = this.value;
27468         }
27469     },
27470     getValueArray : function()
27471     {
27472         var ar = [] ;
27473         
27474         try {
27475             //Roo.log(this.value);
27476             if (typeof(this.value) == 'undefined') {
27477                 return [];
27478             }
27479             var ar = Roo.decode(this.value);
27480             return  ar instanceof Array ? ar : []; //?? valid?
27481             
27482         } catch(e) {
27483             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27484             return [];
27485         }
27486          
27487     },
27488     expand : function ()
27489     {
27490         
27491         Roo.form.ComboCheck.superclass.expand.call(this);
27492         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27493         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27494         
27495
27496     },
27497     
27498     collapse : function(){
27499         Roo.form.ComboCheck.superclass.collapse.call(this);
27500         var sl = this.view.getSelectedIndexes();
27501         var st = this.store;
27502         var nv = [];
27503         var tv = [];
27504         var r;
27505         Roo.each(sl, function(i) {
27506             r = st.getAt(i);
27507             nv.push(r.get(this.valueField));
27508         },this);
27509         this.setValue(Roo.encode(nv));
27510         if (this.value != this.valueBefore) {
27511
27512             this.fireEvent('change', this, this.value, this.valueBefore);
27513             this.valueBefore = this.value;
27514         }
27515         
27516     },
27517     
27518     setValue : function(v){
27519         // Roo.log(v);
27520         this.value = v;
27521         
27522         var vals = this.getValueArray();
27523         var tv = [];
27524         Roo.each(vals, function(k) {
27525             var r = this.findRecord(this.valueField, k);
27526             if(r){
27527                 tv.push(r.data[this.displayField]);
27528             }else if(this.valueNotFoundText !== undefined){
27529                 tv.push( this.valueNotFoundText );
27530             }
27531         },this);
27532        // Roo.log(tv);
27533         
27534         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27535         this.hiddenField.value = v;
27536         this.value = v;
27537     }
27538     
27539 });/*
27540  * Based on:
27541  * Ext JS Library 1.1.1
27542  * Copyright(c) 2006-2007, Ext JS, LLC.
27543  *
27544  * Originally Released Under LGPL - original licence link has changed is not relivant.
27545  *
27546  * Fork - LGPL
27547  * <script type="text/javascript">
27548  */
27549  
27550 /**
27551  * @class Roo.form.Signature
27552  * @extends Roo.form.Field
27553  * Signature field.  
27554  * @constructor
27555  * 
27556  * @param {Object} config Configuration options
27557  */
27558
27559 Roo.form.Signature = function(config){
27560     Roo.form.Signature.superclass.constructor.call(this, config);
27561     
27562     this.addEvents({// not in used??
27563          /**
27564          * @event confirm
27565          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27566              * @param {Roo.form.Signature} combo This combo box
27567              */
27568         'confirm' : true,
27569         /**
27570          * @event reset
27571          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27572              * @param {Roo.form.ComboBox} combo This combo box
27573              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27574              */
27575         'reset' : true
27576     });
27577 };
27578
27579 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27580     /**
27581      * @cfg {Object} labels Label to use when rendering a form.
27582      * defaults to 
27583      * labels : { 
27584      *      clear : "Clear",
27585      *      confirm : "Confirm"
27586      *  }
27587      */
27588     labels : { 
27589         clear : "Clear",
27590         confirm : "Confirm"
27591     },
27592     /**
27593      * @cfg {Number} width The signature panel width (defaults to 300)
27594      */
27595     width: 300,
27596     /**
27597      * @cfg {Number} height The signature panel height (defaults to 100)
27598      */
27599     height : 100,
27600     /**
27601      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27602      */
27603     allowBlank : false,
27604     
27605     //private
27606     // {Object} signPanel The signature SVG panel element (defaults to {})
27607     signPanel : {},
27608     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27609     isMouseDown : false,
27610     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27611     isConfirmed : false,
27612     // {String} signatureTmp SVG mapping string (defaults to empty string)
27613     signatureTmp : '',
27614     
27615     
27616     defaultAutoCreate : { // modified by initCompnoent..
27617         tag: "input",
27618         type:"hidden"
27619     },
27620
27621     // private
27622     onRender : function(ct, position){
27623         
27624         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27625         
27626         this.wrap = this.el.wrap({
27627             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27628         });
27629         
27630         this.createToolbar(this);
27631         this.signPanel = this.wrap.createChild({
27632                 tag: 'div',
27633                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27634             }, this.el
27635         );
27636             
27637         this.svgID = Roo.id();
27638         this.svgEl = this.signPanel.createChild({
27639               xmlns : 'http://www.w3.org/2000/svg',
27640               tag : 'svg',
27641               id : this.svgID + "-svg",
27642               width: this.width,
27643               height: this.height,
27644               viewBox: '0 0 '+this.width+' '+this.height,
27645               cn : [
27646                 {
27647                     tag: "rect",
27648                     id: this.svgID + "-svg-r",
27649                     width: this.width,
27650                     height: this.height,
27651                     fill: "#ffa"
27652                 },
27653                 {
27654                     tag: "line",
27655                     id: this.svgID + "-svg-l",
27656                     x1: "0", // start
27657                     y1: (this.height*0.8), // start set the line in 80% of height
27658                     x2: this.width, // end
27659                     y2: (this.height*0.8), // end set the line in 80% of height
27660                     'stroke': "#666",
27661                     'stroke-width': "1",
27662                     'stroke-dasharray': "3",
27663                     'shape-rendering': "crispEdges",
27664                     'pointer-events': "none"
27665                 },
27666                 {
27667                     tag: "path",
27668                     id: this.svgID + "-svg-p",
27669                     'stroke': "navy",
27670                     'stroke-width': "3",
27671                     'fill': "none",
27672                     'pointer-events': 'none'
27673                 }
27674               ]
27675         });
27676         this.createSVG();
27677         this.svgBox = this.svgEl.dom.getScreenCTM();
27678     },
27679     createSVG : function(){ 
27680         var svg = this.signPanel;
27681         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27682         var t = this;
27683
27684         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27685         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27686         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27687         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27688         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27689         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27690         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27691         
27692     },
27693     isTouchEvent : function(e){
27694         return e.type.match(/^touch/);
27695     },
27696     getCoords : function (e) {
27697         var pt    = this.svgEl.dom.createSVGPoint();
27698         pt.x = e.clientX; 
27699         pt.y = e.clientY;
27700         if (this.isTouchEvent(e)) {
27701             pt.x =  e.targetTouches[0].clientX;
27702             pt.y = e.targetTouches[0].clientY;
27703         }
27704         var a = this.svgEl.dom.getScreenCTM();
27705         var b = a.inverse();
27706         var mx = pt.matrixTransform(b);
27707         return mx.x + ',' + mx.y;
27708     },
27709     //mouse event headler 
27710     down : function (e) {
27711         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27712         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27713         
27714         this.isMouseDown = true;
27715         
27716         e.preventDefault();
27717     },
27718     move : function (e) {
27719         if (this.isMouseDown) {
27720             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27721             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27722         }
27723         
27724         e.preventDefault();
27725     },
27726     up : function (e) {
27727         this.isMouseDown = false;
27728         var sp = this.signatureTmp.split(' ');
27729         
27730         if(sp.length > 1){
27731             if(!sp[sp.length-2].match(/^L/)){
27732                 sp.pop();
27733                 sp.pop();
27734                 sp.push("");
27735                 this.signatureTmp = sp.join(" ");
27736             }
27737         }
27738         if(this.getValue() != this.signatureTmp){
27739             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27740             this.isConfirmed = false;
27741         }
27742         e.preventDefault();
27743     },
27744     
27745     /**
27746      * Protected method that will not generally be called directly. It
27747      * is called when the editor creates its toolbar. Override this method if you need to
27748      * add custom toolbar buttons.
27749      * @param {HtmlEditor} editor
27750      */
27751     createToolbar : function(editor){
27752          function btn(id, toggle, handler){
27753             var xid = fid + '-'+ id ;
27754             return {
27755                 id : xid,
27756                 cmd : id,
27757                 cls : 'x-btn-icon x-edit-'+id,
27758                 enableToggle:toggle !== false,
27759                 scope: editor, // was editor...
27760                 handler:handler||editor.relayBtnCmd,
27761                 clickEvent:'mousedown',
27762                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27763                 tabIndex:-1
27764             };
27765         }
27766         
27767         
27768         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27769         this.tb = tb;
27770         this.tb.add(
27771            {
27772                 cls : ' x-signature-btn x-signature-'+id,
27773                 scope: editor, // was editor...
27774                 handler: this.reset,
27775                 clickEvent:'mousedown',
27776                 text: this.labels.clear
27777             },
27778             {
27779                  xtype : 'Fill',
27780                  xns: Roo.Toolbar
27781             }, 
27782             {
27783                 cls : '  x-signature-btn x-signature-'+id,
27784                 scope: editor, // was editor...
27785                 handler: this.confirmHandler,
27786                 clickEvent:'mousedown',
27787                 text: this.labels.confirm
27788             }
27789         );
27790     
27791     },
27792     //public
27793     /**
27794      * when user is clicked confirm then show this image.....
27795      * 
27796      * @return {String} Image Data URI
27797      */
27798     getImageDataURI : function(){
27799         var svg = this.svgEl.dom.parentNode.innerHTML;
27800         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27801         return src; 
27802     },
27803     /**
27804      * 
27805      * @return {Boolean} this.isConfirmed
27806      */
27807     getConfirmed : function(){
27808         return this.isConfirmed;
27809     },
27810     /**
27811      * 
27812      * @return {Number} this.width
27813      */
27814     getWidth : function(){
27815         return this.width;
27816     },
27817     /**
27818      * 
27819      * @return {Number} this.height
27820      */
27821     getHeight : function(){
27822         return this.height;
27823     },
27824     // private
27825     getSignature : function(){
27826         return this.signatureTmp;
27827     },
27828     // private
27829     reset : function(){
27830         this.signatureTmp = '';
27831         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27832         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27833         this.isConfirmed = false;
27834         Roo.form.Signature.superclass.reset.call(this);
27835     },
27836     setSignature : function(s){
27837         this.signatureTmp = s;
27838         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27839         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27840         this.setValue(s);
27841         this.isConfirmed = false;
27842         Roo.form.Signature.superclass.reset.call(this);
27843     }, 
27844     test : function(){
27845 //        Roo.log(this.signPanel.dom.contentWindow.up())
27846     },
27847     //private
27848     setConfirmed : function(){
27849         
27850         
27851         
27852 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27853     },
27854     // private
27855     confirmHandler : function(){
27856         if(!this.getSignature()){
27857             return;
27858         }
27859         
27860         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27861         this.setValue(this.getSignature());
27862         this.isConfirmed = true;
27863         
27864         this.fireEvent('confirm', this);
27865     },
27866     // private
27867     // Subclasses should provide the validation implementation by overriding this
27868     validateValue : function(value){
27869         if(this.allowBlank){
27870             return true;
27871         }
27872         
27873         if(this.isConfirmed){
27874             return true;
27875         }
27876         return false;
27877     }
27878 });/*
27879  * Based on:
27880  * Ext JS Library 1.1.1
27881  * Copyright(c) 2006-2007, Ext JS, LLC.
27882  *
27883  * Originally Released Under LGPL - original licence link has changed is not relivant.
27884  *
27885  * Fork - LGPL
27886  * <script type="text/javascript">
27887  */
27888  
27889
27890 /**
27891  * @class Roo.form.ComboBox
27892  * @extends Roo.form.TriggerField
27893  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27894  * @constructor
27895  * Create a new ComboBox.
27896  * @param {Object} config Configuration options
27897  */
27898 Roo.form.Select = function(config){
27899     Roo.form.Select.superclass.constructor.call(this, config);
27900      
27901 };
27902
27903 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27904     /**
27905      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27906      */
27907     /**
27908      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27909      * rendering into an Roo.Editor, defaults to false)
27910      */
27911     /**
27912      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27913      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27914      */
27915     /**
27916      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27917      */
27918     /**
27919      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27920      * the dropdown list (defaults to undefined, with no header element)
27921      */
27922
27923      /**
27924      * @cfg {String/Roo.Template} tpl The template to use to render the output
27925      */
27926      
27927     // private
27928     defaultAutoCreate : {tag: "select"  },
27929     /**
27930      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27931      */
27932     listWidth: undefined,
27933     /**
27934      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27935      * mode = 'remote' or 'text' if mode = 'local')
27936      */
27937     displayField: undefined,
27938     /**
27939      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27940      * mode = 'remote' or 'value' if mode = 'local'). 
27941      * Note: use of a valueField requires the user make a selection
27942      * in order for a value to be mapped.
27943      */
27944     valueField: undefined,
27945     
27946     
27947     /**
27948      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27949      * field's data value (defaults to the underlying DOM element's name)
27950      */
27951     hiddenName: undefined,
27952     /**
27953      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27954      */
27955     listClass: '',
27956     /**
27957      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27958      */
27959     selectedClass: 'x-combo-selected',
27960     /**
27961      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27962      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27963      * which displays a downward arrow icon).
27964      */
27965     triggerClass : 'x-form-arrow-trigger',
27966     /**
27967      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27968      */
27969     shadow:'sides',
27970     /**
27971      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27972      * anchor positions (defaults to 'tl-bl')
27973      */
27974     listAlign: 'tl-bl?',
27975     /**
27976      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27977      */
27978     maxHeight: 300,
27979     /**
27980      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27981      * query specified by the allQuery config option (defaults to 'query')
27982      */
27983     triggerAction: 'query',
27984     /**
27985      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27986      * (defaults to 4, does not apply if editable = false)
27987      */
27988     minChars : 4,
27989     /**
27990      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27991      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27992      */
27993     typeAhead: false,
27994     /**
27995      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27996      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27997      */
27998     queryDelay: 500,
27999     /**
28000      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28001      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
28002      */
28003     pageSize: 0,
28004     /**
28005      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
28006      * when editable = true (defaults to false)
28007      */
28008     selectOnFocus:false,
28009     /**
28010      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28011      */
28012     queryParam: 'query',
28013     /**
28014      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
28015      * when mode = 'remote' (defaults to 'Loading...')
28016      */
28017     loadingText: 'Loading...',
28018     /**
28019      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28020      */
28021     resizable: false,
28022     /**
28023      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28024      */
28025     handleHeight : 8,
28026     /**
28027      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28028      * traditional select (defaults to true)
28029      */
28030     editable: true,
28031     /**
28032      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28033      */
28034     allQuery: '',
28035     /**
28036      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28037      */
28038     mode: 'remote',
28039     /**
28040      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28041      * listWidth has a higher value)
28042      */
28043     minListWidth : 70,
28044     /**
28045      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28046      * allow the user to set arbitrary text into the field (defaults to false)
28047      */
28048     forceSelection:false,
28049     /**
28050      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28051      * if typeAhead = true (defaults to 250)
28052      */
28053     typeAheadDelay : 250,
28054     /**
28055      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28056      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28057      */
28058     valueNotFoundText : undefined,
28059     
28060     /**
28061      * @cfg {String} defaultValue The value displayed after loading the store.
28062      */
28063     defaultValue: '',
28064     
28065     /**
28066      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28067      */
28068     blockFocus : false,
28069     
28070     /**
28071      * @cfg {Boolean} disableClear Disable showing of clear button.
28072      */
28073     disableClear : false,
28074     /**
28075      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28076      */
28077     alwaysQuery : false,
28078     
28079     //private
28080     addicon : false,
28081     editicon: false,
28082     
28083     // element that contains real text value.. (when hidden is used..)
28084      
28085     // private
28086     onRender : function(ct, position){
28087         Roo.form.Field.prototype.onRender.call(this, ct, position);
28088         
28089         if(this.store){
28090             this.store.on('beforeload', this.onBeforeLoad, this);
28091             this.store.on('load', this.onLoad, this);
28092             this.store.on('loadexception', this.onLoadException, this);
28093             this.store.load({});
28094         }
28095         
28096         
28097         
28098     },
28099
28100     // private
28101     initEvents : function(){
28102         //Roo.form.ComboBox.superclass.initEvents.call(this);
28103  
28104     },
28105
28106     onDestroy : function(){
28107        
28108         if(this.store){
28109             this.store.un('beforeload', this.onBeforeLoad, this);
28110             this.store.un('load', this.onLoad, this);
28111             this.store.un('loadexception', this.onLoadException, this);
28112         }
28113         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28114     },
28115
28116     // private
28117     fireKey : function(e){
28118         if(e.isNavKeyPress() && !this.list.isVisible()){
28119             this.fireEvent("specialkey", this, e);
28120         }
28121     },
28122
28123     // private
28124     onResize: function(w, h){
28125         
28126         return; 
28127     
28128         
28129     },
28130
28131     /**
28132      * Allow or prevent the user from directly editing the field text.  If false is passed,
28133      * the user will only be able to select from the items defined in the dropdown list.  This method
28134      * is the runtime equivalent of setting the 'editable' config option at config time.
28135      * @param {Boolean} value True to allow the user to directly edit the field text
28136      */
28137     setEditable : function(value){
28138          
28139     },
28140
28141     // private
28142     onBeforeLoad : function(){
28143         
28144         Roo.log("Select before load");
28145         return;
28146     
28147         this.innerList.update(this.loadingText ?
28148                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28149         //this.restrictHeight();
28150         this.selectedIndex = -1;
28151     },
28152
28153     // private
28154     onLoad : function(){
28155
28156     
28157         var dom = this.el.dom;
28158         dom.innerHTML = '';
28159          var od = dom.ownerDocument;
28160          
28161         if (this.emptyText) {
28162             var op = od.createElement('option');
28163             op.setAttribute('value', '');
28164             op.innerHTML = String.format('{0}', this.emptyText);
28165             dom.appendChild(op);
28166         }
28167         if(this.store.getCount() > 0){
28168            
28169             var vf = this.valueField;
28170             var df = this.displayField;
28171             this.store.data.each(function(r) {
28172                 // which colmsn to use... testing - cdoe / title..
28173                 var op = od.createElement('option');
28174                 op.setAttribute('value', r.data[vf]);
28175                 op.innerHTML = String.format('{0}', r.data[df]);
28176                 dom.appendChild(op);
28177             });
28178             if (typeof(this.defaultValue != 'undefined')) {
28179                 this.setValue(this.defaultValue);
28180             }
28181             
28182              
28183         }else{
28184             //this.onEmptyResults();
28185         }
28186         //this.el.focus();
28187     },
28188     // private
28189     onLoadException : function()
28190     {
28191         dom.innerHTML = '';
28192             
28193         Roo.log("Select on load exception");
28194         return;
28195     
28196         this.collapse();
28197         Roo.log(this.store.reader.jsonData);
28198         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28199             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28200         }
28201         
28202         
28203     },
28204     // private
28205     onTypeAhead : function(){
28206          
28207     },
28208
28209     // private
28210     onSelect : function(record, index){
28211         Roo.log('on select?');
28212         return;
28213         if(this.fireEvent('beforeselect', this, record, index) !== false){
28214             this.setFromData(index > -1 ? record.data : false);
28215             this.collapse();
28216             this.fireEvent('select', this, record, index);
28217         }
28218     },
28219
28220     /**
28221      * Returns the currently selected field value or empty string if no value is set.
28222      * @return {String} value The selected value
28223      */
28224     getValue : function(){
28225         var dom = this.el.dom;
28226         this.value = dom.options[dom.selectedIndex].value;
28227         return this.value;
28228         
28229     },
28230
28231     /**
28232      * Clears any text/value currently set in the field
28233      */
28234     clearValue : function(){
28235         this.value = '';
28236         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28237         
28238     },
28239
28240     /**
28241      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28242      * will be displayed in the field.  If the value does not match the data value of an existing item,
28243      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28244      * Otherwise the field will be blank (although the value will still be set).
28245      * @param {String} value The value to match
28246      */
28247     setValue : function(v){
28248         var d = this.el.dom;
28249         for (var i =0; i < d.options.length;i++) {
28250             if (v == d.options[i].value) {
28251                 d.selectedIndex = i;
28252                 this.value = v;
28253                 return;
28254             }
28255         }
28256         this.clearValue();
28257     },
28258     /**
28259      * @property {Object} the last set data for the element
28260      */
28261     
28262     lastData : false,
28263     /**
28264      * Sets the value of the field based on a object which is related to the record format for the store.
28265      * @param {Object} value the value to set as. or false on reset?
28266      */
28267     setFromData : function(o){
28268         Roo.log('setfrom data?');
28269          
28270         
28271         
28272     },
28273     // private
28274     reset : function(){
28275         this.clearValue();
28276     },
28277     // private
28278     findRecord : function(prop, value){
28279         
28280         return false;
28281     
28282         var record;
28283         if(this.store.getCount() > 0){
28284             this.store.each(function(r){
28285                 if(r.data[prop] == value){
28286                     record = r;
28287                     return false;
28288                 }
28289                 return true;
28290             });
28291         }
28292         return record;
28293     },
28294     
28295     getName: function()
28296     {
28297         // returns hidden if it's set..
28298         if (!this.rendered) {return ''};
28299         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28300         
28301     },
28302      
28303
28304     
28305
28306     // private
28307     onEmptyResults : function(){
28308         Roo.log('empty results');
28309         //this.collapse();
28310     },
28311
28312     /**
28313      * Returns true if the dropdown list is expanded, else false.
28314      */
28315     isExpanded : function(){
28316         return false;
28317     },
28318
28319     /**
28320      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28321      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28322      * @param {String} value The data value of the item to select
28323      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28324      * selected item if it is not currently in view (defaults to true)
28325      * @return {Boolean} True if the value matched an item in the list, else false
28326      */
28327     selectByValue : function(v, scrollIntoView){
28328         Roo.log('select By Value');
28329         return false;
28330     
28331         if(v !== undefined && v !== null){
28332             var r = this.findRecord(this.valueField || this.displayField, v);
28333             if(r){
28334                 this.select(this.store.indexOf(r), scrollIntoView);
28335                 return true;
28336             }
28337         }
28338         return false;
28339     },
28340
28341     /**
28342      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28343      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28344      * @param {Number} index The zero-based index of the list item to select
28345      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28346      * selected item if it is not currently in view (defaults to true)
28347      */
28348     select : function(index, scrollIntoView){
28349         Roo.log('select ');
28350         return  ;
28351         
28352         this.selectedIndex = index;
28353         this.view.select(index);
28354         if(scrollIntoView !== false){
28355             var el = this.view.getNode(index);
28356             if(el){
28357                 this.innerList.scrollChildIntoView(el, false);
28358             }
28359         }
28360     },
28361
28362       
28363
28364     // private
28365     validateBlur : function(){
28366         
28367         return;
28368         
28369     },
28370
28371     // private
28372     initQuery : function(){
28373         this.doQuery(this.getRawValue());
28374     },
28375
28376     // private
28377     doForce : function(){
28378         if(this.el.dom.value.length > 0){
28379             this.el.dom.value =
28380                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28381              
28382         }
28383     },
28384
28385     /**
28386      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28387      * query allowing the query action to be canceled if needed.
28388      * @param {String} query The SQL query to execute
28389      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28390      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28391      * saved in the current store (defaults to false)
28392      */
28393     doQuery : function(q, forceAll){
28394         
28395         Roo.log('doQuery?');
28396         if(q === undefined || q === null){
28397             q = '';
28398         }
28399         var qe = {
28400             query: q,
28401             forceAll: forceAll,
28402             combo: this,
28403             cancel:false
28404         };
28405         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28406             return false;
28407         }
28408         q = qe.query;
28409         forceAll = qe.forceAll;
28410         if(forceAll === true || (q.length >= this.minChars)){
28411             if(this.lastQuery != q || this.alwaysQuery){
28412                 this.lastQuery = q;
28413                 if(this.mode == 'local'){
28414                     this.selectedIndex = -1;
28415                     if(forceAll){
28416                         this.store.clearFilter();
28417                     }else{
28418                         this.store.filter(this.displayField, q);
28419                     }
28420                     this.onLoad();
28421                 }else{
28422                     this.store.baseParams[this.queryParam] = q;
28423                     this.store.load({
28424                         params: this.getParams(q)
28425                     });
28426                     this.expand();
28427                 }
28428             }else{
28429                 this.selectedIndex = -1;
28430                 this.onLoad();   
28431             }
28432         }
28433     },
28434
28435     // private
28436     getParams : function(q){
28437         var p = {};
28438         //p[this.queryParam] = q;
28439         if(this.pageSize){
28440             p.start = 0;
28441             p.limit = this.pageSize;
28442         }
28443         return p;
28444     },
28445
28446     /**
28447      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28448      */
28449     collapse : function(){
28450         
28451     },
28452
28453     // private
28454     collapseIf : function(e){
28455         
28456     },
28457
28458     /**
28459      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28460      */
28461     expand : function(){
28462         
28463     } ,
28464
28465     // private
28466      
28467
28468     /** 
28469     * @cfg {Boolean} grow 
28470     * @hide 
28471     */
28472     /** 
28473     * @cfg {Number} growMin 
28474     * @hide 
28475     */
28476     /** 
28477     * @cfg {Number} growMax 
28478     * @hide 
28479     */
28480     /**
28481      * @hide
28482      * @method autoSize
28483      */
28484     
28485     setWidth : function()
28486     {
28487         
28488     },
28489     getResizeEl : function(){
28490         return this.el;
28491     }
28492 });//<script type="text/javasscript">
28493  
28494
28495 /**
28496  * @class Roo.DDView
28497  * A DnD enabled version of Roo.View.
28498  * @param {Element/String} container The Element in which to create the View.
28499  * @param {String} tpl The template string used to create the markup for each element of the View
28500  * @param {Object} config The configuration properties. These include all the config options of
28501  * {@link Roo.View} plus some specific to this class.<br>
28502  * <p>
28503  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28504  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28505  * <p>
28506  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28507 .x-view-drag-insert-above {
28508         border-top:1px dotted #3366cc;
28509 }
28510 .x-view-drag-insert-below {
28511         border-bottom:1px dotted #3366cc;
28512 }
28513 </code></pre>
28514  * 
28515  */
28516  
28517 Roo.DDView = function(container, tpl, config) {
28518     Roo.DDView.superclass.constructor.apply(this, arguments);
28519     this.getEl().setStyle("outline", "0px none");
28520     this.getEl().unselectable();
28521     if (this.dragGroup) {
28522         this.setDraggable(this.dragGroup.split(","));
28523     }
28524     if (this.dropGroup) {
28525         this.setDroppable(this.dropGroup.split(","));
28526     }
28527     if (this.deletable) {
28528         this.setDeletable();
28529     }
28530     this.isDirtyFlag = false;
28531         this.addEvents({
28532                 "drop" : true
28533         });
28534 };
28535
28536 Roo.extend(Roo.DDView, Roo.View, {
28537 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28538 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28539 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28540 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28541
28542         isFormField: true,
28543
28544         reset: Roo.emptyFn,
28545         
28546         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28547
28548         validate: function() {
28549                 return true;
28550         },
28551         
28552         destroy: function() {
28553                 this.purgeListeners();
28554                 this.getEl.removeAllListeners();
28555                 this.getEl().remove();
28556                 if (this.dragZone) {
28557                         if (this.dragZone.destroy) {
28558                                 this.dragZone.destroy();
28559                         }
28560                 }
28561                 if (this.dropZone) {
28562                         if (this.dropZone.destroy) {
28563                                 this.dropZone.destroy();
28564                         }
28565                 }
28566         },
28567
28568 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28569         getName: function() {
28570                 return this.name;
28571         },
28572
28573 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28574         setValue: function(v) {
28575                 if (!this.store) {
28576                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28577                 }
28578                 var data = {};
28579                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28580                 this.store.proxy = new Roo.data.MemoryProxy(data);
28581                 this.store.load();
28582         },
28583
28584 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28585         getValue: function() {
28586                 var result = '(';
28587                 this.store.each(function(rec) {
28588                         result += rec.id + ',';
28589                 });
28590                 return result.substr(0, result.length - 1) + ')';
28591         },
28592         
28593         getIds: function() {
28594                 var i = 0, result = new Array(this.store.getCount());
28595                 this.store.each(function(rec) {
28596                         result[i++] = rec.id;
28597                 });
28598                 return result;
28599         },
28600         
28601         isDirty: function() {
28602                 return this.isDirtyFlag;
28603         },
28604
28605 /**
28606  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28607  *      whole Element becomes the target, and this causes the drop gesture to append.
28608  */
28609     getTargetFromEvent : function(e) {
28610                 var target = e.getTarget();
28611                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28612                 target = target.parentNode;
28613                 }
28614                 if (!target) {
28615                         target = this.el.dom.lastChild || this.el.dom;
28616                 }
28617                 return target;
28618     },
28619
28620 /**
28621  *      Create the drag data which consists of an object which has the property "ddel" as
28622  *      the drag proxy element. 
28623  */
28624     getDragData : function(e) {
28625         var target = this.findItemFromChild(e.getTarget());
28626                 if(target) {
28627                         this.handleSelection(e);
28628                         var selNodes = this.getSelectedNodes();
28629             var dragData = {
28630                 source: this,
28631                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28632                 nodes: selNodes,
28633                 records: []
28634                         };
28635                         var selectedIndices = this.getSelectedIndexes();
28636                         for (var i = 0; i < selectedIndices.length; i++) {
28637                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28638                         }
28639                         if (selNodes.length == 1) {
28640                                 dragData.ddel = target.cloneNode(true); // the div element
28641                         } else {
28642                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28643                                 div.className = 'multi-proxy';
28644                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28645                                         div.appendChild(selNodes[i].cloneNode(true));
28646                                 }
28647                                 dragData.ddel = div;
28648                         }
28649             //console.log(dragData)
28650             //console.log(dragData.ddel.innerHTML)
28651                         return dragData;
28652                 }
28653         //console.log('nodragData')
28654                 return false;
28655     },
28656     
28657 /**     Specify to which ddGroup items in this DDView may be dragged. */
28658     setDraggable: function(ddGroup) {
28659         if (ddGroup instanceof Array) {
28660                 Roo.each(ddGroup, this.setDraggable, this);
28661                 return;
28662         }
28663         if (this.dragZone) {
28664                 this.dragZone.addToGroup(ddGroup);
28665         } else {
28666                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28667                                 containerScroll: true,
28668                                 ddGroup: ddGroup 
28669
28670                         });
28671 //                      Draggability implies selection. DragZone's mousedown selects the element.
28672                         if (!this.multiSelect) { this.singleSelect = true; }
28673
28674 //                      Wire the DragZone's handlers up to methods in *this*
28675                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28676                 }
28677     },
28678
28679 /**     Specify from which ddGroup this DDView accepts drops. */
28680     setDroppable: function(ddGroup) {
28681         if (ddGroup instanceof Array) {
28682                 Roo.each(ddGroup, this.setDroppable, this);
28683                 return;
28684         }
28685         if (this.dropZone) {
28686                 this.dropZone.addToGroup(ddGroup);
28687         } else {
28688                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28689                                 containerScroll: true,
28690                                 ddGroup: ddGroup
28691                         });
28692
28693 //                      Wire the DropZone's handlers up to methods in *this*
28694                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28695                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28696                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28697                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28698                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28699                 }
28700     },
28701
28702 /**     Decide whether to drop above or below a View node. */
28703     getDropPoint : function(e, n, dd){
28704         if (n == this.el.dom) { return "above"; }
28705                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28706                 var c = t + (b - t) / 2;
28707                 var y = Roo.lib.Event.getPageY(e);
28708                 if(y <= c) {
28709                         return "above";
28710                 }else{
28711                         return "below";
28712                 }
28713     },
28714
28715     onNodeEnter : function(n, dd, e, data){
28716                 return false;
28717     },
28718     
28719     onNodeOver : function(n, dd, e, data){
28720                 var pt = this.getDropPoint(e, n, dd);
28721                 // set the insert point style on the target node
28722                 var dragElClass = this.dropNotAllowed;
28723                 if (pt) {
28724                         var targetElClass;
28725                         if (pt == "above"){
28726                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28727                                 targetElClass = "x-view-drag-insert-above";
28728                         } else {
28729                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28730                                 targetElClass = "x-view-drag-insert-below";
28731                         }
28732                         if (this.lastInsertClass != targetElClass){
28733                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28734                                 this.lastInsertClass = targetElClass;
28735                         }
28736                 }
28737                 return dragElClass;
28738         },
28739
28740     onNodeOut : function(n, dd, e, data){
28741                 this.removeDropIndicators(n);
28742     },
28743
28744     onNodeDrop : function(n, dd, e, data){
28745         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28746                 return false;
28747         }
28748         var pt = this.getDropPoint(e, n, dd);
28749                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28750                 if (pt == "below") { insertAt++; }
28751                 for (var i = 0; i < data.records.length; i++) {
28752                         var r = data.records[i];
28753                         var dup = this.store.getById(r.id);
28754                         if (dup && (dd != this.dragZone)) {
28755                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28756                         } else {
28757                                 if (data.copy) {
28758                                         this.store.insert(insertAt++, r.copy());
28759                                 } else {
28760                                         data.source.isDirtyFlag = true;
28761                                         r.store.remove(r);
28762                                         this.store.insert(insertAt++, r);
28763                                 }
28764                                 this.isDirtyFlag = true;
28765                         }
28766                 }
28767                 this.dragZone.cachedTarget = null;
28768                 return true;
28769     },
28770
28771     removeDropIndicators : function(n){
28772                 if(n){
28773                         Roo.fly(n).removeClass([
28774                                 "x-view-drag-insert-above",
28775                                 "x-view-drag-insert-below"]);
28776                         this.lastInsertClass = "_noclass";
28777                 }
28778     },
28779
28780 /**
28781  *      Utility method. Add a delete option to the DDView's context menu.
28782  *      @param {String} imageUrl The URL of the "delete" icon image.
28783  */
28784         setDeletable: function(imageUrl) {
28785                 if (!this.singleSelect && !this.multiSelect) {
28786                         this.singleSelect = true;
28787                 }
28788                 var c = this.getContextMenu();
28789                 this.contextMenu.on("itemclick", function(item) {
28790                         switch (item.id) {
28791                                 case "delete":
28792                                         this.remove(this.getSelectedIndexes());
28793                                         break;
28794                         }
28795                 }, this);
28796                 this.contextMenu.add({
28797                         icon: imageUrl,
28798                         id: "delete",
28799                         text: 'Delete'
28800                 });
28801         },
28802         
28803 /**     Return the context menu for this DDView. */
28804         getContextMenu: function() {
28805                 if (!this.contextMenu) {
28806 //                      Create the View's context menu
28807                         this.contextMenu = new Roo.menu.Menu({
28808                                 id: this.id + "-contextmenu"
28809                         });
28810                         this.el.on("contextmenu", this.showContextMenu, this);
28811                 }
28812                 return this.contextMenu;
28813         },
28814         
28815         disableContextMenu: function() {
28816                 if (this.contextMenu) {
28817                         this.el.un("contextmenu", this.showContextMenu, this);
28818                 }
28819         },
28820
28821         showContextMenu: function(e, item) {
28822         item = this.findItemFromChild(e.getTarget());
28823                 if (item) {
28824                         e.stopEvent();
28825                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28826                         this.contextMenu.showAt(e.getXY());
28827             }
28828     },
28829
28830 /**
28831  *      Remove {@link Roo.data.Record}s at the specified indices.
28832  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28833  */
28834     remove: function(selectedIndices) {
28835                 selectedIndices = [].concat(selectedIndices);
28836                 for (var i = 0; i < selectedIndices.length; i++) {
28837                         var rec = this.store.getAt(selectedIndices[i]);
28838                         this.store.remove(rec);
28839                 }
28840     },
28841
28842 /**
28843  *      Double click fires the event, but also, if this is draggable, and there is only one other
28844  *      related DropZone, it transfers the selected node.
28845  */
28846     onDblClick : function(e){
28847         var item = this.findItemFromChild(e.getTarget());
28848         if(item){
28849             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28850                 return false;
28851             }
28852             if (this.dragGroup) {
28853                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28854                     while (targets.indexOf(this.dropZone) > -1) {
28855                             targets.remove(this.dropZone);
28856                                 }
28857                     if (targets.length == 1) {
28858                                         this.dragZone.cachedTarget = null;
28859                         var el = Roo.get(targets[0].getEl());
28860                         var box = el.getBox(true);
28861                         targets[0].onNodeDrop(el.dom, {
28862                                 target: el.dom,
28863                                 xy: [box.x, box.y + box.height - 1]
28864                         }, null, this.getDragData(e));
28865                     }
28866                 }
28867         }
28868     },
28869     
28870     handleSelection: function(e) {
28871                 this.dragZone.cachedTarget = null;
28872         var item = this.findItemFromChild(e.getTarget());
28873         if (!item) {
28874                 this.clearSelections(true);
28875                 return;
28876         }
28877                 if (item && (this.multiSelect || this.singleSelect)){
28878                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28879                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28880                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28881                                 this.unselect(item);
28882                         } else {
28883                                 this.select(item, this.multiSelect && e.ctrlKey);
28884                                 this.lastSelection = item;
28885                         }
28886                 }
28887     },
28888
28889     onItemClick : function(item, index, e){
28890                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28891                         return false;
28892                 }
28893                 return true;
28894     },
28895
28896     unselect : function(nodeInfo, suppressEvent){
28897                 var node = this.getNode(nodeInfo);
28898                 if(node && this.isSelected(node)){
28899                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28900                                 Roo.fly(node).removeClass(this.selectedClass);
28901                                 this.selections.remove(node);
28902                                 if(!suppressEvent){
28903                                         this.fireEvent("selectionchange", this, this.selections);
28904                                 }
28905                         }
28906                 }
28907     }
28908 });
28909 /*
28910  * Based on:
28911  * Ext JS Library 1.1.1
28912  * Copyright(c) 2006-2007, Ext JS, LLC.
28913  *
28914  * Originally Released Under LGPL - original licence link has changed is not relivant.
28915  *
28916  * Fork - LGPL
28917  * <script type="text/javascript">
28918  */
28919  
28920 /**
28921  * @class Roo.LayoutManager
28922  * @extends Roo.util.Observable
28923  * Base class for layout managers.
28924  */
28925 Roo.LayoutManager = function(container, config){
28926     Roo.LayoutManager.superclass.constructor.call(this);
28927     this.el = Roo.get(container);
28928     // ie scrollbar fix
28929     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28930         document.body.scroll = "no";
28931     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28932         this.el.position('relative');
28933     }
28934     this.id = this.el.id;
28935     this.el.addClass("x-layout-container");
28936     /** false to disable window resize monitoring @type Boolean */
28937     this.monitorWindowResize = true;
28938     this.regions = {};
28939     this.addEvents({
28940         /**
28941          * @event layout
28942          * Fires when a layout is performed. 
28943          * @param {Roo.LayoutManager} this
28944          */
28945         "layout" : true,
28946         /**
28947          * @event regionresized
28948          * Fires when the user resizes a region. 
28949          * @param {Roo.LayoutRegion} region The resized region
28950          * @param {Number} newSize The new size (width for east/west, height for north/south)
28951          */
28952         "regionresized" : true,
28953         /**
28954          * @event regioncollapsed
28955          * Fires when a region is collapsed. 
28956          * @param {Roo.LayoutRegion} region The collapsed region
28957          */
28958         "regioncollapsed" : true,
28959         /**
28960          * @event regionexpanded
28961          * Fires when a region is expanded.  
28962          * @param {Roo.LayoutRegion} region The expanded region
28963          */
28964         "regionexpanded" : true
28965     });
28966     this.updating = false;
28967     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28968 };
28969
28970 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28971     /**
28972      * Returns true if this layout is currently being updated
28973      * @return {Boolean}
28974      */
28975     isUpdating : function(){
28976         return this.updating; 
28977     },
28978     
28979     /**
28980      * Suspend the LayoutManager from doing auto-layouts while
28981      * making multiple add or remove calls
28982      */
28983     beginUpdate : function(){
28984         this.updating = true;    
28985     },
28986     
28987     /**
28988      * Restore auto-layouts and optionally disable the manager from performing a layout
28989      * @param {Boolean} noLayout true to disable a layout update 
28990      */
28991     endUpdate : function(noLayout){
28992         this.updating = false;
28993         if(!noLayout){
28994             this.layout();
28995         }    
28996     },
28997     
28998     layout: function(){
28999         
29000     },
29001     
29002     onRegionResized : function(region, newSize){
29003         this.fireEvent("regionresized", region, newSize);
29004         this.layout();
29005     },
29006     
29007     onRegionCollapsed : function(region){
29008         this.fireEvent("regioncollapsed", region);
29009     },
29010     
29011     onRegionExpanded : function(region){
29012         this.fireEvent("regionexpanded", region);
29013     },
29014         
29015     /**
29016      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29017      * performs box-model adjustments.
29018      * @return {Object} The size as an object {width: (the width), height: (the height)}
29019      */
29020     getViewSize : function(){
29021         var size;
29022         if(this.el.dom != document.body){
29023             size = this.el.getSize();
29024         }else{
29025             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29026         }
29027         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29028         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29029         return size;
29030     },
29031     
29032     /**
29033      * Returns the Element this layout is bound to.
29034      * @return {Roo.Element}
29035      */
29036     getEl : function(){
29037         return this.el;
29038     },
29039     
29040     /**
29041      * Returns the specified region.
29042      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29043      * @return {Roo.LayoutRegion}
29044      */
29045     getRegion : function(target){
29046         return this.regions[target.toLowerCase()];
29047     },
29048     
29049     onWindowResize : function(){
29050         if(this.monitorWindowResize){
29051             this.layout();
29052         }
29053     }
29054 });/*
29055  * Based on:
29056  * Ext JS Library 1.1.1
29057  * Copyright(c) 2006-2007, Ext JS, LLC.
29058  *
29059  * Originally Released Under LGPL - original licence link has changed is not relivant.
29060  *
29061  * Fork - LGPL
29062  * <script type="text/javascript">
29063  */
29064 /**
29065  * @class Roo.BorderLayout
29066  * @extends Roo.LayoutManager
29067  * @children Roo.ContentPanel
29068  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29069  * please see: <br><br>
29070  * <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>
29071  * <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>
29072  * Example:
29073  <pre><code>
29074  var layout = new Roo.BorderLayout(document.body, {
29075     north: {
29076         initialSize: 25,
29077         titlebar: false
29078     },
29079     west: {
29080         split:true,
29081         initialSize: 200,
29082         minSize: 175,
29083         maxSize: 400,
29084         titlebar: true,
29085         collapsible: true
29086     },
29087     east: {
29088         split:true,
29089         initialSize: 202,
29090         minSize: 175,
29091         maxSize: 400,
29092         titlebar: true,
29093         collapsible: true
29094     },
29095     south: {
29096         split:true,
29097         initialSize: 100,
29098         minSize: 100,
29099         maxSize: 200,
29100         titlebar: true,
29101         collapsible: true
29102     },
29103     center: {
29104         titlebar: true,
29105         autoScroll:true,
29106         resizeTabs: true,
29107         minTabWidth: 50,
29108         preferredTabWidth: 150
29109     }
29110 });
29111
29112 // shorthand
29113 var CP = Roo.ContentPanel;
29114
29115 layout.beginUpdate();
29116 layout.add("north", new CP("north", "North"));
29117 layout.add("south", new CP("south", {title: "South", closable: true}));
29118 layout.add("west", new CP("west", {title: "West"}));
29119 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29120 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29121 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29122 layout.getRegion("center").showPanel("center1");
29123 layout.endUpdate();
29124 </code></pre>
29125
29126 <b>The container the layout is rendered into can be either the body element or any other element.
29127 If it is not the body element, the container needs to either be an absolute positioned element,
29128 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29129 the container size if it is not the body element.</b>
29130
29131 * @constructor
29132 * Create a new BorderLayout
29133 * @param {String/HTMLElement/Element} container The container this layout is bound to
29134 * @param {Object} config Configuration options
29135  */
29136 Roo.BorderLayout = function(container, config){
29137     config = config || {};
29138     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29139     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29140     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29141         var target = this.factory.validRegions[i];
29142         if(config[target]){
29143             this.addRegion(target, config[target]);
29144         }
29145     }
29146 };
29147
29148 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29149         
29150         /**
29151          * @cfg {Roo.LayoutRegion} east
29152          */
29153         /**
29154          * @cfg {Roo.LayoutRegion} west
29155          */
29156         /**
29157          * @cfg {Roo.LayoutRegion} north
29158          */
29159         /**
29160          * @cfg {Roo.LayoutRegion} south
29161          */
29162         /**
29163          * @cfg {Roo.LayoutRegion} center
29164          */
29165     /**
29166      * Creates and adds a new region if it doesn't already exist.
29167      * @param {String} target The target region key (north, south, east, west or center).
29168      * @param {Object} config The regions config object
29169      * @return {BorderLayoutRegion} The new region
29170      */
29171     addRegion : function(target, config){
29172         if(!this.regions[target]){
29173             var r = this.factory.create(target, this, config);
29174             this.bindRegion(target, r);
29175         }
29176         return this.regions[target];
29177     },
29178
29179     // private (kinda)
29180     bindRegion : function(name, r){
29181         this.regions[name] = r;
29182         r.on("visibilitychange", this.layout, this);
29183         r.on("paneladded", this.layout, this);
29184         r.on("panelremoved", this.layout, this);
29185         r.on("invalidated", this.layout, this);
29186         r.on("resized", this.onRegionResized, this);
29187         r.on("collapsed", this.onRegionCollapsed, this);
29188         r.on("expanded", this.onRegionExpanded, this);
29189     },
29190
29191     /**
29192      * Performs a layout update.
29193      */
29194     layout : function(){
29195         if(this.updating) {
29196             return;
29197         }
29198         var size = this.getViewSize();
29199         var w = size.width;
29200         var h = size.height;
29201         var centerW = w;
29202         var centerH = h;
29203         var centerY = 0;
29204         var centerX = 0;
29205         //var x = 0, y = 0;
29206
29207         var rs = this.regions;
29208         var north = rs["north"];
29209         var south = rs["south"]; 
29210         var west = rs["west"];
29211         var east = rs["east"];
29212         var center = rs["center"];
29213         //if(this.hideOnLayout){ // not supported anymore
29214             //c.el.setStyle("display", "none");
29215         //}
29216         if(north && north.isVisible()){
29217             var b = north.getBox();
29218             var m = north.getMargins();
29219             b.width = w - (m.left+m.right);
29220             b.x = m.left;
29221             b.y = m.top;
29222             centerY = b.height + b.y + m.bottom;
29223             centerH -= centerY;
29224             north.updateBox(this.safeBox(b));
29225         }
29226         if(south && south.isVisible()){
29227             var b = south.getBox();
29228             var m = south.getMargins();
29229             b.width = w - (m.left+m.right);
29230             b.x = m.left;
29231             var totalHeight = (b.height + m.top + m.bottom);
29232             b.y = h - totalHeight + m.top;
29233             centerH -= totalHeight;
29234             south.updateBox(this.safeBox(b));
29235         }
29236         if(west && west.isVisible()){
29237             var b = west.getBox();
29238             var m = west.getMargins();
29239             b.height = centerH - (m.top+m.bottom);
29240             b.x = m.left;
29241             b.y = centerY + m.top;
29242             var totalWidth = (b.width + m.left + m.right);
29243             centerX += totalWidth;
29244             centerW -= totalWidth;
29245             west.updateBox(this.safeBox(b));
29246         }
29247         if(east && east.isVisible()){
29248             var b = east.getBox();
29249             var m = east.getMargins();
29250             b.height = centerH - (m.top+m.bottom);
29251             var totalWidth = (b.width + m.left + m.right);
29252             b.x = w - totalWidth + m.left;
29253             b.y = centerY + m.top;
29254             centerW -= totalWidth;
29255             east.updateBox(this.safeBox(b));
29256         }
29257         if(center){
29258             var m = center.getMargins();
29259             var centerBox = {
29260                 x: centerX + m.left,
29261                 y: centerY + m.top,
29262                 width: centerW - (m.left+m.right),
29263                 height: centerH - (m.top+m.bottom)
29264             };
29265             //if(this.hideOnLayout){
29266                 //center.el.setStyle("display", "block");
29267             //}
29268             center.updateBox(this.safeBox(centerBox));
29269         }
29270         this.el.repaint();
29271         this.fireEvent("layout", this);
29272     },
29273
29274     // private
29275     safeBox : function(box){
29276         box.width = Math.max(0, box.width);
29277         box.height = Math.max(0, box.height);
29278         return box;
29279     },
29280
29281     /**
29282      * Adds a ContentPanel (or subclass) to this layout.
29283      * @param {String} target The target region key (north, south, east, west or center).
29284      * @param {Roo.ContentPanel} panel The panel to add
29285      * @return {Roo.ContentPanel} The added panel
29286      */
29287     add : function(target, panel){
29288          
29289         target = target.toLowerCase();
29290         return this.regions[target].add(panel);
29291     },
29292
29293     /**
29294      * Remove a ContentPanel (or subclass) to this layout.
29295      * @param {String} target The target region key (north, south, east, west or center).
29296      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29297      * @return {Roo.ContentPanel} The removed panel
29298      */
29299     remove : function(target, panel){
29300         target = target.toLowerCase();
29301         return this.regions[target].remove(panel);
29302     },
29303
29304     /**
29305      * Searches all regions for a panel with the specified id
29306      * @param {String} panelId
29307      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29308      */
29309     findPanel : function(panelId){
29310         var rs = this.regions;
29311         for(var target in rs){
29312             if(typeof rs[target] != "function"){
29313                 var p = rs[target].getPanel(panelId);
29314                 if(p){
29315                     return p;
29316                 }
29317             }
29318         }
29319         return null;
29320     },
29321
29322     /**
29323      * Searches all regions for a panel with the specified id and activates (shows) it.
29324      * @param {String/ContentPanel} panelId The panels id or the panel itself
29325      * @return {Roo.ContentPanel} The shown panel or null
29326      */
29327     showPanel : function(panelId) {
29328       var rs = this.regions;
29329       for(var target in rs){
29330          var r = rs[target];
29331          if(typeof r != "function"){
29332             if(r.hasPanel(panelId)){
29333                return r.showPanel(panelId);
29334             }
29335          }
29336       }
29337       return null;
29338    },
29339
29340    /**
29341      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29342      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29343      */
29344     restoreState : function(provider){
29345         if(!provider){
29346             provider = Roo.state.Manager;
29347         }
29348         var sm = new Roo.LayoutStateManager();
29349         sm.init(this, provider);
29350     },
29351
29352     /**
29353      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29354      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29355      * a valid ContentPanel config object.  Example:
29356      * <pre><code>
29357 // Create the main layout
29358 var layout = new Roo.BorderLayout('main-ct', {
29359     west: {
29360         split:true,
29361         minSize: 175,
29362         titlebar: true
29363     },
29364     center: {
29365         title:'Components'
29366     }
29367 }, 'main-ct');
29368
29369 // Create and add multiple ContentPanels at once via configs
29370 layout.batchAdd({
29371    west: {
29372        id: 'source-files',
29373        autoCreate:true,
29374        title:'Ext Source Files',
29375        autoScroll:true,
29376        fitToFrame:true
29377    },
29378    center : {
29379        el: cview,
29380        autoScroll:true,
29381        fitToFrame:true,
29382        toolbar: tb,
29383        resizeEl:'cbody'
29384    }
29385 });
29386 </code></pre>
29387      * @param {Object} regions An object containing ContentPanel configs by region name
29388      */
29389     batchAdd : function(regions){
29390         this.beginUpdate();
29391         for(var rname in regions){
29392             var lr = this.regions[rname];
29393             if(lr){
29394                 this.addTypedPanels(lr, regions[rname]);
29395             }
29396         }
29397         this.endUpdate();
29398     },
29399
29400     // private
29401     addTypedPanels : function(lr, ps){
29402         if(typeof ps == 'string'){
29403             lr.add(new Roo.ContentPanel(ps));
29404         }
29405         else if(ps instanceof Array){
29406             for(var i =0, len = ps.length; i < len; i++){
29407                 this.addTypedPanels(lr, ps[i]);
29408             }
29409         }
29410         else if(!ps.events){ // raw config?
29411             var el = ps.el;
29412             delete ps.el; // prevent conflict
29413             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29414         }
29415         else {  // panel object assumed!
29416             lr.add(ps);
29417         }
29418     },
29419     /**
29420      * Adds a xtype elements to the layout.
29421      * <pre><code>
29422
29423 layout.addxtype({
29424        xtype : 'ContentPanel',
29425        region: 'west',
29426        items: [ .... ]
29427    }
29428 );
29429
29430 layout.addxtype({
29431         xtype : 'NestedLayoutPanel',
29432         region: 'west',
29433         layout: {
29434            center: { },
29435            west: { }   
29436         },
29437         items : [ ... list of content panels or nested layout panels.. ]
29438    }
29439 );
29440 </code></pre>
29441      * @param {Object} cfg Xtype definition of item to add.
29442      */
29443     addxtype : function(cfg)
29444     {
29445         // basically accepts a pannel...
29446         // can accept a layout region..!?!?
29447         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29448         
29449         if (!cfg.xtype.match(/Panel$/)) {
29450             return false;
29451         }
29452         var ret = false;
29453         
29454         if (typeof(cfg.region) == 'undefined') {
29455             Roo.log("Failed to add Panel, region was not set");
29456             Roo.log(cfg);
29457             return false;
29458         }
29459         var region = cfg.region;
29460         delete cfg.region;
29461         
29462           
29463         var xitems = [];
29464         if (cfg.items) {
29465             xitems = cfg.items;
29466             delete cfg.items;
29467         }
29468         var nb = false;
29469         
29470         switch(cfg.xtype) 
29471         {
29472             case 'ContentPanel':  // ContentPanel (el, cfg)
29473             case 'ScrollPanel':  // ContentPanel (el, cfg)
29474             case 'ViewPanel': 
29475                 if(cfg.autoCreate) {
29476                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29477                 } else {
29478                     var el = this.el.createChild();
29479                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29480                 }
29481                 
29482                 this.add(region, ret);
29483                 break;
29484             
29485             
29486             case 'TreePanel': // our new panel!
29487                 cfg.el = this.el.createChild();
29488                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29489                 this.add(region, ret);
29490                 break;
29491             
29492             case 'NestedLayoutPanel': 
29493                 // create a new Layout (which is  a Border Layout...
29494                 var el = this.el.createChild();
29495                 var clayout = cfg.layout;
29496                 delete cfg.layout;
29497                 clayout.items   = clayout.items  || [];
29498                 // replace this exitems with the clayout ones..
29499                 xitems = clayout.items;
29500                  
29501                 
29502                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29503                     cfg.background = false;
29504                 }
29505                 var layout = new Roo.BorderLayout(el, clayout);
29506                 
29507                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29508                 //console.log('adding nested layout panel '  + cfg.toSource());
29509                 this.add(region, ret);
29510                 nb = {}; /// find first...
29511                 break;
29512                 
29513             case 'GridPanel': 
29514             
29515                 // needs grid and region
29516                 
29517                 //var el = this.getRegion(region).el.createChild();
29518                 var el = this.el.createChild();
29519                 // create the grid first...
29520                 
29521                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29522                 delete cfg.grid;
29523                 if (region == 'center' && this.active ) {
29524                     cfg.background = false;
29525                 }
29526                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29527                 
29528                 this.add(region, ret);
29529                 if (cfg.background) {
29530                     ret.on('activate', function(gp) {
29531                         if (!gp.grid.rendered) {
29532                             gp.grid.render();
29533                         }
29534                     });
29535                 } else {
29536                     grid.render();
29537                 }
29538                 break;
29539            
29540            
29541            
29542                 
29543                 
29544                 
29545             default:
29546                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29547                     
29548                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29549                     this.add(region, ret);
29550                 } else {
29551                 
29552                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29553                     return null;
29554                 }
29555                 
29556              // GridPanel (grid, cfg)
29557             
29558         }
29559         this.beginUpdate();
29560         // add children..
29561         var region = '';
29562         var abn = {};
29563         Roo.each(xitems, function(i)  {
29564             region = nb && i.region ? i.region : false;
29565             
29566             var add = ret.addxtype(i);
29567            
29568             if (region) {
29569                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29570                 if (!i.background) {
29571                     abn[region] = nb[region] ;
29572                 }
29573             }
29574             
29575         });
29576         this.endUpdate();
29577
29578         // make the last non-background panel active..
29579         //if (nb) { Roo.log(abn); }
29580         if (nb) {
29581             
29582             for(var r in abn) {
29583                 region = this.getRegion(r);
29584                 if (region) {
29585                     // tried using nb[r], but it does not work..
29586                      
29587                     region.showPanel(abn[r]);
29588                    
29589                 }
29590             }
29591         }
29592         return ret;
29593         
29594     }
29595 });
29596
29597 /**
29598  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29599  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29600  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29601  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29602  * <pre><code>
29603 // shorthand
29604 var CP = Roo.ContentPanel;
29605
29606 var layout = Roo.BorderLayout.create({
29607     north: {
29608         initialSize: 25,
29609         titlebar: false,
29610         panels: [new CP("north", "North")]
29611     },
29612     west: {
29613         split:true,
29614         initialSize: 200,
29615         minSize: 175,
29616         maxSize: 400,
29617         titlebar: true,
29618         collapsible: true,
29619         panels: [new CP("west", {title: "West"})]
29620     },
29621     east: {
29622         split:true,
29623         initialSize: 202,
29624         minSize: 175,
29625         maxSize: 400,
29626         titlebar: true,
29627         collapsible: true,
29628         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29629     },
29630     south: {
29631         split:true,
29632         initialSize: 100,
29633         minSize: 100,
29634         maxSize: 200,
29635         titlebar: true,
29636         collapsible: true,
29637         panels: [new CP("south", {title: "South", closable: true})]
29638     },
29639     center: {
29640         titlebar: true,
29641         autoScroll:true,
29642         resizeTabs: true,
29643         minTabWidth: 50,
29644         preferredTabWidth: 150,
29645         panels: [
29646             new CP("center1", {title: "Close Me", closable: true}),
29647             new CP("center2", {title: "Center Panel", closable: false})
29648         ]
29649     }
29650 }, document.body);
29651
29652 layout.getRegion("center").showPanel("center1");
29653 </code></pre>
29654  * @param config
29655  * @param targetEl
29656  */
29657 Roo.BorderLayout.create = function(config, targetEl){
29658     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29659     layout.beginUpdate();
29660     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29661     for(var j = 0, jlen = regions.length; j < jlen; j++){
29662         var lr = regions[j];
29663         if(layout.regions[lr] && config[lr].panels){
29664             var r = layout.regions[lr];
29665             var ps = config[lr].panels;
29666             layout.addTypedPanels(r, ps);
29667         }
29668     }
29669     layout.endUpdate();
29670     return layout;
29671 };
29672
29673 // private
29674 Roo.BorderLayout.RegionFactory = {
29675     // private
29676     validRegions : ["north","south","east","west","center"],
29677
29678     // private
29679     create : function(target, mgr, config){
29680         target = target.toLowerCase();
29681         if(config.lightweight || config.basic){
29682             return new Roo.BasicLayoutRegion(mgr, config, target);
29683         }
29684         switch(target){
29685             case "north":
29686                 return new Roo.NorthLayoutRegion(mgr, config);
29687             case "south":
29688                 return new Roo.SouthLayoutRegion(mgr, config);
29689             case "east":
29690                 return new Roo.EastLayoutRegion(mgr, config);
29691             case "west":
29692                 return new Roo.WestLayoutRegion(mgr, config);
29693             case "center":
29694                 return new Roo.CenterLayoutRegion(mgr, config);
29695         }
29696         throw 'Layout region "'+target+'" not supported.';
29697     }
29698 };/*
29699  * Based on:
29700  * Ext JS Library 1.1.1
29701  * Copyright(c) 2006-2007, Ext JS, LLC.
29702  *
29703  * Originally Released Under LGPL - original licence link has changed is not relivant.
29704  *
29705  * Fork - LGPL
29706  * <script type="text/javascript">
29707  */
29708  
29709 /**
29710  * @class Roo.BasicLayoutRegion
29711  * @extends Roo.util.Observable
29712  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29713  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29714  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29715  */
29716 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29717     this.mgr = mgr;
29718     this.position  = pos;
29719     this.events = {
29720         /**
29721          * @scope Roo.BasicLayoutRegion
29722          */
29723         
29724         /**
29725          * @event beforeremove
29726          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29727          * @param {Roo.LayoutRegion} this
29728          * @param {Roo.ContentPanel} panel The panel
29729          * @param {Object} e The cancel event object
29730          */
29731         "beforeremove" : true,
29732         /**
29733          * @event invalidated
29734          * Fires when the layout for this region is changed.
29735          * @param {Roo.LayoutRegion} this
29736          */
29737         "invalidated" : true,
29738         /**
29739          * @event visibilitychange
29740          * Fires when this region is shown or hidden 
29741          * @param {Roo.LayoutRegion} this
29742          * @param {Boolean} visibility true or false
29743          */
29744         "visibilitychange" : true,
29745         /**
29746          * @event paneladded
29747          * Fires when a panel is added. 
29748          * @param {Roo.LayoutRegion} this
29749          * @param {Roo.ContentPanel} panel The panel
29750          */
29751         "paneladded" : true,
29752         /**
29753          * @event panelremoved
29754          * Fires when a panel is removed. 
29755          * @param {Roo.LayoutRegion} this
29756          * @param {Roo.ContentPanel} panel The panel
29757          */
29758         "panelremoved" : true,
29759         /**
29760          * @event beforecollapse
29761          * Fires when this region before collapse.
29762          * @param {Roo.LayoutRegion} this
29763          */
29764         "beforecollapse" : true,
29765         /**
29766          * @event collapsed
29767          * Fires when this region is collapsed.
29768          * @param {Roo.LayoutRegion} this
29769          */
29770         "collapsed" : true,
29771         /**
29772          * @event expanded
29773          * Fires when this region is expanded.
29774          * @param {Roo.LayoutRegion} this
29775          */
29776         "expanded" : true,
29777         /**
29778          * @event slideshow
29779          * Fires when this region is slid into view.
29780          * @param {Roo.LayoutRegion} this
29781          */
29782         "slideshow" : true,
29783         /**
29784          * @event slidehide
29785          * Fires when this region slides out of view. 
29786          * @param {Roo.LayoutRegion} this
29787          */
29788         "slidehide" : true,
29789         /**
29790          * @event panelactivated
29791          * Fires when a panel is activated. 
29792          * @param {Roo.LayoutRegion} this
29793          * @param {Roo.ContentPanel} panel The activated panel
29794          */
29795         "panelactivated" : true,
29796         /**
29797          * @event resized
29798          * Fires when the user resizes this region. 
29799          * @param {Roo.LayoutRegion} this
29800          * @param {Number} newSize The new size (width for east/west, height for north/south)
29801          */
29802         "resized" : true
29803     };
29804     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29805     this.panels = new Roo.util.MixedCollection();
29806     this.panels.getKey = this.getPanelId.createDelegate(this);
29807     this.box = null;
29808     this.activePanel = null;
29809     // ensure listeners are added...
29810     
29811     if (config.listeners || config.events) {
29812         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29813             listeners : config.listeners || {},
29814             events : config.events || {}
29815         });
29816     }
29817     
29818     if(skipConfig !== true){
29819         this.applyConfig(config);
29820     }
29821 };
29822
29823 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29824     getPanelId : function(p){
29825         return p.getId();
29826     },
29827     
29828     applyConfig : function(config){
29829         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29830         this.config = config;
29831         
29832     },
29833     
29834     /**
29835      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29836      * the width, for horizontal (north, south) the height.
29837      * @param {Number} newSize The new width or height
29838      */
29839     resizeTo : function(newSize){
29840         var el = this.el ? this.el :
29841                  (this.activePanel ? this.activePanel.getEl() : null);
29842         if(el){
29843             switch(this.position){
29844                 case "east":
29845                 case "west":
29846                     el.setWidth(newSize);
29847                     this.fireEvent("resized", this, newSize);
29848                 break;
29849                 case "north":
29850                 case "south":
29851                     el.setHeight(newSize);
29852                     this.fireEvent("resized", this, newSize);
29853                 break;                
29854             }
29855         }
29856     },
29857     
29858     getBox : function(){
29859         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29860     },
29861     
29862     getMargins : function(){
29863         return this.margins;
29864     },
29865     
29866     updateBox : function(box){
29867         this.box = box;
29868         var el = this.activePanel.getEl();
29869         el.dom.style.left = box.x + "px";
29870         el.dom.style.top = box.y + "px";
29871         this.activePanel.setSize(box.width, box.height);
29872     },
29873     
29874     /**
29875      * Returns the container element for this region.
29876      * @return {Roo.Element}
29877      */
29878     getEl : function(){
29879         return this.activePanel;
29880     },
29881     
29882     /**
29883      * Returns true if this region is currently visible.
29884      * @return {Boolean}
29885      */
29886     isVisible : function(){
29887         return this.activePanel ? true : false;
29888     },
29889     
29890     setActivePanel : function(panel){
29891         panel = this.getPanel(panel);
29892         if(this.activePanel && this.activePanel != panel){
29893             this.activePanel.setActiveState(false);
29894             this.activePanel.getEl().setLeftTop(-10000,-10000);
29895         }
29896         this.activePanel = panel;
29897         panel.setActiveState(true);
29898         if(this.box){
29899             panel.setSize(this.box.width, this.box.height);
29900         }
29901         this.fireEvent("panelactivated", this, panel);
29902         this.fireEvent("invalidated");
29903     },
29904     
29905     /**
29906      * Show the specified panel.
29907      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29908      * @return {Roo.ContentPanel} The shown panel or null
29909      */
29910     showPanel : function(panel){
29911         if(panel = this.getPanel(panel)){
29912             this.setActivePanel(panel);
29913         }
29914         return panel;
29915     },
29916     
29917     /**
29918      * Get the active panel for this region.
29919      * @return {Roo.ContentPanel} The active panel or null
29920      */
29921     getActivePanel : function(){
29922         return this.activePanel;
29923     },
29924     
29925     /**
29926      * Add the passed ContentPanel(s)
29927      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29928      * @return {Roo.ContentPanel} The panel added (if only one was added)
29929      */
29930     add : function(panel){
29931         if(arguments.length > 1){
29932             for(var i = 0, len = arguments.length; i < len; i++) {
29933                 this.add(arguments[i]);
29934             }
29935             return null;
29936         }
29937         if(this.hasPanel(panel)){
29938             this.showPanel(panel);
29939             return panel;
29940         }
29941         var el = panel.getEl();
29942         if(el.dom.parentNode != this.mgr.el.dom){
29943             this.mgr.el.dom.appendChild(el.dom);
29944         }
29945         if(panel.setRegion){
29946             panel.setRegion(this);
29947         }
29948         this.panels.add(panel);
29949         el.setStyle("position", "absolute");
29950         if(!panel.background){
29951             this.setActivePanel(panel);
29952             if(this.config.initialSize && this.panels.getCount()==1){
29953                 this.resizeTo(this.config.initialSize);
29954             }
29955         }
29956         this.fireEvent("paneladded", this, panel);
29957         return panel;
29958     },
29959     
29960     /**
29961      * Returns true if the panel is in this region.
29962      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29963      * @return {Boolean}
29964      */
29965     hasPanel : function(panel){
29966         if(typeof panel == "object"){ // must be panel obj
29967             panel = panel.getId();
29968         }
29969         return this.getPanel(panel) ? true : false;
29970     },
29971     
29972     /**
29973      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29974      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29975      * @param {Boolean} preservePanel Overrides the config preservePanel option
29976      * @return {Roo.ContentPanel} The panel that was removed
29977      */
29978     remove : function(panel, preservePanel){
29979         panel = this.getPanel(panel);
29980         if(!panel){
29981             return null;
29982         }
29983         var e = {};
29984         this.fireEvent("beforeremove", this, panel, e);
29985         if(e.cancel === true){
29986             return null;
29987         }
29988         var panelId = panel.getId();
29989         this.panels.removeKey(panelId);
29990         return panel;
29991     },
29992     
29993     /**
29994      * Returns the panel specified or null if it's not in this region.
29995      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29996      * @return {Roo.ContentPanel}
29997      */
29998     getPanel : function(id){
29999         if(typeof id == "object"){ // must be panel obj
30000             return id;
30001         }
30002         return this.panels.get(id);
30003     },
30004     
30005     /**
30006      * Returns this regions position (north/south/east/west/center).
30007      * @return {String} 
30008      */
30009     getPosition: function(){
30010         return this.position;    
30011     }
30012 });/*
30013  * Based on:
30014  * Ext JS Library 1.1.1
30015  * Copyright(c) 2006-2007, Ext JS, LLC.
30016  *
30017  * Originally Released Under LGPL - original licence link has changed is not relivant.
30018  *
30019  * Fork - LGPL
30020  * <script type="text/javascript">
30021  */
30022  
30023 /**
30024  * @class Roo.LayoutRegion
30025  * @extends Roo.BasicLayoutRegion
30026  * This class represents a region in a layout manager.
30027  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30028  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30029  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30030  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30031  * @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})
30032  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
30033  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30034  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30035  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30036  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30037  * @cfg {String}    title           The title for the region (overrides panel titles)
30038  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30039  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30040  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30041  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30042  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30043  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30044  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30045  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30046  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30047  * @cfg {Boolean}   showPin         True to show a pin button
30048  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30049  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30050  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30051  * @cfg {Number}    width           For East/West panels
30052  * @cfg {Number}    height          For North/South panels
30053  * @cfg {Boolean}   split           To show the splitter
30054  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30055  */
30056 Roo.LayoutRegion = function(mgr, config, pos){
30057     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30058     var dh = Roo.DomHelper;
30059     /** This region's container element 
30060     * @type Roo.Element */
30061     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30062     /** This region's title element 
30063     * @type Roo.Element */
30064
30065     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30066         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30067         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30068     ]}, true);
30069     this.titleEl.enableDisplayMode();
30070     /** This region's title text element 
30071     * @type HTMLElement */
30072     this.titleTextEl = this.titleEl.dom.firstChild;
30073     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30074     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30075     this.closeBtn.enableDisplayMode();
30076     this.closeBtn.on("click", this.closeClicked, this);
30077     this.closeBtn.hide();
30078
30079     this.createBody(config);
30080     this.visible = true;
30081     this.collapsed = false;
30082
30083     if(config.hideWhenEmpty){
30084         this.hide();
30085         this.on("paneladded", this.validateVisibility, this);
30086         this.on("panelremoved", this.validateVisibility, this);
30087     }
30088     this.applyConfig(config);
30089 };
30090
30091 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30092
30093     createBody : function(){
30094         /** This region's body element 
30095         * @type Roo.Element */
30096         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30097     },
30098
30099     applyConfig : function(c){
30100         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30101             var dh = Roo.DomHelper;
30102             if(c.titlebar !== false){
30103                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30104                 this.collapseBtn.on("click", this.collapse, this);
30105                 this.collapseBtn.enableDisplayMode();
30106
30107                 if(c.showPin === true || this.showPin){
30108                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30109                     this.stickBtn.enableDisplayMode();
30110                     this.stickBtn.on("click", this.expand, this);
30111                     this.stickBtn.hide();
30112                 }
30113             }
30114             /** This region's collapsed element
30115             * @type Roo.Element */
30116             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30117                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30118             ]}, true);
30119             if(c.floatable !== false){
30120                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30121                this.collapsedEl.on("click", this.collapseClick, this);
30122             }
30123
30124             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30125                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30126                    id: "message", unselectable: "on", style:{"float":"left"}});
30127                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30128              }
30129             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30130             this.expandBtn.on("click", this.expand, this);
30131         }
30132         if(this.collapseBtn){
30133             this.collapseBtn.setVisible(c.collapsible == true);
30134         }
30135         this.cmargins = c.cmargins || this.cmargins ||
30136                          (this.position == "west" || this.position == "east" ?
30137                              {top: 0, left: 2, right:2, bottom: 0} :
30138                              {top: 2, left: 0, right:0, bottom: 2});
30139         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30140         this.bottomTabs = c.tabPosition != "top";
30141         this.autoScroll = c.autoScroll || false;
30142         if(this.autoScroll){
30143             this.bodyEl.setStyle("overflow", "auto");
30144         }else{
30145             this.bodyEl.setStyle("overflow", "hidden");
30146         }
30147         //if(c.titlebar !== false){
30148             if((!c.titlebar && !c.title) || c.titlebar === false){
30149                 this.titleEl.hide();
30150             }else{
30151                 this.titleEl.show();
30152                 if(c.title){
30153                     this.titleTextEl.innerHTML = c.title;
30154                 }
30155             }
30156         //}
30157         this.duration = c.duration || .30;
30158         this.slideDuration = c.slideDuration || .45;
30159         this.config = c;
30160         if(c.collapsed){
30161             this.collapse(true);
30162         }
30163         if(c.hidden){
30164             this.hide();
30165         }
30166     },
30167     /**
30168      * Returns true if this region is currently visible.
30169      * @return {Boolean}
30170      */
30171     isVisible : function(){
30172         return this.visible;
30173     },
30174
30175     /**
30176      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30177      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30178      */
30179     setCollapsedTitle : function(title){
30180         title = title || "&#160;";
30181         if(this.collapsedTitleTextEl){
30182             this.collapsedTitleTextEl.innerHTML = title;
30183         }
30184     },
30185
30186     getBox : function(){
30187         var b;
30188         if(!this.collapsed){
30189             b = this.el.getBox(false, true);
30190         }else{
30191             b = this.collapsedEl.getBox(false, true);
30192         }
30193         return b;
30194     },
30195
30196     getMargins : function(){
30197         return this.collapsed ? this.cmargins : this.margins;
30198     },
30199
30200     highlight : function(){
30201         this.el.addClass("x-layout-panel-dragover");
30202     },
30203
30204     unhighlight : function(){
30205         this.el.removeClass("x-layout-panel-dragover");
30206     },
30207
30208     updateBox : function(box){
30209         this.box = box;
30210         if(!this.collapsed){
30211             this.el.dom.style.left = box.x + "px";
30212             this.el.dom.style.top = box.y + "px";
30213             this.updateBody(box.width, box.height);
30214         }else{
30215             this.collapsedEl.dom.style.left = box.x + "px";
30216             this.collapsedEl.dom.style.top = box.y + "px";
30217             this.collapsedEl.setSize(box.width, box.height);
30218         }
30219         if(this.tabs){
30220             this.tabs.autoSizeTabs();
30221         }
30222     },
30223
30224     updateBody : function(w, h){
30225         if(w !== null){
30226             this.el.setWidth(w);
30227             w -= this.el.getBorderWidth("rl");
30228             if(this.config.adjustments){
30229                 w += this.config.adjustments[0];
30230             }
30231         }
30232         if(h !== null){
30233             this.el.setHeight(h);
30234             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30235             h -= this.el.getBorderWidth("tb");
30236             if(this.config.adjustments){
30237                 h += this.config.adjustments[1];
30238             }
30239             this.bodyEl.setHeight(h);
30240             if(this.tabs){
30241                 h = this.tabs.syncHeight(h);
30242             }
30243         }
30244         if(this.panelSize){
30245             w = w !== null ? w : this.panelSize.width;
30246             h = h !== null ? h : this.panelSize.height;
30247         }
30248         if(this.activePanel){
30249             var el = this.activePanel.getEl();
30250             w = w !== null ? w : el.getWidth();
30251             h = h !== null ? h : el.getHeight();
30252             this.panelSize = {width: w, height: h};
30253             this.activePanel.setSize(w, h);
30254         }
30255         if(Roo.isIE && this.tabs){
30256             this.tabs.el.repaint();
30257         }
30258     },
30259
30260     /**
30261      * Returns the container element for this region.
30262      * @return {Roo.Element}
30263      */
30264     getEl : function(){
30265         return this.el;
30266     },
30267
30268     /**
30269      * Hides this region.
30270      */
30271     hide : function(){
30272         if(!this.collapsed){
30273             this.el.dom.style.left = "-2000px";
30274             this.el.hide();
30275         }else{
30276             this.collapsedEl.dom.style.left = "-2000px";
30277             this.collapsedEl.hide();
30278         }
30279         this.visible = false;
30280         this.fireEvent("visibilitychange", this, false);
30281     },
30282
30283     /**
30284      * Shows this region if it was previously hidden.
30285      */
30286     show : function(){
30287         if(!this.collapsed){
30288             this.el.show();
30289         }else{
30290             this.collapsedEl.show();
30291         }
30292         this.visible = true;
30293         this.fireEvent("visibilitychange", this, true);
30294     },
30295
30296     closeClicked : function(){
30297         if(this.activePanel){
30298             this.remove(this.activePanel);
30299         }
30300     },
30301
30302     collapseClick : function(e){
30303         if(this.isSlid){
30304            e.stopPropagation();
30305            this.slideIn();
30306         }else{
30307            e.stopPropagation();
30308            this.slideOut();
30309         }
30310     },
30311
30312     /**
30313      * Collapses this region.
30314      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30315      */
30316     collapse : function(skipAnim, skipCheck){
30317         if(this.collapsed) {
30318             return;
30319         }
30320         
30321         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30322             
30323             this.collapsed = true;
30324             if(this.split){
30325                 this.split.el.hide();
30326             }
30327             if(this.config.animate && skipAnim !== true){
30328                 this.fireEvent("invalidated", this);
30329                 this.animateCollapse();
30330             }else{
30331                 this.el.setLocation(-20000,-20000);
30332                 this.el.hide();
30333                 this.collapsedEl.show();
30334                 this.fireEvent("collapsed", this);
30335                 this.fireEvent("invalidated", this);
30336             }
30337         }
30338         
30339     },
30340
30341     animateCollapse : function(){
30342         // overridden
30343     },
30344
30345     /**
30346      * Expands this region if it was previously collapsed.
30347      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30348      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30349      */
30350     expand : function(e, skipAnim){
30351         if(e) {
30352             e.stopPropagation();
30353         }
30354         if(!this.collapsed || this.el.hasActiveFx()) {
30355             return;
30356         }
30357         if(this.isSlid){
30358             this.afterSlideIn();
30359             skipAnim = true;
30360         }
30361         this.collapsed = false;
30362         if(this.config.animate && skipAnim !== true){
30363             this.animateExpand();
30364         }else{
30365             this.el.show();
30366             if(this.split){
30367                 this.split.el.show();
30368             }
30369             this.collapsedEl.setLocation(-2000,-2000);
30370             this.collapsedEl.hide();
30371             this.fireEvent("invalidated", this);
30372             this.fireEvent("expanded", this);
30373         }
30374     },
30375
30376     animateExpand : function(){
30377         // overridden
30378     },
30379
30380     initTabs : function()
30381     {
30382         this.bodyEl.setStyle("overflow", "hidden");
30383         var ts = new Roo.TabPanel(
30384                 this.bodyEl.dom,
30385                 {
30386                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30387                     disableTooltips: this.config.disableTabTips,
30388                     toolbar : this.config.toolbar
30389                 }
30390         );
30391         if(this.config.hideTabs){
30392             ts.stripWrap.setDisplayed(false);
30393         }
30394         this.tabs = ts;
30395         ts.resizeTabs = this.config.resizeTabs === true;
30396         ts.minTabWidth = this.config.minTabWidth || 40;
30397         ts.maxTabWidth = this.config.maxTabWidth || 250;
30398         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30399         ts.monitorResize = false;
30400         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30401         ts.bodyEl.addClass('x-layout-tabs-body');
30402         this.panels.each(this.initPanelAsTab, this);
30403     },
30404
30405     initPanelAsTab : function(panel){
30406         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30407                     this.config.closeOnTab && panel.isClosable());
30408         if(panel.tabTip !== undefined){
30409             ti.setTooltip(panel.tabTip);
30410         }
30411         ti.on("activate", function(){
30412               this.setActivePanel(panel);
30413         }, this);
30414         if(this.config.closeOnTab){
30415             ti.on("beforeclose", function(t, e){
30416                 e.cancel = true;
30417                 this.remove(panel);
30418             }, this);
30419         }
30420         return ti;
30421     },
30422
30423     updatePanelTitle : function(panel, title){
30424         if(this.activePanel == panel){
30425             this.updateTitle(title);
30426         }
30427         if(this.tabs){
30428             var ti = this.tabs.getTab(panel.getEl().id);
30429             ti.setText(title);
30430             if(panel.tabTip !== undefined){
30431                 ti.setTooltip(panel.tabTip);
30432             }
30433         }
30434     },
30435
30436     updateTitle : function(title){
30437         if(this.titleTextEl && !this.config.title){
30438             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30439         }
30440     },
30441
30442     setActivePanel : function(panel){
30443         panel = this.getPanel(panel);
30444         if(this.activePanel && this.activePanel != panel){
30445             this.activePanel.setActiveState(false);
30446         }
30447         this.activePanel = panel;
30448         panel.setActiveState(true);
30449         if(this.panelSize){
30450             panel.setSize(this.panelSize.width, this.panelSize.height);
30451         }
30452         if(this.closeBtn){
30453             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30454         }
30455         this.updateTitle(panel.getTitle());
30456         if(this.tabs){
30457             this.fireEvent("invalidated", this);
30458         }
30459         this.fireEvent("panelactivated", this, panel);
30460     },
30461
30462     /**
30463      * Shows the specified panel.
30464      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30465      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30466      */
30467     showPanel : function(panel)
30468     {
30469         panel = this.getPanel(panel);
30470         if(panel){
30471             if(this.tabs){
30472                 var tab = this.tabs.getTab(panel.getEl().id);
30473                 if(tab.isHidden()){
30474                     this.tabs.unhideTab(tab.id);
30475                 }
30476                 tab.activate();
30477             }else{
30478                 this.setActivePanel(panel);
30479             }
30480         }
30481         return panel;
30482     },
30483
30484     /**
30485      * Get the active panel for this region.
30486      * @return {Roo.ContentPanel} The active panel or null
30487      */
30488     getActivePanel : function(){
30489         return this.activePanel;
30490     },
30491
30492     validateVisibility : function(){
30493         if(this.panels.getCount() < 1){
30494             this.updateTitle("&#160;");
30495             this.closeBtn.hide();
30496             this.hide();
30497         }else{
30498             if(!this.isVisible()){
30499                 this.show();
30500             }
30501         }
30502     },
30503
30504     /**
30505      * Adds the passed ContentPanel(s) to this region.
30506      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30507      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30508      */
30509     add : function(panel){
30510         if(arguments.length > 1){
30511             for(var i = 0, len = arguments.length; i < len; i++) {
30512                 this.add(arguments[i]);
30513             }
30514             return null;
30515         }
30516         if(this.hasPanel(panel)){
30517             this.showPanel(panel);
30518             return panel;
30519         }
30520         panel.setRegion(this);
30521         this.panels.add(panel);
30522         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30523             this.bodyEl.dom.appendChild(panel.getEl().dom);
30524             if(panel.background !== true){
30525                 this.setActivePanel(panel);
30526             }
30527             this.fireEvent("paneladded", this, panel);
30528             return panel;
30529         }
30530         if(!this.tabs){
30531             this.initTabs();
30532         }else{
30533             this.initPanelAsTab(panel);
30534         }
30535         if(panel.background !== true){
30536             this.tabs.activate(panel.getEl().id);
30537         }
30538         this.fireEvent("paneladded", this, panel);
30539         return panel;
30540     },
30541
30542     /**
30543      * Hides the tab for the specified panel.
30544      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30545      */
30546     hidePanel : function(panel){
30547         if(this.tabs && (panel = this.getPanel(panel))){
30548             this.tabs.hideTab(panel.getEl().id);
30549         }
30550     },
30551
30552     /**
30553      * Unhides the tab for a previously hidden panel.
30554      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30555      */
30556     unhidePanel : function(panel){
30557         if(this.tabs && (panel = this.getPanel(panel))){
30558             this.tabs.unhideTab(panel.getEl().id);
30559         }
30560     },
30561
30562     clearPanels : function(){
30563         while(this.panels.getCount() > 0){
30564              this.remove(this.panels.first());
30565         }
30566     },
30567
30568     /**
30569      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30570      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30571      * @param {Boolean} preservePanel Overrides the config preservePanel option
30572      * @return {Roo.ContentPanel} The panel that was removed
30573      */
30574     remove : function(panel, preservePanel){
30575         panel = this.getPanel(panel);
30576         if(!panel){
30577             return null;
30578         }
30579         var e = {};
30580         this.fireEvent("beforeremove", this, panel, e);
30581         if(e.cancel === true){
30582             return null;
30583         }
30584         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30585         var panelId = panel.getId();
30586         this.panels.removeKey(panelId);
30587         if(preservePanel){
30588             document.body.appendChild(panel.getEl().dom);
30589         }
30590         if(this.tabs){
30591             this.tabs.removeTab(panel.getEl().id);
30592         }else if (!preservePanel){
30593             this.bodyEl.dom.removeChild(panel.getEl().dom);
30594         }
30595         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30596             var p = this.panels.first();
30597             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30598             tempEl.appendChild(p.getEl().dom);
30599             this.bodyEl.update("");
30600             this.bodyEl.dom.appendChild(p.getEl().dom);
30601             tempEl = null;
30602             this.updateTitle(p.getTitle());
30603             this.tabs = null;
30604             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30605             this.setActivePanel(p);
30606         }
30607         panel.setRegion(null);
30608         if(this.activePanel == panel){
30609             this.activePanel = null;
30610         }
30611         if(this.config.autoDestroy !== false && preservePanel !== true){
30612             try{panel.destroy();}catch(e){}
30613         }
30614         this.fireEvent("panelremoved", this, panel);
30615         return panel;
30616     },
30617
30618     /**
30619      * Returns the TabPanel component used by this region
30620      * @return {Roo.TabPanel}
30621      */
30622     getTabs : function(){
30623         return this.tabs;
30624     },
30625
30626     createTool : function(parentEl, className){
30627         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30628             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30629         btn.addClassOnOver("x-layout-tools-button-over");
30630         return btn;
30631     }
30632 });/*
30633  * Based on:
30634  * Ext JS Library 1.1.1
30635  * Copyright(c) 2006-2007, Ext JS, LLC.
30636  *
30637  * Originally Released Under LGPL - original licence link has changed is not relivant.
30638  *
30639  * Fork - LGPL
30640  * <script type="text/javascript">
30641  */
30642  
30643
30644
30645 /**
30646  * @class Roo.SplitLayoutRegion
30647  * @extends Roo.LayoutRegion
30648  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30649  */
30650 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30651     this.cursor = cursor;
30652     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30653 };
30654
30655 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30656     splitTip : "Drag to resize.",
30657     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30658     useSplitTips : false,
30659
30660     applyConfig : function(config){
30661         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30662         if(config.split){
30663             if(!this.split){
30664                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30665                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30666                 /** The SplitBar for this region 
30667                 * @type Roo.SplitBar */
30668                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30669                 this.split.on("moved", this.onSplitMove, this);
30670                 this.split.useShim = config.useShim === true;
30671                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30672                 if(this.useSplitTips){
30673                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30674                 }
30675                 if(config.collapsible){
30676                     this.split.el.on("dblclick", this.collapse,  this);
30677                 }
30678             }
30679             if(typeof config.minSize != "undefined"){
30680                 this.split.minSize = config.minSize;
30681             }
30682             if(typeof config.maxSize != "undefined"){
30683                 this.split.maxSize = config.maxSize;
30684             }
30685             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30686                 this.hideSplitter();
30687             }
30688         }
30689     },
30690
30691     getHMaxSize : function(){
30692          var cmax = this.config.maxSize || 10000;
30693          var center = this.mgr.getRegion("center");
30694          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30695     },
30696
30697     getVMaxSize : function(){
30698          var cmax = this.config.maxSize || 10000;
30699          var center = this.mgr.getRegion("center");
30700          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30701     },
30702
30703     onSplitMove : function(split, newSize){
30704         this.fireEvent("resized", this, newSize);
30705     },
30706     
30707     /** 
30708      * Returns the {@link Roo.SplitBar} for this region.
30709      * @return {Roo.SplitBar}
30710      */
30711     getSplitBar : function(){
30712         return this.split;
30713     },
30714     
30715     hide : function(){
30716         this.hideSplitter();
30717         Roo.SplitLayoutRegion.superclass.hide.call(this);
30718     },
30719
30720     hideSplitter : function(){
30721         if(this.split){
30722             this.split.el.setLocation(-2000,-2000);
30723             this.split.el.hide();
30724         }
30725     },
30726
30727     show : function(){
30728         if(this.split){
30729             this.split.el.show();
30730         }
30731         Roo.SplitLayoutRegion.superclass.show.call(this);
30732     },
30733     
30734     beforeSlide: function(){
30735         if(Roo.isGecko){// firefox overflow auto bug workaround
30736             this.bodyEl.clip();
30737             if(this.tabs) {
30738                 this.tabs.bodyEl.clip();
30739             }
30740             if(this.activePanel){
30741                 this.activePanel.getEl().clip();
30742                 
30743                 if(this.activePanel.beforeSlide){
30744                     this.activePanel.beforeSlide();
30745                 }
30746             }
30747         }
30748     },
30749     
30750     afterSlide : function(){
30751         if(Roo.isGecko){// firefox overflow auto bug workaround
30752             this.bodyEl.unclip();
30753             if(this.tabs) {
30754                 this.tabs.bodyEl.unclip();
30755             }
30756             if(this.activePanel){
30757                 this.activePanel.getEl().unclip();
30758                 if(this.activePanel.afterSlide){
30759                     this.activePanel.afterSlide();
30760                 }
30761             }
30762         }
30763     },
30764
30765     initAutoHide : function(){
30766         if(this.autoHide !== false){
30767             if(!this.autoHideHd){
30768                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30769                 this.autoHideHd = {
30770                     "mouseout": function(e){
30771                         if(!e.within(this.el, true)){
30772                             st.delay(500);
30773                         }
30774                     },
30775                     "mouseover" : function(e){
30776                         st.cancel();
30777                     },
30778                     scope : this
30779                 };
30780             }
30781             this.el.on(this.autoHideHd);
30782         }
30783     },
30784
30785     clearAutoHide : function(){
30786         if(this.autoHide !== false){
30787             this.el.un("mouseout", this.autoHideHd.mouseout);
30788             this.el.un("mouseover", this.autoHideHd.mouseover);
30789         }
30790     },
30791
30792     clearMonitor : function(){
30793         Roo.get(document).un("click", this.slideInIf, this);
30794     },
30795
30796     // these names are backwards but not changed for compat
30797     slideOut : function(){
30798         if(this.isSlid || this.el.hasActiveFx()){
30799             return;
30800         }
30801         this.isSlid = true;
30802         if(this.collapseBtn){
30803             this.collapseBtn.hide();
30804         }
30805         this.closeBtnState = this.closeBtn.getStyle('display');
30806         this.closeBtn.hide();
30807         if(this.stickBtn){
30808             this.stickBtn.show();
30809         }
30810         this.el.show();
30811         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30812         this.beforeSlide();
30813         this.el.setStyle("z-index", 10001);
30814         this.el.slideIn(this.getSlideAnchor(), {
30815             callback: function(){
30816                 this.afterSlide();
30817                 this.initAutoHide();
30818                 Roo.get(document).on("click", this.slideInIf, this);
30819                 this.fireEvent("slideshow", this);
30820             },
30821             scope: this,
30822             block: true
30823         });
30824     },
30825
30826     afterSlideIn : function(){
30827         this.clearAutoHide();
30828         this.isSlid = false;
30829         this.clearMonitor();
30830         this.el.setStyle("z-index", "");
30831         if(this.collapseBtn){
30832             this.collapseBtn.show();
30833         }
30834         this.closeBtn.setStyle('display', this.closeBtnState);
30835         if(this.stickBtn){
30836             this.stickBtn.hide();
30837         }
30838         this.fireEvent("slidehide", this);
30839     },
30840
30841     slideIn : function(cb){
30842         if(!this.isSlid || this.el.hasActiveFx()){
30843             Roo.callback(cb);
30844             return;
30845         }
30846         this.isSlid = false;
30847         this.beforeSlide();
30848         this.el.slideOut(this.getSlideAnchor(), {
30849             callback: function(){
30850                 this.el.setLeftTop(-10000, -10000);
30851                 this.afterSlide();
30852                 this.afterSlideIn();
30853                 Roo.callback(cb);
30854             },
30855             scope: this,
30856             block: true
30857         });
30858     },
30859     
30860     slideInIf : function(e){
30861         if(!e.within(this.el)){
30862             this.slideIn();
30863         }
30864     },
30865
30866     animateCollapse : function(){
30867         this.beforeSlide();
30868         this.el.setStyle("z-index", 20000);
30869         var anchor = this.getSlideAnchor();
30870         this.el.slideOut(anchor, {
30871             callback : function(){
30872                 this.el.setStyle("z-index", "");
30873                 this.collapsedEl.slideIn(anchor, {duration:.3});
30874                 this.afterSlide();
30875                 this.el.setLocation(-10000,-10000);
30876                 this.el.hide();
30877                 this.fireEvent("collapsed", this);
30878             },
30879             scope: this,
30880             block: true
30881         });
30882     },
30883
30884     animateExpand : function(){
30885         this.beforeSlide();
30886         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30887         this.el.setStyle("z-index", 20000);
30888         this.collapsedEl.hide({
30889             duration:.1
30890         });
30891         this.el.slideIn(this.getSlideAnchor(), {
30892             callback : function(){
30893                 this.el.setStyle("z-index", "");
30894                 this.afterSlide();
30895                 if(this.split){
30896                     this.split.el.show();
30897                 }
30898                 this.fireEvent("invalidated", this);
30899                 this.fireEvent("expanded", this);
30900             },
30901             scope: this,
30902             block: true
30903         });
30904     },
30905
30906     anchors : {
30907         "west" : "left",
30908         "east" : "right",
30909         "north" : "top",
30910         "south" : "bottom"
30911     },
30912
30913     sanchors : {
30914         "west" : "l",
30915         "east" : "r",
30916         "north" : "t",
30917         "south" : "b"
30918     },
30919
30920     canchors : {
30921         "west" : "tl-tr",
30922         "east" : "tr-tl",
30923         "north" : "tl-bl",
30924         "south" : "bl-tl"
30925     },
30926
30927     getAnchor : function(){
30928         return this.anchors[this.position];
30929     },
30930
30931     getCollapseAnchor : function(){
30932         return this.canchors[this.position];
30933     },
30934
30935     getSlideAnchor : function(){
30936         return this.sanchors[this.position];
30937     },
30938
30939     getAlignAdj : function(){
30940         var cm = this.cmargins;
30941         switch(this.position){
30942             case "west":
30943                 return [0, 0];
30944             break;
30945             case "east":
30946                 return [0, 0];
30947             break;
30948             case "north":
30949                 return [0, 0];
30950             break;
30951             case "south":
30952                 return [0, 0];
30953             break;
30954         }
30955     },
30956
30957     getExpandAdj : function(){
30958         var c = this.collapsedEl, cm = this.cmargins;
30959         switch(this.position){
30960             case "west":
30961                 return [-(cm.right+c.getWidth()+cm.left), 0];
30962             break;
30963             case "east":
30964                 return [cm.right+c.getWidth()+cm.left, 0];
30965             break;
30966             case "north":
30967                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30968             break;
30969             case "south":
30970                 return [0, cm.top+cm.bottom+c.getHeight()];
30971             break;
30972         }
30973     }
30974 });/*
30975  * Based on:
30976  * Ext JS Library 1.1.1
30977  * Copyright(c) 2006-2007, Ext JS, LLC.
30978  *
30979  * Originally Released Under LGPL - original licence link has changed is not relivant.
30980  *
30981  * Fork - LGPL
30982  * <script type="text/javascript">
30983  */
30984 /*
30985  * These classes are private internal classes
30986  */
30987 Roo.CenterLayoutRegion = function(mgr, config){
30988     Roo.LayoutRegion.call(this, mgr, config, "center");
30989     this.visible = true;
30990     this.minWidth = config.minWidth || 20;
30991     this.minHeight = config.minHeight || 20;
30992 };
30993
30994 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30995     hide : function(){
30996         // center panel can't be hidden
30997     },
30998     
30999     show : function(){
31000         // center panel can't be hidden
31001     },
31002     
31003     getMinWidth: function(){
31004         return this.minWidth;
31005     },
31006     
31007     getMinHeight: function(){
31008         return this.minHeight;
31009     }
31010 });
31011
31012
31013 Roo.NorthLayoutRegion = function(mgr, config){
31014     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31015     if(this.split){
31016         this.split.placement = Roo.SplitBar.TOP;
31017         this.split.orientation = Roo.SplitBar.VERTICAL;
31018         this.split.el.addClass("x-layout-split-v");
31019     }
31020     var size = config.initialSize || config.height;
31021     if(typeof size != "undefined"){
31022         this.el.setHeight(size);
31023     }
31024 };
31025 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31026     orientation: Roo.SplitBar.VERTICAL,
31027     getBox : function(){
31028         if(this.collapsed){
31029             return this.collapsedEl.getBox();
31030         }
31031         var box = this.el.getBox();
31032         if(this.split){
31033             box.height += this.split.el.getHeight();
31034         }
31035         return box;
31036     },
31037     
31038     updateBox : function(box){
31039         if(this.split && !this.collapsed){
31040             box.height -= this.split.el.getHeight();
31041             this.split.el.setLeft(box.x);
31042             this.split.el.setTop(box.y+box.height);
31043             this.split.el.setWidth(box.width);
31044         }
31045         if(this.collapsed){
31046             this.updateBody(box.width, null);
31047         }
31048         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31049     }
31050 });
31051
31052 Roo.SouthLayoutRegion = function(mgr, config){
31053     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31054     if(this.split){
31055         this.split.placement = Roo.SplitBar.BOTTOM;
31056         this.split.orientation = Roo.SplitBar.VERTICAL;
31057         this.split.el.addClass("x-layout-split-v");
31058     }
31059     var size = config.initialSize || config.height;
31060     if(typeof size != "undefined"){
31061         this.el.setHeight(size);
31062     }
31063 };
31064 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31065     orientation: Roo.SplitBar.VERTICAL,
31066     getBox : function(){
31067         if(this.collapsed){
31068             return this.collapsedEl.getBox();
31069         }
31070         var box = this.el.getBox();
31071         if(this.split){
31072             var sh = this.split.el.getHeight();
31073             box.height += sh;
31074             box.y -= sh;
31075         }
31076         return box;
31077     },
31078     
31079     updateBox : function(box){
31080         if(this.split && !this.collapsed){
31081             var sh = this.split.el.getHeight();
31082             box.height -= sh;
31083             box.y += sh;
31084             this.split.el.setLeft(box.x);
31085             this.split.el.setTop(box.y-sh);
31086             this.split.el.setWidth(box.width);
31087         }
31088         if(this.collapsed){
31089             this.updateBody(box.width, null);
31090         }
31091         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31092     }
31093 });
31094
31095 Roo.EastLayoutRegion = function(mgr, config){
31096     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31097     if(this.split){
31098         this.split.placement = Roo.SplitBar.RIGHT;
31099         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31100         this.split.el.addClass("x-layout-split-h");
31101     }
31102     var size = config.initialSize || config.width;
31103     if(typeof size != "undefined"){
31104         this.el.setWidth(size);
31105     }
31106 };
31107 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31108     orientation: Roo.SplitBar.HORIZONTAL,
31109     getBox : function(){
31110         if(this.collapsed){
31111             return this.collapsedEl.getBox();
31112         }
31113         var box = this.el.getBox();
31114         if(this.split){
31115             var sw = this.split.el.getWidth();
31116             box.width += sw;
31117             box.x -= sw;
31118         }
31119         return box;
31120     },
31121
31122     updateBox : function(box){
31123         if(this.split && !this.collapsed){
31124             var sw = this.split.el.getWidth();
31125             box.width -= sw;
31126             this.split.el.setLeft(box.x);
31127             this.split.el.setTop(box.y);
31128             this.split.el.setHeight(box.height);
31129             box.x += sw;
31130         }
31131         if(this.collapsed){
31132             this.updateBody(null, box.height);
31133         }
31134         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31135     }
31136 });
31137
31138 Roo.WestLayoutRegion = function(mgr, config){
31139     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31140     if(this.split){
31141         this.split.placement = Roo.SplitBar.LEFT;
31142         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31143         this.split.el.addClass("x-layout-split-h");
31144     }
31145     var size = config.initialSize || config.width;
31146     if(typeof size != "undefined"){
31147         this.el.setWidth(size);
31148     }
31149 };
31150 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31151     orientation: Roo.SplitBar.HORIZONTAL,
31152     getBox : function(){
31153         if(this.collapsed){
31154             return this.collapsedEl.getBox();
31155         }
31156         var box = this.el.getBox();
31157         if(this.split){
31158             box.width += this.split.el.getWidth();
31159         }
31160         return box;
31161     },
31162     
31163     updateBox : function(box){
31164         if(this.split && !this.collapsed){
31165             var sw = this.split.el.getWidth();
31166             box.width -= sw;
31167             this.split.el.setLeft(box.x+box.width);
31168             this.split.el.setTop(box.y);
31169             this.split.el.setHeight(box.height);
31170         }
31171         if(this.collapsed){
31172             this.updateBody(null, box.height);
31173         }
31174         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31175     }
31176 });
31177 /*
31178  * Based on:
31179  * Ext JS Library 1.1.1
31180  * Copyright(c) 2006-2007, Ext JS, LLC.
31181  *
31182  * Originally Released Under LGPL - original licence link has changed is not relivant.
31183  *
31184  * Fork - LGPL
31185  * <script type="text/javascript">
31186  */
31187  
31188  
31189 /*
31190  * Private internal class for reading and applying state
31191  */
31192 Roo.LayoutStateManager = function(layout){
31193      // default empty state
31194      this.state = {
31195         north: {},
31196         south: {},
31197         east: {},
31198         west: {}       
31199     };
31200 };
31201
31202 Roo.LayoutStateManager.prototype = {
31203     init : function(layout, provider){
31204         this.provider = provider;
31205         var state = provider.get(layout.id+"-layout-state");
31206         if(state){
31207             var wasUpdating = layout.isUpdating();
31208             if(!wasUpdating){
31209                 layout.beginUpdate();
31210             }
31211             for(var key in state){
31212                 if(typeof state[key] != "function"){
31213                     var rstate = state[key];
31214                     var r = layout.getRegion(key);
31215                     if(r && rstate){
31216                         if(rstate.size){
31217                             r.resizeTo(rstate.size);
31218                         }
31219                         if(rstate.collapsed == true){
31220                             r.collapse(true);
31221                         }else{
31222                             r.expand(null, true);
31223                         }
31224                     }
31225                 }
31226             }
31227             if(!wasUpdating){
31228                 layout.endUpdate();
31229             }
31230             this.state = state; 
31231         }
31232         this.layout = layout;
31233         layout.on("regionresized", this.onRegionResized, this);
31234         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31235         layout.on("regionexpanded", this.onRegionExpanded, this);
31236     },
31237     
31238     storeState : function(){
31239         this.provider.set(this.layout.id+"-layout-state", this.state);
31240     },
31241     
31242     onRegionResized : function(region, newSize){
31243         this.state[region.getPosition()].size = newSize;
31244         this.storeState();
31245     },
31246     
31247     onRegionCollapsed : function(region){
31248         this.state[region.getPosition()].collapsed = true;
31249         this.storeState();
31250     },
31251     
31252     onRegionExpanded : function(region){
31253         this.state[region.getPosition()].collapsed = false;
31254         this.storeState();
31255     }
31256 };/*
31257  * Based on:
31258  * Ext JS Library 1.1.1
31259  * Copyright(c) 2006-2007, Ext JS, LLC.
31260  *
31261  * Originally Released Under LGPL - original licence link has changed is not relivant.
31262  *
31263  * Fork - LGPL
31264  * <script type="text/javascript">
31265  */
31266 /**
31267  * @class Roo.ContentPanel
31268  * @extends Roo.util.Observable
31269  * @children Roo.form.Form Roo.JsonView Roo.View
31270  * A basic ContentPanel element.
31271  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31272  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31273  * @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
31274  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31275  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31276  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31277  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
31278  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31279  * @cfg {String} title          The title for this panel
31280  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31281  * @cfg {String} url            Calls {@link #setUrl} with this value
31282  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31283  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31284  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31285  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31286  * @cfg {String}    style  Extra style to add to the content panel
31287  * @cfg {Roo.menu.Menu} menu  popup menu
31288
31289  * @constructor
31290  * Create a new ContentPanel.
31291  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31292  * @param {String/Object} config A string to set only the title or a config object
31293  * @param {String} content (optional) Set the HTML content for this panel
31294  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31295  */
31296 Roo.ContentPanel = function(el, config, content){
31297     
31298      
31299     /*
31300     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31301         config = el;
31302         el = Roo.id();
31303     }
31304     if (config && config.parentLayout) { 
31305         el = config.parentLayout.el.createChild(); 
31306     }
31307     */
31308     if(el.autoCreate){ // xtype is available if this is called from factory
31309         config = el;
31310         el = Roo.id();
31311     }
31312     this.el = Roo.get(el);
31313     if(!this.el && config && config.autoCreate){
31314         if(typeof config.autoCreate == "object"){
31315             if(!config.autoCreate.id){
31316                 config.autoCreate.id = config.id||el;
31317             }
31318             this.el = Roo.DomHelper.append(document.body,
31319                         config.autoCreate, true);
31320         }else{
31321             this.el = Roo.DomHelper.append(document.body,
31322                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31323         }
31324     }
31325     
31326     
31327     this.closable = false;
31328     this.loaded = false;
31329     this.active = false;
31330     if(typeof config == "string"){
31331         this.title = config;
31332     }else{
31333         Roo.apply(this, config);
31334     }
31335     
31336     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31337         this.wrapEl = this.el.wrap();
31338         this.toolbar.container = this.el.insertSibling(false, 'before');
31339         this.toolbar = new Roo.Toolbar(this.toolbar);
31340     }
31341     
31342     // xtype created footer. - not sure if will work as we normally have to render first..
31343     if (this.footer && !this.footer.el && this.footer.xtype) {
31344         if (!this.wrapEl) {
31345             this.wrapEl = this.el.wrap();
31346         }
31347     
31348         this.footer.container = this.wrapEl.createChild();
31349          
31350         this.footer = Roo.factory(this.footer, Roo);
31351         
31352     }
31353     
31354     if(this.resizeEl){
31355         this.resizeEl = Roo.get(this.resizeEl, true);
31356     }else{
31357         this.resizeEl = this.el;
31358     }
31359     // handle view.xtype
31360     
31361  
31362     
31363     
31364     this.addEvents({
31365         /**
31366          * @event activate
31367          * Fires when this panel is activated. 
31368          * @param {Roo.ContentPanel} this
31369          */
31370         "activate" : true,
31371         /**
31372          * @event deactivate
31373          * Fires when this panel is activated. 
31374          * @param {Roo.ContentPanel} this
31375          */
31376         "deactivate" : true,
31377
31378         /**
31379          * @event resize
31380          * Fires when this panel is resized if fitToFrame is true.
31381          * @param {Roo.ContentPanel} this
31382          * @param {Number} width The width after any component adjustments
31383          * @param {Number} height The height after any component adjustments
31384          */
31385         "resize" : true,
31386         
31387          /**
31388          * @event render
31389          * Fires when this tab is created
31390          * @param {Roo.ContentPanel} this
31391          */
31392         "render" : true
31393          
31394         
31395     });
31396     
31397
31398     
31399     
31400     if(this.autoScroll){
31401         this.resizeEl.setStyle("overflow", "auto");
31402     } else {
31403         // fix randome scrolling
31404         this.el.on('scroll', function() {
31405             Roo.log('fix random scolling');
31406             this.scrollTo('top',0); 
31407         });
31408     }
31409     content = content || this.content;
31410     if(content){
31411         this.setContent(content);
31412     }
31413     if(config && config.url){
31414         this.setUrl(this.url, this.params, this.loadOnce);
31415     }
31416     
31417     
31418     
31419     Roo.ContentPanel.superclass.constructor.call(this);
31420     
31421     if (this.view && typeof(this.view.xtype) != 'undefined') {
31422         this.view.el = this.el.appendChild(document.createElement("div"));
31423         this.view = Roo.factory(this.view); 
31424         this.view.render  &&  this.view.render(false, '');  
31425     }
31426     
31427     
31428     this.fireEvent('render', this);
31429 };
31430
31431 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31432     tabTip:'',
31433     setRegion : function(region){
31434         this.region = region;
31435         if(region){
31436            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31437         }else{
31438            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31439         } 
31440     },
31441     
31442     /**
31443      * Returns the toolbar for this Panel if one was configured. 
31444      * @return {Roo.Toolbar} 
31445      */
31446     getToolbar : function(){
31447         return this.toolbar;
31448     },
31449     
31450     setActiveState : function(active){
31451         this.active = active;
31452         if(!active){
31453             this.fireEvent("deactivate", this);
31454         }else{
31455             this.fireEvent("activate", this);
31456         }
31457     },
31458     /**
31459      * Updates this panel's element
31460      * @param {String} content The new content
31461      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31462     */
31463     setContent : function(content, loadScripts){
31464         this.el.update(content, loadScripts);
31465     },
31466
31467     ignoreResize : function(w, h){
31468         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31469             return true;
31470         }else{
31471             this.lastSize = {width: w, height: h};
31472             return false;
31473         }
31474     },
31475     /**
31476      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31477      * @return {Roo.UpdateManager} The UpdateManager
31478      */
31479     getUpdateManager : function(){
31480         return this.el.getUpdateManager();
31481     },
31482      /**
31483      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31484      * @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:
31485 <pre><code>
31486 panel.load({
31487     url: "your-url.php",
31488     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31489     callback: yourFunction,
31490     scope: yourObject, //(optional scope)
31491     discardUrl: false,
31492     nocache: false,
31493     text: "Loading...",
31494     timeout: 30,
31495     scripts: false
31496 });
31497 </code></pre>
31498      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31499      * 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.
31500      * @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}
31501      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31502      * @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.
31503      * @return {Roo.ContentPanel} this
31504      */
31505     load : function(){
31506         var um = this.el.getUpdateManager();
31507         um.update.apply(um, arguments);
31508         return this;
31509     },
31510
31511
31512     /**
31513      * 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.
31514      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31515      * @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)
31516      * @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)
31517      * @return {Roo.UpdateManager} The UpdateManager
31518      */
31519     setUrl : function(url, params, loadOnce){
31520         if(this.refreshDelegate){
31521             this.removeListener("activate", this.refreshDelegate);
31522         }
31523         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31524         this.on("activate", this.refreshDelegate);
31525         return this.el.getUpdateManager();
31526     },
31527     
31528     _handleRefresh : function(url, params, loadOnce){
31529         if(!loadOnce || !this.loaded){
31530             var updater = this.el.getUpdateManager();
31531             updater.update(url, params, this._setLoaded.createDelegate(this));
31532         }
31533     },
31534     
31535     _setLoaded : function(){
31536         this.loaded = true;
31537     }, 
31538     
31539     /**
31540      * Returns this panel's id
31541      * @return {String} 
31542      */
31543     getId : function(){
31544         return this.el.id;
31545     },
31546     
31547     /** 
31548      * Returns this panel's element - used by regiosn to add.
31549      * @return {Roo.Element} 
31550      */
31551     getEl : function(){
31552         return this.wrapEl || this.el;
31553     },
31554     
31555     adjustForComponents : function(width, height)
31556     {
31557         //Roo.log('adjustForComponents ');
31558         if(this.resizeEl != this.el){
31559             width -= this.el.getFrameWidth('lr');
31560             height -= this.el.getFrameWidth('tb');
31561         }
31562         if(this.toolbar){
31563             var te = this.toolbar.getEl();
31564             height -= te.getHeight();
31565             te.setWidth(width);
31566         }
31567         if(this.footer){
31568             var te = this.footer.getEl();
31569             //Roo.log("footer:" + te.getHeight());
31570             
31571             height -= te.getHeight();
31572             te.setWidth(width);
31573         }
31574         
31575         
31576         if(this.adjustments){
31577             width += this.adjustments[0];
31578             height += this.adjustments[1];
31579         }
31580         return {"width": width, "height": height};
31581     },
31582     
31583     setSize : function(width, height){
31584         if(this.fitToFrame && !this.ignoreResize(width, height)){
31585             if(this.fitContainer && this.resizeEl != this.el){
31586                 this.el.setSize(width, height);
31587             }
31588             var size = this.adjustForComponents(width, height);
31589             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31590             this.fireEvent('resize', this, size.width, size.height);
31591         }
31592     },
31593     
31594     /**
31595      * Returns this panel's title
31596      * @return {String} 
31597      */
31598     getTitle : function(){
31599         return this.title;
31600     },
31601     
31602     /**
31603      * Set this panel's title
31604      * @param {String} title
31605      */
31606     setTitle : function(title){
31607         this.title = title;
31608         if(this.region){
31609             this.region.updatePanelTitle(this, title);
31610         }
31611     },
31612     
31613     /**
31614      * Returns true is this panel was configured to be closable
31615      * @return {Boolean} 
31616      */
31617     isClosable : function(){
31618         return this.closable;
31619     },
31620     
31621     beforeSlide : function(){
31622         this.el.clip();
31623         this.resizeEl.clip();
31624     },
31625     
31626     afterSlide : function(){
31627         this.el.unclip();
31628         this.resizeEl.unclip();
31629     },
31630     
31631     /**
31632      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31633      *   Will fail silently if the {@link #setUrl} method has not been called.
31634      *   This does not activate the panel, just updates its content.
31635      */
31636     refresh : function(){
31637         if(this.refreshDelegate){
31638            this.loaded = false;
31639            this.refreshDelegate();
31640         }
31641     },
31642     
31643     /**
31644      * Destroys this panel
31645      */
31646     destroy : function(){
31647         this.el.removeAllListeners();
31648         var tempEl = document.createElement("span");
31649         tempEl.appendChild(this.el.dom);
31650         tempEl.innerHTML = "";
31651         this.el.remove();
31652         this.el = null;
31653     },
31654     
31655     /**
31656      * form - if the content panel contains a form - this is a reference to it.
31657      * @type {Roo.form.Form}
31658      */
31659     form : false,
31660     /**
31661      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31662      *    This contains a reference to it.
31663      * @type {Roo.View}
31664      */
31665     view : false,
31666     
31667       /**
31668      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31669      * <pre><code>
31670
31671 layout.addxtype({
31672        xtype : 'Form',
31673        items: [ .... ]
31674    }
31675 );
31676
31677 </code></pre>
31678      * @param {Object} cfg Xtype definition of item to add.
31679      */
31680     
31681     addxtype : function(cfg) {
31682         // add form..
31683         if (cfg.xtype.match(/^Form$/)) {
31684             
31685             var el;
31686             //if (this.footer) {
31687             //    el = this.footer.container.insertSibling(false, 'before');
31688             //} else {
31689                 el = this.el.createChild();
31690             //}
31691
31692             this.form = new  Roo.form.Form(cfg);
31693             
31694             
31695             if ( this.form.allItems.length) {
31696                 this.form.render(el.dom);
31697             }
31698             return this.form;
31699         }
31700         // should only have one of theses..
31701         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31702             // views.. should not be just added - used named prop 'view''
31703             
31704             cfg.el = this.el.appendChild(document.createElement("div"));
31705             // factory?
31706             
31707             var ret = new Roo.factory(cfg);
31708              
31709              ret.render && ret.render(false, ''); // render blank..
31710             this.view = ret;
31711             return ret;
31712         }
31713         return false;
31714     }
31715 });
31716
31717 /**
31718  * @class Roo.GridPanel
31719  * @extends Roo.ContentPanel
31720  * @constructor
31721  * Create a new GridPanel.
31722  * @param {Roo.grid.Grid} grid The grid for this panel
31723  * @param {String/Object} config A string to set only the panel's title, or a config object
31724  */
31725 Roo.GridPanel = function(grid, config){
31726     
31727   
31728     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31729         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31730         
31731     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31732     
31733     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31734     
31735     if(this.toolbar){
31736         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31737     }
31738     // xtype created footer. - not sure if will work as we normally have to render first..
31739     if (this.footer && !this.footer.el && this.footer.xtype) {
31740         
31741         this.footer.container = this.grid.getView().getFooterPanel(true);
31742         this.footer.dataSource = this.grid.dataSource;
31743         this.footer = Roo.factory(this.footer, Roo);
31744         
31745     }
31746     
31747     grid.monitorWindowResize = false; // turn off autosizing
31748     grid.autoHeight = false;
31749     grid.autoWidth = false;
31750     this.grid = grid;
31751     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31752 };
31753
31754 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31755     getId : function(){
31756         return this.grid.id;
31757     },
31758     
31759     /**
31760      * Returns the grid for this panel
31761      * @return {Roo.grid.Grid} 
31762      */
31763     getGrid : function(){
31764         return this.grid;    
31765     },
31766     
31767     setSize : function(width, height){
31768         if(!this.ignoreResize(width, height)){
31769             var grid = this.grid;
31770             var size = this.adjustForComponents(width, height);
31771             grid.getGridEl().setSize(size.width, size.height);
31772             grid.autoSize();
31773         }
31774     },
31775     
31776     beforeSlide : function(){
31777         this.grid.getView().scroller.clip();
31778     },
31779     
31780     afterSlide : function(){
31781         this.grid.getView().scroller.unclip();
31782     },
31783     
31784     destroy : function(){
31785         this.grid.destroy();
31786         delete this.grid;
31787         Roo.GridPanel.superclass.destroy.call(this); 
31788     }
31789 });
31790
31791
31792 /**
31793  * @class Roo.NestedLayoutPanel
31794  * @extends Roo.ContentPanel
31795  * @constructor
31796  * Create a new NestedLayoutPanel.
31797  * 
31798  * 
31799  * @param {Roo.BorderLayout} layout [required] The layout for this panel
31800  * @param {String/Object} config A string to set only the title or a config object
31801  */
31802 Roo.NestedLayoutPanel = function(layout, config)
31803 {
31804     // construct with only one argument..
31805     /* FIXME - implement nicer consturctors
31806     if (layout.layout) {
31807         config = layout;
31808         layout = config.layout;
31809         delete config.layout;
31810     }
31811     if (layout.xtype && !layout.getEl) {
31812         // then layout needs constructing..
31813         layout = Roo.factory(layout, Roo);
31814     }
31815     */
31816     
31817     
31818     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31819     
31820     layout.monitorWindowResize = false; // turn off autosizing
31821     this.layout = layout;
31822     this.layout.getEl().addClass("x-layout-nested-layout");
31823     
31824     
31825     
31826     
31827 };
31828
31829 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31830
31831     setSize : function(width, height){
31832         if(!this.ignoreResize(width, height)){
31833             var size = this.adjustForComponents(width, height);
31834             var el = this.layout.getEl();
31835             el.setSize(size.width, size.height);
31836             var touch = el.dom.offsetWidth;
31837             this.layout.layout();
31838             // ie requires a double layout on the first pass
31839             if(Roo.isIE && !this.initialized){
31840                 this.initialized = true;
31841                 this.layout.layout();
31842             }
31843         }
31844     },
31845     
31846     // activate all subpanels if not currently active..
31847     
31848     setActiveState : function(active){
31849         this.active = active;
31850         if(!active){
31851             this.fireEvent("deactivate", this);
31852             return;
31853         }
31854         
31855         this.fireEvent("activate", this);
31856         // not sure if this should happen before or after..
31857         if (!this.layout) {
31858             return; // should not happen..
31859         }
31860         var reg = false;
31861         for (var r in this.layout.regions) {
31862             reg = this.layout.getRegion(r);
31863             if (reg.getActivePanel()) {
31864                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31865                 reg.setActivePanel(reg.getActivePanel());
31866                 continue;
31867             }
31868             if (!reg.panels.length) {
31869                 continue;
31870             }
31871             reg.showPanel(reg.getPanel(0));
31872         }
31873         
31874         
31875         
31876         
31877     },
31878     
31879     /**
31880      * Returns the nested BorderLayout for this panel
31881      * @return {Roo.BorderLayout} 
31882      */
31883     getLayout : function(){
31884         return this.layout;
31885     },
31886     
31887      /**
31888      * Adds a xtype elements to the layout of the nested panel
31889      * <pre><code>
31890
31891 panel.addxtype({
31892        xtype : 'ContentPanel',
31893        region: 'west',
31894        items: [ .... ]
31895    }
31896 );
31897
31898 panel.addxtype({
31899         xtype : 'NestedLayoutPanel',
31900         region: 'west',
31901         layout: {
31902            center: { },
31903            west: { }   
31904         },
31905         items : [ ... list of content panels or nested layout panels.. ]
31906    }
31907 );
31908 </code></pre>
31909      * @param {Object} cfg Xtype definition of item to add.
31910      */
31911     addxtype : function(cfg) {
31912         return this.layout.addxtype(cfg);
31913     
31914     }
31915 });
31916
31917 Roo.ScrollPanel = function(el, config, content){
31918     config = config || {};
31919     config.fitToFrame = true;
31920     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31921     
31922     this.el.dom.style.overflow = "hidden";
31923     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31924     this.el.removeClass("x-layout-inactive-content");
31925     this.el.on("mousewheel", this.onWheel, this);
31926
31927     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31928     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31929     up.unselectable(); down.unselectable();
31930     up.on("click", this.scrollUp, this);
31931     down.on("click", this.scrollDown, this);
31932     up.addClassOnOver("x-scroller-btn-over");
31933     down.addClassOnOver("x-scroller-btn-over");
31934     up.addClassOnClick("x-scroller-btn-click");
31935     down.addClassOnClick("x-scroller-btn-click");
31936     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31937
31938     this.resizeEl = this.el;
31939     this.el = wrap; this.up = up; this.down = down;
31940 };
31941
31942 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31943     increment : 100,
31944     wheelIncrement : 5,
31945     scrollUp : function(){
31946         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31947     },
31948
31949     scrollDown : function(){
31950         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31951     },
31952
31953     afterScroll : function(){
31954         var el = this.resizeEl;
31955         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31956         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31957         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31958     },
31959
31960     setSize : function(){
31961         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31962         this.afterScroll();
31963     },
31964
31965     onWheel : function(e){
31966         var d = e.getWheelDelta();
31967         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31968         this.afterScroll();
31969         e.stopEvent();
31970     },
31971
31972     setContent : function(content, loadScripts){
31973         this.resizeEl.update(content, loadScripts);
31974     }
31975
31976 });
31977
31978
31979
31980 /**
31981  * @class Roo.TreePanel
31982  * @extends Roo.ContentPanel
31983  * Treepanel component
31984  * 
31985  * @constructor
31986  * Create a new TreePanel. - defaults to fit/scoll contents.
31987  * @param {String/Object} config A string to set only the panel's title, or a config object
31988  */
31989 Roo.TreePanel = function(config){
31990     var el = config.el;
31991     var tree = config.tree;
31992     delete config.tree; 
31993     delete config.el; // hopefull!
31994     
31995     // wrapper for IE7 strict & safari scroll issue
31996     
31997     var treeEl = el.createChild();
31998     config.resizeEl = treeEl;
31999     
32000     
32001     
32002     Roo.TreePanel.superclass.constructor.call(this, el, config);
32003  
32004  
32005     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32006     //console.log(tree);
32007     this.on('activate', function()
32008     {
32009         if (this.tree.rendered) {
32010             return;
32011         }
32012         //console.log('render tree');
32013         this.tree.render();
32014     });
32015     // this should not be needed.. - it's actually the 'el' that resizes?
32016     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32017     
32018     //this.on('resize',  function (cp, w, h) {
32019     //        this.tree.innerCt.setWidth(w);
32020     //        this.tree.innerCt.setHeight(h);
32021     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
32022     //});
32023
32024         
32025     
32026 };
32027
32028 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32029     fitToFrame : true,
32030     autoScroll : true,
32031     /*
32032      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
32033      */
32034     tree : false
32035
32036 });
32037
32038
32039
32040
32041
32042
32043
32044
32045
32046
32047
32048 /*
32049  * Based on:
32050  * Ext JS Library 1.1.1
32051  * Copyright(c) 2006-2007, Ext JS, LLC.
32052  *
32053  * Originally Released Under LGPL - original licence link has changed is not relivant.
32054  *
32055  * Fork - LGPL
32056  * <script type="text/javascript">
32057  */
32058  
32059
32060 /**
32061  * @class Roo.ReaderLayout
32062  * @extends Roo.BorderLayout
32063  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32064  * center region containing two nested regions (a top one for a list view and one for item preview below),
32065  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32066  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32067  * expedites the setup of the overall layout and regions for this common application style.
32068  * Example:
32069  <pre><code>
32070 var reader = new Roo.ReaderLayout();
32071 var CP = Roo.ContentPanel;  // shortcut for adding
32072
32073 reader.beginUpdate();
32074 reader.add("north", new CP("north", "North"));
32075 reader.add("west", new CP("west", {title: "West"}));
32076 reader.add("east", new CP("east", {title: "East"}));
32077
32078 reader.regions.listView.add(new CP("listView", "List"));
32079 reader.regions.preview.add(new CP("preview", "Preview"));
32080 reader.endUpdate();
32081 </code></pre>
32082 * @constructor
32083 * Create a new ReaderLayout
32084 * @param {Object} config Configuration options
32085 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32086 * document.body if omitted)
32087 */
32088 Roo.ReaderLayout = function(config, renderTo){
32089     var c = config || {size:{}};
32090     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32091         north: c.north !== false ? Roo.apply({
32092             split:false,
32093             initialSize: 32,
32094             titlebar: false
32095         }, c.north) : false,
32096         west: c.west !== false ? Roo.apply({
32097             split:true,
32098             initialSize: 200,
32099             minSize: 175,
32100             maxSize: 400,
32101             titlebar: true,
32102             collapsible: true,
32103             animate: true,
32104             margins:{left:5,right:0,bottom:5,top:5},
32105             cmargins:{left:5,right:5,bottom:5,top:5}
32106         }, c.west) : false,
32107         east: c.east !== false ? Roo.apply({
32108             split:true,
32109             initialSize: 200,
32110             minSize: 175,
32111             maxSize: 400,
32112             titlebar: true,
32113             collapsible: true,
32114             animate: true,
32115             margins:{left:0,right:5,bottom:5,top:5},
32116             cmargins:{left:5,right:5,bottom:5,top:5}
32117         }, c.east) : false,
32118         center: Roo.apply({
32119             tabPosition: 'top',
32120             autoScroll:false,
32121             closeOnTab: true,
32122             titlebar:false,
32123             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32124         }, c.center)
32125     });
32126
32127     this.el.addClass('x-reader');
32128
32129     this.beginUpdate();
32130
32131     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32132         south: c.preview !== false ? Roo.apply({
32133             split:true,
32134             initialSize: 200,
32135             minSize: 100,
32136             autoScroll:true,
32137             collapsible:true,
32138             titlebar: true,
32139             cmargins:{top:5,left:0, right:0, bottom:0}
32140         }, c.preview) : false,
32141         center: Roo.apply({
32142             autoScroll:false,
32143             titlebar:false,
32144             minHeight:200
32145         }, c.listView)
32146     });
32147     this.add('center', new Roo.NestedLayoutPanel(inner,
32148             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32149
32150     this.endUpdate();
32151
32152     this.regions.preview = inner.getRegion('south');
32153     this.regions.listView = inner.getRegion('center');
32154 };
32155
32156 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32157  * Based on:
32158  * Ext JS Library 1.1.1
32159  * Copyright(c) 2006-2007, Ext JS, LLC.
32160  *
32161  * Originally Released Under LGPL - original licence link has changed is not relivant.
32162  *
32163  * Fork - LGPL
32164  * <script type="text/javascript">
32165  */
32166  
32167 /**
32168  * @class Roo.grid.Grid
32169  * @extends Roo.util.Observable
32170  * This class represents the primary interface of a component based grid control.
32171  * <br><br>Usage:<pre><code>
32172  var grid = new Roo.grid.Grid("my-container-id", {
32173      ds: myDataStore,
32174      cm: myColModel,
32175      selModel: mySelectionModel,
32176      autoSizeColumns: true,
32177      monitorWindowResize: false,
32178      trackMouseOver: true
32179  });
32180  // set any options
32181  grid.render();
32182  * </code></pre>
32183  * <b>Common Problems:</b><br/>
32184  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32185  * element will correct this<br/>
32186  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32187  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32188  * are unpredictable.<br/>
32189  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32190  * grid to calculate dimensions/offsets.<br/>
32191   * @constructor
32192  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32193  * The container MUST have some type of size defined for the grid to fill. The container will be
32194  * automatically set to position relative if it isn't already.
32195  * @param {Object} config A config object that sets properties on this grid.
32196  */
32197 Roo.grid.Grid = function(container, config){
32198         // initialize the container
32199         this.container = Roo.get(container);
32200         this.container.update("");
32201         this.container.setStyle("overflow", "hidden");
32202     this.container.addClass('x-grid-container');
32203
32204     this.id = this.container.id;
32205
32206     Roo.apply(this, config);
32207     // check and correct shorthanded configs
32208     if(this.ds){
32209         this.dataSource = this.ds;
32210         delete this.ds;
32211     }
32212     if(this.cm){
32213         this.colModel = this.cm;
32214         delete this.cm;
32215     }
32216     if(this.sm){
32217         this.selModel = this.sm;
32218         delete this.sm;
32219     }
32220
32221     if (this.selModel) {
32222         this.selModel = Roo.factory(this.selModel, Roo.grid);
32223         this.sm = this.selModel;
32224         this.sm.xmodule = this.xmodule || false;
32225     }
32226     if (typeof(this.colModel.config) == 'undefined') {
32227         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32228         this.cm = this.colModel;
32229         this.cm.xmodule = this.xmodule || false;
32230     }
32231     if (this.dataSource) {
32232         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32233         this.ds = this.dataSource;
32234         this.ds.xmodule = this.xmodule || false;
32235          
32236     }
32237     
32238     
32239     
32240     if(this.width){
32241         this.container.setWidth(this.width);
32242     }
32243
32244     if(this.height){
32245         this.container.setHeight(this.height);
32246     }
32247     /** @private */
32248         this.addEvents({
32249         // raw events
32250         /**
32251          * @event click
32252          * The raw click event for the entire grid.
32253          * @param {Roo.EventObject} e
32254          */
32255         "click" : true,
32256         /**
32257          * @event dblclick
32258          * The raw dblclick event for the entire grid.
32259          * @param {Roo.EventObject} e
32260          */
32261         "dblclick" : true,
32262         /**
32263          * @event contextmenu
32264          * The raw contextmenu event for the entire grid.
32265          * @param {Roo.EventObject} e
32266          */
32267         "contextmenu" : true,
32268         /**
32269          * @event mousedown
32270          * The raw mousedown event for the entire grid.
32271          * @param {Roo.EventObject} e
32272          */
32273         "mousedown" : true,
32274         /**
32275          * @event mouseup
32276          * The raw mouseup event for the entire grid.
32277          * @param {Roo.EventObject} e
32278          */
32279         "mouseup" : true,
32280         /**
32281          * @event mouseover
32282          * The raw mouseover event for the entire grid.
32283          * @param {Roo.EventObject} e
32284          */
32285         "mouseover" : true,
32286         /**
32287          * @event mouseout
32288          * The raw mouseout event for the entire grid.
32289          * @param {Roo.EventObject} e
32290          */
32291         "mouseout" : true,
32292         /**
32293          * @event keypress
32294          * The raw keypress event for the entire grid.
32295          * @param {Roo.EventObject} e
32296          */
32297         "keypress" : true,
32298         /**
32299          * @event keydown
32300          * The raw keydown event for the entire grid.
32301          * @param {Roo.EventObject} e
32302          */
32303         "keydown" : true,
32304
32305         // custom events
32306
32307         /**
32308          * @event cellclick
32309          * Fires when a cell is clicked
32310          * @param {Grid} this
32311          * @param {Number} rowIndex
32312          * @param {Number} columnIndex
32313          * @param {Roo.EventObject} e
32314          */
32315         "cellclick" : true,
32316         /**
32317          * @event celldblclick
32318          * Fires when a cell is double clicked
32319          * @param {Grid} this
32320          * @param {Number} rowIndex
32321          * @param {Number} columnIndex
32322          * @param {Roo.EventObject} e
32323          */
32324         "celldblclick" : true,
32325         /**
32326          * @event rowclick
32327          * Fires when a row is clicked
32328          * @param {Grid} this
32329          * @param {Number} rowIndex
32330          * @param {Roo.EventObject} e
32331          */
32332         "rowclick" : true,
32333         /**
32334          * @event rowdblclick
32335          * Fires when a row is double clicked
32336          * @param {Grid} this
32337          * @param {Number} rowIndex
32338          * @param {Roo.EventObject} e
32339          */
32340         "rowdblclick" : true,
32341         /**
32342          * @event headerclick
32343          * Fires when a header is clicked
32344          * @param {Grid} this
32345          * @param {Number} columnIndex
32346          * @param {Roo.EventObject} e
32347          */
32348         "headerclick" : true,
32349         /**
32350          * @event headerdblclick
32351          * Fires when a header cell is double clicked
32352          * @param {Grid} this
32353          * @param {Number} columnIndex
32354          * @param {Roo.EventObject} e
32355          */
32356         "headerdblclick" : true,
32357         /**
32358          * @event rowcontextmenu
32359          * Fires when a row is right clicked
32360          * @param {Grid} this
32361          * @param {Number} rowIndex
32362          * @param {Roo.EventObject} e
32363          */
32364         "rowcontextmenu" : true,
32365         /**
32366          * @event cellcontextmenu
32367          * Fires when a cell is right clicked
32368          * @param {Grid} this
32369          * @param {Number} rowIndex
32370          * @param {Number} cellIndex
32371          * @param {Roo.EventObject} e
32372          */
32373          "cellcontextmenu" : true,
32374         /**
32375          * @event headercontextmenu
32376          * Fires when a header is right clicked
32377          * @param {Grid} this
32378          * @param {Number} columnIndex
32379          * @param {Roo.EventObject} e
32380          */
32381         "headercontextmenu" : true,
32382         /**
32383          * @event bodyscroll
32384          * Fires when the body element is scrolled
32385          * @param {Number} scrollLeft
32386          * @param {Number} scrollTop
32387          */
32388         "bodyscroll" : true,
32389         /**
32390          * @event columnresize
32391          * Fires when the user resizes a column
32392          * @param {Number} columnIndex
32393          * @param {Number} newSize
32394          */
32395         "columnresize" : true,
32396         /**
32397          * @event columnmove
32398          * Fires when the user moves a column
32399          * @param {Number} oldIndex
32400          * @param {Number} newIndex
32401          */
32402         "columnmove" : true,
32403         /**
32404          * @event startdrag
32405          * Fires when row(s) start being dragged
32406          * @param {Grid} this
32407          * @param {Roo.GridDD} dd The drag drop object
32408          * @param {event} e The raw browser event
32409          */
32410         "startdrag" : true,
32411         /**
32412          * @event enddrag
32413          * Fires when a drag operation is complete
32414          * @param {Grid} this
32415          * @param {Roo.GridDD} dd The drag drop object
32416          * @param {event} e The raw browser event
32417          */
32418         "enddrag" : true,
32419         /**
32420          * @event dragdrop
32421          * Fires when dragged row(s) are dropped on a valid DD target
32422          * @param {Grid} this
32423          * @param {Roo.GridDD} dd The drag drop object
32424          * @param {String} targetId The target drag drop object
32425          * @param {event} e The raw browser event
32426          */
32427         "dragdrop" : true,
32428         /**
32429          * @event dragover
32430          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32431          * @param {Grid} this
32432          * @param {Roo.GridDD} dd The drag drop object
32433          * @param {String} targetId The target drag drop object
32434          * @param {event} e The raw browser event
32435          */
32436         "dragover" : true,
32437         /**
32438          * @event dragenter
32439          *  Fires when the dragged row(s) first cross another DD target while being dragged
32440          * @param {Grid} this
32441          * @param {Roo.GridDD} dd The drag drop object
32442          * @param {String} targetId The target drag drop object
32443          * @param {event} e The raw browser event
32444          */
32445         "dragenter" : true,
32446         /**
32447          * @event dragout
32448          * Fires when the dragged row(s) leave another DD target while being dragged
32449          * @param {Grid} this
32450          * @param {Roo.GridDD} dd The drag drop object
32451          * @param {String} targetId The target drag drop object
32452          * @param {event} e The raw browser event
32453          */
32454         "dragout" : true,
32455         /**
32456          * @event rowclass
32457          * Fires when a row is rendered, so you can change add a style to it.
32458          * @param {GridView} gridview   The grid view
32459          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32460          */
32461         'rowclass' : true,
32462
32463         /**
32464          * @event render
32465          * Fires when the grid is rendered
32466          * @param {Grid} grid
32467          */
32468         'render' : true
32469     });
32470
32471     Roo.grid.Grid.superclass.constructor.call(this);
32472 };
32473 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32474     
32475     /**
32476          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
32477          */
32478         /**
32479          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
32480          */
32481         /**
32482          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
32483          */
32484         /**
32485          * @cfg {Roo.grid.Store} ds The data store for the grid
32486          */
32487         /**
32488          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
32489          */
32490         /**
32491      * @cfg {String} ddGroup - drag drop group.
32492      */
32493       /**
32494      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
32495      */
32496
32497     /**
32498      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32499      */
32500     minColumnWidth : 25,
32501
32502     /**
32503      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32504      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32505      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32506      */
32507     autoSizeColumns : false,
32508
32509     /**
32510      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32511      */
32512     autoSizeHeaders : true,
32513
32514     /**
32515      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32516      */
32517     monitorWindowResize : true,
32518
32519     /**
32520      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32521      * rows measured to get a columns size. Default is 0 (all rows).
32522      */
32523     maxRowsToMeasure : 0,
32524
32525     /**
32526      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32527      */
32528     trackMouseOver : true,
32529
32530     /**
32531     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32532     */
32533       /**
32534     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
32535     */
32536     
32537     /**
32538     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32539     */
32540     enableDragDrop : false,
32541     
32542     /**
32543     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32544     */
32545     enableColumnMove : true,
32546     
32547     /**
32548     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32549     */
32550     enableColumnHide : true,
32551     
32552     /**
32553     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32554     */
32555     enableRowHeightSync : false,
32556     
32557     /**
32558     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32559     */
32560     stripeRows : true,
32561     
32562     /**
32563     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32564     */
32565     autoHeight : false,
32566
32567     /**
32568      * @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.
32569      */
32570     autoExpandColumn : false,
32571
32572     /**
32573     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32574     * Default is 50.
32575     */
32576     autoExpandMin : 50,
32577
32578     /**
32579     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32580     */
32581     autoExpandMax : 1000,
32582
32583     /**
32584     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32585     */
32586     view : null,
32587
32588     /**
32589     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32590     */
32591     loadMask : false,
32592     /**
32593     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32594     */
32595     dropTarget: false,
32596     
32597    
32598     
32599     // private
32600     rendered : false,
32601
32602     /**
32603     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32604     * of a fixed width. Default is false.
32605     */
32606     /**
32607     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32608     */
32609     
32610     
32611     /**
32612     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32613     * %0 is replaced with the number of selected rows.
32614     */
32615     ddText : "{0} selected row{1}",
32616     
32617     
32618     /**
32619      * Called once after all setup has been completed and the grid is ready to be rendered.
32620      * @return {Roo.grid.Grid} this
32621      */
32622     render : function()
32623     {
32624         var c = this.container;
32625         // try to detect autoHeight/width mode
32626         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32627             this.autoHeight = true;
32628         }
32629         var view = this.getView();
32630         view.init(this);
32631
32632         c.on("click", this.onClick, this);
32633         c.on("dblclick", this.onDblClick, this);
32634         c.on("contextmenu", this.onContextMenu, this);
32635         c.on("keydown", this.onKeyDown, this);
32636         if (Roo.isTouch) {
32637             c.on("touchstart", this.onTouchStart, this);
32638         }
32639
32640         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32641
32642         this.getSelectionModel().init(this);
32643
32644         view.render();
32645
32646         if(this.loadMask){
32647             this.loadMask = new Roo.LoadMask(this.container,
32648                     Roo.apply({store:this.dataSource}, this.loadMask));
32649         }
32650         
32651         
32652         if (this.toolbar && this.toolbar.xtype) {
32653             this.toolbar.container = this.getView().getHeaderPanel(true);
32654             this.toolbar = new Roo.Toolbar(this.toolbar);
32655         }
32656         if (this.footer && this.footer.xtype) {
32657             this.footer.dataSource = this.getDataSource();
32658             this.footer.container = this.getView().getFooterPanel(true);
32659             this.footer = Roo.factory(this.footer, Roo);
32660         }
32661         if (this.dropTarget && this.dropTarget.xtype) {
32662             delete this.dropTarget.xtype;
32663             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32664         }
32665         
32666         
32667         this.rendered = true;
32668         this.fireEvent('render', this);
32669         return this;
32670     },
32671
32672     /**
32673      * Reconfigures the grid to use a different Store and Column Model.
32674      * The View will be bound to the new objects and refreshed.
32675      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32676      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32677      */
32678     reconfigure : function(dataSource, colModel){
32679         if(this.loadMask){
32680             this.loadMask.destroy();
32681             this.loadMask = new Roo.LoadMask(this.container,
32682                     Roo.apply({store:dataSource}, this.loadMask));
32683         }
32684         this.view.bind(dataSource, colModel);
32685         this.dataSource = dataSource;
32686         this.colModel = colModel;
32687         this.view.refresh(true);
32688     },
32689     /**
32690      * addColumns
32691      * Add's a column, default at the end..
32692      
32693      * @param {int} position to add (default end)
32694      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
32695      */
32696     addColumns : function(pos, ar)
32697     {
32698         
32699         for (var i =0;i< ar.length;i++) {
32700             var cfg = ar[i];
32701             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32702             this.cm.lookup[cfg.id] = cfg;
32703         }
32704         
32705         
32706         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32707             pos = this.cm.config.length; //this.cm.config.push(cfg);
32708         } 
32709         pos = Math.max(0,pos);
32710         ar.unshift(0);
32711         ar.unshift(pos);
32712         this.cm.config.splice.apply(this.cm.config, ar);
32713         
32714         
32715         
32716         this.view.generateRules(this.cm);
32717         this.view.refresh(true);
32718         
32719     },
32720     
32721     
32722     
32723     
32724     // private
32725     onKeyDown : function(e){
32726         this.fireEvent("keydown", e);
32727     },
32728
32729     /**
32730      * Destroy this grid.
32731      * @param {Boolean} removeEl True to remove the element
32732      */
32733     destroy : function(removeEl, keepListeners){
32734         if(this.loadMask){
32735             this.loadMask.destroy();
32736         }
32737         var c = this.container;
32738         c.removeAllListeners();
32739         this.view.destroy();
32740         this.colModel.purgeListeners();
32741         if(!keepListeners){
32742             this.purgeListeners();
32743         }
32744         c.update("");
32745         if(removeEl === true){
32746             c.remove();
32747         }
32748     },
32749
32750     // private
32751     processEvent : function(name, e){
32752         // does this fire select???
32753         //Roo.log('grid:processEvent '  + name);
32754         
32755         if (name != 'touchstart' ) {
32756             this.fireEvent(name, e);    
32757         }
32758         
32759         var t = e.getTarget();
32760         var v = this.view;
32761         var header = v.findHeaderIndex(t);
32762         if(header !== false){
32763             var ename = name == 'touchstart' ? 'click' : name;
32764              
32765             this.fireEvent("header" + ename, this, header, e);
32766         }else{
32767             var row = v.findRowIndex(t);
32768             var cell = v.findCellIndex(t);
32769             if (name == 'touchstart') {
32770                 // first touch is always a click.
32771                 // hopefull this happens after selection is updated.?
32772                 name = false;
32773                 
32774                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32775                     var cs = this.selModel.getSelectedCell();
32776                     if (row == cs[0] && cell == cs[1]){
32777                         name = 'dblclick';
32778                     }
32779                 }
32780                 if (typeof(this.selModel.getSelections) != 'undefined') {
32781                     var cs = this.selModel.getSelections();
32782                     var ds = this.dataSource;
32783                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32784                         name = 'dblclick';
32785                     }
32786                 }
32787                 if (!name) {
32788                     return;
32789                 }
32790             }
32791             
32792             
32793             if(row !== false){
32794                 this.fireEvent("row" + name, this, row, e);
32795                 if(cell !== false){
32796                     this.fireEvent("cell" + name, this, row, cell, e);
32797                 }
32798             }
32799         }
32800     },
32801
32802     // private
32803     onClick : function(e){
32804         this.processEvent("click", e);
32805     },
32806    // private
32807     onTouchStart : function(e){
32808         this.processEvent("touchstart", e);
32809     },
32810
32811     // private
32812     onContextMenu : function(e, t){
32813         this.processEvent("contextmenu", e);
32814     },
32815
32816     // private
32817     onDblClick : function(e){
32818         this.processEvent("dblclick", e);
32819     },
32820
32821     // private
32822     walkCells : function(row, col, step, fn, scope){
32823         var cm = this.colModel, clen = cm.getColumnCount();
32824         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32825         if(step < 0){
32826             if(col < 0){
32827                 row--;
32828                 first = false;
32829             }
32830             while(row >= 0){
32831                 if(!first){
32832                     col = clen-1;
32833                 }
32834                 first = false;
32835                 while(col >= 0){
32836                     if(fn.call(scope || this, row, col, cm) === true){
32837                         return [row, col];
32838                     }
32839                     col--;
32840                 }
32841                 row--;
32842             }
32843         } else {
32844             if(col >= clen){
32845                 row++;
32846                 first = false;
32847             }
32848             while(row < rlen){
32849                 if(!first){
32850                     col = 0;
32851                 }
32852                 first = false;
32853                 while(col < clen){
32854                     if(fn.call(scope || this, row, col, cm) === true){
32855                         return [row, col];
32856                     }
32857                     col++;
32858                 }
32859                 row++;
32860             }
32861         }
32862         return null;
32863     },
32864
32865     // private
32866     getSelections : function(){
32867         return this.selModel.getSelections();
32868     },
32869
32870     /**
32871      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32872      * but if manual update is required this method will initiate it.
32873      */
32874     autoSize : function(){
32875         if(this.rendered){
32876             this.view.layout();
32877             if(this.view.adjustForScroll){
32878                 this.view.adjustForScroll();
32879             }
32880         }
32881     },
32882
32883     /**
32884      * Returns the grid's underlying element.
32885      * @return {Element} The element
32886      */
32887     getGridEl : function(){
32888         return this.container;
32889     },
32890
32891     // private for compatibility, overridden by editor grid
32892     stopEditing : function(){},
32893
32894     /**
32895      * Returns the grid's SelectionModel.
32896      * @return {SelectionModel}
32897      */
32898     getSelectionModel : function(){
32899         if(!this.selModel){
32900             this.selModel = new Roo.grid.RowSelectionModel();
32901         }
32902         return this.selModel;
32903     },
32904
32905     /**
32906      * Returns the grid's DataSource.
32907      * @return {DataSource}
32908      */
32909     getDataSource : function(){
32910         return this.dataSource;
32911     },
32912
32913     /**
32914      * Returns the grid's ColumnModel.
32915      * @return {ColumnModel}
32916      */
32917     getColumnModel : function(){
32918         return this.colModel;
32919     },
32920
32921     /**
32922      * Returns the grid's GridView object.
32923      * @return {GridView}
32924      */
32925     getView : function(){
32926         if(!this.view){
32927             this.view = new Roo.grid.GridView(this.viewConfig);
32928             this.relayEvents(this.view, [
32929                 "beforerowremoved", "beforerowsinserted",
32930                 "beforerefresh", "rowremoved",
32931                 "rowsinserted", "rowupdated" ,"refresh"
32932             ]);
32933         }
32934         return this.view;
32935     },
32936     /**
32937      * Called to get grid's drag proxy text, by default returns this.ddText.
32938      * Override this to put something different in the dragged text.
32939      * @return {String}
32940      */
32941     getDragDropText : function(){
32942         var count = this.selModel.getCount();
32943         return String.format(this.ddText, count, count == 1 ? '' : 's');
32944     }
32945 });
32946 /*
32947  * Based on:
32948  * Ext JS Library 1.1.1
32949  * Copyright(c) 2006-2007, Ext JS, LLC.
32950  *
32951  * Originally Released Under LGPL - original licence link has changed is not relivant.
32952  *
32953  * Fork - LGPL
32954  * <script type="text/javascript">
32955  */
32956  /**
32957  * @class Roo.grid.AbstractGridView
32958  * @extends Roo.util.Observable
32959  * @abstract
32960  * Abstract base class for grid Views
32961  * @constructor
32962  */
32963 Roo.grid.AbstractGridView = function(){
32964         this.grid = null;
32965         
32966         this.events = {
32967             "beforerowremoved" : true,
32968             "beforerowsinserted" : true,
32969             "beforerefresh" : true,
32970             "rowremoved" : true,
32971             "rowsinserted" : true,
32972             "rowupdated" : true,
32973             "refresh" : true
32974         };
32975     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32976 };
32977
32978 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32979     rowClass : "x-grid-row",
32980     cellClass : "x-grid-cell",
32981     tdClass : "x-grid-td",
32982     hdClass : "x-grid-hd",
32983     splitClass : "x-grid-hd-split",
32984     
32985     init: function(grid){
32986         this.grid = grid;
32987                 var cid = this.grid.getGridEl().id;
32988         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32989         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32990         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32991         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32992         },
32993         
32994     getColumnRenderers : function(){
32995         var renderers = [];
32996         var cm = this.grid.colModel;
32997         var colCount = cm.getColumnCount();
32998         for(var i = 0; i < colCount; i++){
32999             renderers[i] = cm.getRenderer(i);
33000         }
33001         return renderers;
33002     },
33003     
33004     getColumnIds : function(){
33005         var ids = [];
33006         var cm = this.grid.colModel;
33007         var colCount = cm.getColumnCount();
33008         for(var i = 0; i < colCount; i++){
33009             ids[i] = cm.getColumnId(i);
33010         }
33011         return ids;
33012     },
33013     
33014     getDataIndexes : function(){
33015         if(!this.indexMap){
33016             this.indexMap = this.buildIndexMap();
33017         }
33018         return this.indexMap.colToData;
33019     },
33020     
33021     getColumnIndexByDataIndex : function(dataIndex){
33022         if(!this.indexMap){
33023             this.indexMap = this.buildIndexMap();
33024         }
33025         return this.indexMap.dataToCol[dataIndex];
33026     },
33027     
33028     /**
33029      * Set a css style for a column dynamically. 
33030      * @param {Number} colIndex The index of the column
33031      * @param {String} name The css property name
33032      * @param {String} value The css value
33033      */
33034     setCSSStyle : function(colIndex, name, value){
33035         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33036         Roo.util.CSS.updateRule(selector, name, value);
33037     },
33038     
33039     generateRules : function(cm){
33040         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33041         Roo.util.CSS.removeStyleSheet(rulesId);
33042         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33043             var cid = cm.getColumnId(i);
33044             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33045                          this.tdSelector, cid, " {\n}\n",
33046                          this.hdSelector, cid, " {\n}\n",
33047                          this.splitSelector, cid, " {\n}\n");
33048         }
33049         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33050     }
33051 });/*
33052  * Based on:
33053  * Ext JS Library 1.1.1
33054  * Copyright(c) 2006-2007, Ext JS, LLC.
33055  *
33056  * Originally Released Under LGPL - original licence link has changed is not relivant.
33057  *
33058  * Fork - LGPL
33059  * <script type="text/javascript">
33060  */
33061
33062 // private
33063 // This is a support class used internally by the Grid components
33064 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33065     this.grid = grid;
33066     this.view = grid.getView();
33067     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33068     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33069     if(hd2){
33070         this.setHandleElId(Roo.id(hd));
33071         this.setOuterHandleElId(Roo.id(hd2));
33072     }
33073     this.scroll = false;
33074 };
33075 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33076     maxDragWidth: 120,
33077     getDragData : function(e){
33078         var t = Roo.lib.Event.getTarget(e);
33079         var h = this.view.findHeaderCell(t);
33080         if(h){
33081             return {ddel: h.firstChild, header:h};
33082         }
33083         return false;
33084     },
33085
33086     onInitDrag : function(e){
33087         this.view.headersDisabled = true;
33088         var clone = this.dragData.ddel.cloneNode(true);
33089         clone.id = Roo.id();
33090         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33091         this.proxy.update(clone);
33092         return true;
33093     },
33094
33095     afterValidDrop : function(){
33096         var v = this.view;
33097         setTimeout(function(){
33098             v.headersDisabled = false;
33099         }, 50);
33100     },
33101
33102     afterInvalidDrop : function(){
33103         var v = this.view;
33104         setTimeout(function(){
33105             v.headersDisabled = false;
33106         }, 50);
33107     }
33108 });
33109 /*
33110  * Based on:
33111  * Ext JS Library 1.1.1
33112  * Copyright(c) 2006-2007, Ext JS, LLC.
33113  *
33114  * Originally Released Under LGPL - original licence link has changed is not relivant.
33115  *
33116  * Fork - LGPL
33117  * <script type="text/javascript">
33118  */
33119 // private
33120 // This is a support class used internally by the Grid components
33121 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33122     this.grid = grid;
33123     this.view = grid.getView();
33124     // split the proxies so they don't interfere with mouse events
33125     this.proxyTop = Roo.DomHelper.append(document.body, {
33126         cls:"col-move-top", html:"&#160;"
33127     }, true);
33128     this.proxyBottom = Roo.DomHelper.append(document.body, {
33129         cls:"col-move-bottom", html:"&#160;"
33130     }, true);
33131     this.proxyTop.hide = this.proxyBottom.hide = function(){
33132         this.setLeftTop(-100,-100);
33133         this.setStyle("visibility", "hidden");
33134     };
33135     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33136     // temporarily disabled
33137     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33138     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33139 };
33140 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33141     proxyOffsets : [-4, -9],
33142     fly: Roo.Element.fly,
33143
33144     getTargetFromEvent : function(e){
33145         var t = Roo.lib.Event.getTarget(e);
33146         var cindex = this.view.findCellIndex(t);
33147         if(cindex !== false){
33148             return this.view.getHeaderCell(cindex);
33149         }
33150         return null;
33151     },
33152
33153     nextVisible : function(h){
33154         var v = this.view, cm = this.grid.colModel;
33155         h = h.nextSibling;
33156         while(h){
33157             if(!cm.isHidden(v.getCellIndex(h))){
33158                 return h;
33159             }
33160             h = h.nextSibling;
33161         }
33162         return null;
33163     },
33164
33165     prevVisible : function(h){
33166         var v = this.view, cm = this.grid.colModel;
33167         h = h.prevSibling;
33168         while(h){
33169             if(!cm.isHidden(v.getCellIndex(h))){
33170                 return h;
33171             }
33172             h = h.prevSibling;
33173         }
33174         return null;
33175     },
33176
33177     positionIndicator : function(h, n, e){
33178         var x = Roo.lib.Event.getPageX(e);
33179         var r = Roo.lib.Dom.getRegion(n.firstChild);
33180         var px, pt, py = r.top + this.proxyOffsets[1];
33181         if((r.right - x) <= (r.right-r.left)/2){
33182             px = r.right+this.view.borderWidth;
33183             pt = "after";
33184         }else{
33185             px = r.left;
33186             pt = "before";
33187         }
33188         var oldIndex = this.view.getCellIndex(h);
33189         var newIndex = this.view.getCellIndex(n);
33190
33191         if(this.grid.colModel.isFixed(newIndex)){
33192             return false;
33193         }
33194
33195         var locked = this.grid.colModel.isLocked(newIndex);
33196
33197         if(pt == "after"){
33198             newIndex++;
33199         }
33200         if(oldIndex < newIndex){
33201             newIndex--;
33202         }
33203         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33204             return false;
33205         }
33206         px +=  this.proxyOffsets[0];
33207         this.proxyTop.setLeftTop(px, py);
33208         this.proxyTop.show();
33209         if(!this.bottomOffset){
33210             this.bottomOffset = this.view.mainHd.getHeight();
33211         }
33212         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33213         this.proxyBottom.show();
33214         return pt;
33215     },
33216
33217     onNodeEnter : function(n, dd, e, data){
33218         if(data.header != n){
33219             this.positionIndicator(data.header, n, e);
33220         }
33221     },
33222
33223     onNodeOver : function(n, dd, e, data){
33224         var result = false;
33225         if(data.header != n){
33226             result = this.positionIndicator(data.header, n, e);
33227         }
33228         if(!result){
33229             this.proxyTop.hide();
33230             this.proxyBottom.hide();
33231         }
33232         return result ? this.dropAllowed : this.dropNotAllowed;
33233     },
33234
33235     onNodeOut : function(n, dd, e, data){
33236         this.proxyTop.hide();
33237         this.proxyBottom.hide();
33238     },
33239
33240     onNodeDrop : function(n, dd, e, data){
33241         var h = data.header;
33242         if(h != n){
33243             var cm = this.grid.colModel;
33244             var x = Roo.lib.Event.getPageX(e);
33245             var r = Roo.lib.Dom.getRegion(n.firstChild);
33246             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33247             var oldIndex = this.view.getCellIndex(h);
33248             var newIndex = this.view.getCellIndex(n);
33249             var locked = cm.isLocked(newIndex);
33250             if(pt == "after"){
33251                 newIndex++;
33252             }
33253             if(oldIndex < newIndex){
33254                 newIndex--;
33255             }
33256             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33257                 return false;
33258             }
33259             cm.setLocked(oldIndex, locked, true);
33260             cm.moveColumn(oldIndex, newIndex);
33261             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33262             return true;
33263         }
33264         return false;
33265     }
33266 });
33267 /*
33268  * Based on:
33269  * Ext JS Library 1.1.1
33270  * Copyright(c) 2006-2007, Ext JS, LLC.
33271  *
33272  * Originally Released Under LGPL - original licence link has changed is not relivant.
33273  *
33274  * Fork - LGPL
33275  * <script type="text/javascript">
33276  */
33277   
33278 /**
33279  * @class Roo.grid.GridView
33280  * @extends Roo.util.Observable
33281  *
33282  * @constructor
33283  * @param {Object} config
33284  */
33285 Roo.grid.GridView = function(config){
33286     Roo.grid.GridView.superclass.constructor.call(this);
33287     this.el = null;
33288
33289     Roo.apply(this, config);
33290 };
33291
33292 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33293
33294     unselectable :  'unselectable="on"',
33295     unselectableCls :  'x-unselectable',
33296     
33297     
33298     rowClass : "x-grid-row",
33299
33300     cellClass : "x-grid-col",
33301
33302     tdClass : "x-grid-td",
33303
33304     hdClass : "x-grid-hd",
33305
33306     splitClass : "x-grid-split",
33307
33308     sortClasses : ["sort-asc", "sort-desc"],
33309
33310     enableMoveAnim : false,
33311
33312     hlColor: "C3DAF9",
33313
33314     dh : Roo.DomHelper,
33315
33316     fly : Roo.Element.fly,
33317
33318     css : Roo.util.CSS,
33319
33320     borderWidth: 1,
33321
33322     splitOffset: 3,
33323
33324     scrollIncrement : 22,
33325
33326     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33327
33328     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33329
33330     bind : function(ds, cm){
33331         if(this.ds){
33332             this.ds.un("load", this.onLoad, this);
33333             this.ds.un("datachanged", this.onDataChange, this);
33334             this.ds.un("add", this.onAdd, this);
33335             this.ds.un("remove", this.onRemove, this);
33336             this.ds.un("update", this.onUpdate, this);
33337             this.ds.un("clear", this.onClear, this);
33338         }
33339         if(ds){
33340             ds.on("load", this.onLoad, this);
33341             ds.on("datachanged", this.onDataChange, this);
33342             ds.on("add", this.onAdd, this);
33343             ds.on("remove", this.onRemove, this);
33344             ds.on("update", this.onUpdate, this);
33345             ds.on("clear", this.onClear, this);
33346         }
33347         this.ds = ds;
33348
33349         if(this.cm){
33350             this.cm.un("widthchange", this.onColWidthChange, this);
33351             this.cm.un("headerchange", this.onHeaderChange, this);
33352             this.cm.un("hiddenchange", this.onHiddenChange, this);
33353             this.cm.un("columnmoved", this.onColumnMove, this);
33354             this.cm.un("columnlockchange", this.onColumnLock, this);
33355         }
33356         if(cm){
33357             this.generateRules(cm);
33358             cm.on("widthchange", this.onColWidthChange, this);
33359             cm.on("headerchange", this.onHeaderChange, this);
33360             cm.on("hiddenchange", this.onHiddenChange, this);
33361             cm.on("columnmoved", this.onColumnMove, this);
33362             cm.on("columnlockchange", this.onColumnLock, this);
33363         }
33364         this.cm = cm;
33365     },
33366
33367     init: function(grid){
33368         Roo.grid.GridView.superclass.init.call(this, grid);
33369
33370         this.bind(grid.dataSource, grid.colModel);
33371
33372         grid.on("headerclick", this.handleHeaderClick, this);
33373
33374         if(grid.trackMouseOver){
33375             grid.on("mouseover", this.onRowOver, this);
33376             grid.on("mouseout", this.onRowOut, this);
33377         }
33378         grid.cancelTextSelection = function(){};
33379         this.gridId = grid.id;
33380
33381         var tpls = this.templates || {};
33382
33383         if(!tpls.master){
33384             tpls.master = new Roo.Template(
33385                '<div class="x-grid" hidefocus="true">',
33386                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33387                   '<div class="x-grid-topbar"></div>',
33388                   '<div class="x-grid-scroller"><div></div></div>',
33389                   '<div class="x-grid-locked">',
33390                       '<div class="x-grid-header">{lockedHeader}</div>',
33391                       '<div class="x-grid-body">{lockedBody}</div>',
33392                   "</div>",
33393                   '<div class="x-grid-viewport">',
33394                       '<div class="x-grid-header">{header}</div>',
33395                       '<div class="x-grid-body">{body}</div>',
33396                   "</div>",
33397                   '<div class="x-grid-bottombar"></div>',
33398                  
33399                   '<div class="x-grid-resize-proxy">&#160;</div>',
33400                "</div>"
33401             );
33402             tpls.master.disableformats = true;
33403         }
33404
33405         if(!tpls.header){
33406             tpls.header = new Roo.Template(
33407                '<table border="0" cellspacing="0" cellpadding="0">',
33408                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33409                "</table>{splits}"
33410             );
33411             tpls.header.disableformats = true;
33412         }
33413         tpls.header.compile();
33414
33415         if(!tpls.hcell){
33416             tpls.hcell = new Roo.Template(
33417                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33418                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33419                 "</div></td>"
33420              );
33421              tpls.hcell.disableFormats = true;
33422         }
33423         tpls.hcell.compile();
33424
33425         if(!tpls.hsplit){
33426             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33427                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33428             tpls.hsplit.disableFormats = true;
33429         }
33430         tpls.hsplit.compile();
33431
33432         if(!tpls.body){
33433             tpls.body = new Roo.Template(
33434                '<table border="0" cellspacing="0" cellpadding="0">',
33435                "<tbody>{rows}</tbody>",
33436                "</table>"
33437             );
33438             tpls.body.disableFormats = true;
33439         }
33440         tpls.body.compile();
33441
33442         if(!tpls.row){
33443             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33444             tpls.row.disableFormats = true;
33445         }
33446         tpls.row.compile();
33447
33448         if(!tpls.cell){
33449             tpls.cell = new Roo.Template(
33450                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33451                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33452                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33453                 "</td>"
33454             );
33455             tpls.cell.disableFormats = true;
33456         }
33457         tpls.cell.compile();
33458
33459         this.templates = tpls;
33460     },
33461
33462     // remap these for backwards compat
33463     onColWidthChange : function(){
33464         this.updateColumns.apply(this, arguments);
33465     },
33466     onHeaderChange : function(){
33467         this.updateHeaders.apply(this, arguments);
33468     }, 
33469     onHiddenChange : function(){
33470         this.handleHiddenChange.apply(this, arguments);
33471     },
33472     onColumnMove : function(){
33473         this.handleColumnMove.apply(this, arguments);
33474     },
33475     onColumnLock : function(){
33476         this.handleLockChange.apply(this, arguments);
33477     },
33478
33479     onDataChange : function(){
33480         this.refresh();
33481         this.updateHeaderSortState();
33482     },
33483
33484     onClear : function(){
33485         this.refresh();
33486     },
33487
33488     onUpdate : function(ds, record){
33489         this.refreshRow(record);
33490     },
33491
33492     refreshRow : function(record){
33493         var ds = this.ds, index;
33494         if(typeof record == 'number'){
33495             index = record;
33496             record = ds.getAt(index);
33497         }else{
33498             index = ds.indexOf(record);
33499         }
33500         this.insertRows(ds, index, index, true);
33501         this.onRemove(ds, record, index+1, true);
33502         this.syncRowHeights(index, index);
33503         this.layout();
33504         this.fireEvent("rowupdated", this, index, record);
33505     },
33506
33507     onAdd : function(ds, records, index){
33508         this.insertRows(ds, index, index + (records.length-1));
33509     },
33510
33511     onRemove : function(ds, record, index, isUpdate){
33512         if(isUpdate !== true){
33513             this.fireEvent("beforerowremoved", this, index, record);
33514         }
33515         var bt = this.getBodyTable(), lt = this.getLockedTable();
33516         if(bt.rows[index]){
33517             bt.firstChild.removeChild(bt.rows[index]);
33518         }
33519         if(lt.rows[index]){
33520             lt.firstChild.removeChild(lt.rows[index]);
33521         }
33522         if(isUpdate !== true){
33523             this.stripeRows(index);
33524             this.syncRowHeights(index, index);
33525             this.layout();
33526             this.fireEvent("rowremoved", this, index, record);
33527         }
33528     },
33529
33530     onLoad : function(){
33531         this.scrollToTop();
33532     },
33533
33534     /**
33535      * Scrolls the grid to the top
33536      */
33537     scrollToTop : function(){
33538         if(this.scroller){
33539             this.scroller.dom.scrollTop = 0;
33540             this.syncScroll();
33541         }
33542     },
33543
33544     /**
33545      * Gets a panel in the header of the grid that can be used for toolbars etc.
33546      * After modifying the contents of this panel a call to grid.autoSize() may be
33547      * required to register any changes in size.
33548      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33549      * @return Roo.Element
33550      */
33551     getHeaderPanel : function(doShow){
33552         if(doShow){
33553             this.headerPanel.show();
33554         }
33555         return this.headerPanel;
33556     },
33557
33558     /**
33559      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33560      * After modifying the contents of this panel a call to grid.autoSize() may be
33561      * required to register any changes in size.
33562      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33563      * @return Roo.Element
33564      */
33565     getFooterPanel : function(doShow){
33566         if(doShow){
33567             this.footerPanel.show();
33568         }
33569         return this.footerPanel;
33570     },
33571
33572     initElements : function(){
33573         var E = Roo.Element;
33574         var el = this.grid.getGridEl().dom.firstChild;
33575         var cs = el.childNodes;
33576
33577         this.el = new E(el);
33578         
33579          this.focusEl = new E(el.firstChild);
33580         this.focusEl.swallowEvent("click", true);
33581         
33582         this.headerPanel = new E(cs[1]);
33583         this.headerPanel.enableDisplayMode("block");
33584
33585         this.scroller = new E(cs[2]);
33586         this.scrollSizer = new E(this.scroller.dom.firstChild);
33587
33588         this.lockedWrap = new E(cs[3]);
33589         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33590         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33591
33592         this.mainWrap = new E(cs[4]);
33593         this.mainHd = new E(this.mainWrap.dom.firstChild);
33594         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33595
33596         this.footerPanel = new E(cs[5]);
33597         this.footerPanel.enableDisplayMode("block");
33598
33599         this.resizeProxy = new E(cs[6]);
33600
33601         this.headerSelector = String.format(
33602            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33603            this.lockedHd.id, this.mainHd.id
33604         );
33605
33606         this.splitterSelector = String.format(
33607            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33608            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33609         );
33610     },
33611     idToCssName : function(s)
33612     {
33613         return s.replace(/[^a-z0-9]+/ig, '-');
33614     },
33615
33616     getHeaderCell : function(index){
33617         return Roo.DomQuery.select(this.headerSelector)[index];
33618     },
33619
33620     getHeaderCellMeasure : function(index){
33621         return this.getHeaderCell(index).firstChild;
33622     },
33623
33624     getHeaderCellText : function(index){
33625         return this.getHeaderCell(index).firstChild.firstChild;
33626     },
33627
33628     getLockedTable : function(){
33629         return this.lockedBody.dom.firstChild;
33630     },
33631
33632     getBodyTable : function(){
33633         return this.mainBody.dom.firstChild;
33634     },
33635
33636     getLockedRow : function(index){
33637         return this.getLockedTable().rows[index];
33638     },
33639
33640     getRow : function(index){
33641         return this.getBodyTable().rows[index];
33642     },
33643
33644     getRowComposite : function(index){
33645         if(!this.rowEl){
33646             this.rowEl = new Roo.CompositeElementLite();
33647         }
33648         var els = [], lrow, mrow;
33649         if(lrow = this.getLockedRow(index)){
33650             els.push(lrow);
33651         }
33652         if(mrow = this.getRow(index)){
33653             els.push(mrow);
33654         }
33655         this.rowEl.elements = els;
33656         return this.rowEl;
33657     },
33658     /**
33659      * Gets the 'td' of the cell
33660      * 
33661      * @param {Integer} rowIndex row to select
33662      * @param {Integer} colIndex column to select
33663      * 
33664      * @return {Object} 
33665      */
33666     getCell : function(rowIndex, colIndex){
33667         var locked = this.cm.getLockedCount();
33668         var source;
33669         if(colIndex < locked){
33670             source = this.lockedBody.dom.firstChild;
33671         }else{
33672             source = this.mainBody.dom.firstChild;
33673             colIndex -= locked;
33674         }
33675         return source.rows[rowIndex].childNodes[colIndex];
33676     },
33677
33678     getCellText : function(rowIndex, colIndex){
33679         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33680     },
33681
33682     getCellBox : function(cell){
33683         var b = this.fly(cell).getBox();
33684         if(Roo.isOpera){ // opera fails to report the Y
33685             b.y = cell.offsetTop + this.mainBody.getY();
33686         }
33687         return b;
33688     },
33689
33690     getCellIndex : function(cell){
33691         var id = String(cell.className).match(this.cellRE);
33692         if(id){
33693             return parseInt(id[1], 10);
33694         }
33695         return 0;
33696     },
33697
33698     findHeaderIndex : function(n){
33699         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33700         return r ? this.getCellIndex(r) : false;
33701     },
33702
33703     findHeaderCell : function(n){
33704         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33705         return r ? r : false;
33706     },
33707
33708     findRowIndex : function(n){
33709         if(!n){
33710             return false;
33711         }
33712         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33713         return r ? r.rowIndex : false;
33714     },
33715
33716     findCellIndex : function(node){
33717         var stop = this.el.dom;
33718         while(node && node != stop){
33719             if(this.findRE.test(node.className)){
33720                 return this.getCellIndex(node);
33721             }
33722             node = node.parentNode;
33723         }
33724         return false;
33725     },
33726
33727     getColumnId : function(index){
33728         return this.cm.getColumnId(index);
33729     },
33730
33731     getSplitters : function()
33732     {
33733         if(this.splitterSelector){
33734            return Roo.DomQuery.select(this.splitterSelector);
33735         }else{
33736             return null;
33737       }
33738     },
33739
33740     getSplitter : function(index){
33741         return this.getSplitters()[index];
33742     },
33743
33744     onRowOver : function(e, t){
33745         var row;
33746         if((row = this.findRowIndex(t)) !== false){
33747             this.getRowComposite(row).addClass("x-grid-row-over");
33748         }
33749     },
33750
33751     onRowOut : function(e, t){
33752         var row;
33753         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33754             this.getRowComposite(row).removeClass("x-grid-row-over");
33755         }
33756     },
33757
33758     renderHeaders : function(){
33759         var cm = this.cm;
33760         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33761         var cb = [], lb = [], sb = [], lsb = [], p = {};
33762         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33763             p.cellId = "x-grid-hd-0-" + i;
33764             p.splitId = "x-grid-csplit-0-" + i;
33765             p.id = cm.getColumnId(i);
33766             p.value = cm.getColumnHeader(i) || "";
33767             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33768             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33769             if(!cm.isLocked(i)){
33770                 cb[cb.length] = ct.apply(p);
33771                 sb[sb.length] = st.apply(p);
33772             }else{
33773                 lb[lb.length] = ct.apply(p);
33774                 lsb[lsb.length] = st.apply(p);
33775             }
33776         }
33777         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33778                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33779     },
33780
33781     updateHeaders : function(){
33782         var html = this.renderHeaders();
33783         this.lockedHd.update(html[0]);
33784         this.mainHd.update(html[1]);
33785     },
33786
33787     /**
33788      * Focuses the specified row.
33789      * @param {Number} row The row index
33790      */
33791     focusRow : function(row)
33792     {
33793         //Roo.log('GridView.focusRow');
33794         var x = this.scroller.dom.scrollLeft;
33795         this.focusCell(row, 0, false);
33796         this.scroller.dom.scrollLeft = x;
33797     },
33798
33799     /**
33800      * Focuses the specified cell.
33801      * @param {Number} row The row index
33802      * @param {Number} col The column index
33803      * @param {Boolean} hscroll false to disable horizontal scrolling
33804      */
33805     focusCell : function(row, col, hscroll)
33806     {
33807         //Roo.log('GridView.focusCell');
33808         var el = this.ensureVisible(row, col, hscroll);
33809         this.focusEl.alignTo(el, "tl-tl");
33810         if(Roo.isGecko){
33811             this.focusEl.focus();
33812         }else{
33813             this.focusEl.focus.defer(1, this.focusEl);
33814         }
33815     },
33816
33817     /**
33818      * Scrolls the specified cell into view
33819      * @param {Number} row The row index
33820      * @param {Number} col The column index
33821      * @param {Boolean} hscroll false to disable horizontal scrolling
33822      */
33823     ensureVisible : function(row, col, hscroll)
33824     {
33825         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33826         //return null; //disable for testing.
33827         if(typeof row != "number"){
33828             row = row.rowIndex;
33829         }
33830         if(row < 0 && row >= this.ds.getCount()){
33831             return  null;
33832         }
33833         col = (col !== undefined ? col : 0);
33834         var cm = this.grid.colModel;
33835         while(cm.isHidden(col)){
33836             col++;
33837         }
33838
33839         var el = this.getCell(row, col);
33840         if(!el){
33841             return null;
33842         }
33843         var c = this.scroller.dom;
33844
33845         var ctop = parseInt(el.offsetTop, 10);
33846         var cleft = parseInt(el.offsetLeft, 10);
33847         var cbot = ctop + el.offsetHeight;
33848         var cright = cleft + el.offsetWidth;
33849         
33850         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33851         var stop = parseInt(c.scrollTop, 10);
33852         var sleft = parseInt(c.scrollLeft, 10);
33853         var sbot = stop + ch;
33854         var sright = sleft + c.clientWidth;
33855         /*
33856         Roo.log('GridView.ensureVisible:' +
33857                 ' ctop:' + ctop +
33858                 ' c.clientHeight:' + c.clientHeight +
33859                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33860                 ' stop:' + stop +
33861                 ' cbot:' + cbot +
33862                 ' sbot:' + sbot +
33863                 ' ch:' + ch  
33864                 );
33865         */
33866         if(ctop < stop){
33867             c.scrollTop = ctop;
33868             //Roo.log("set scrolltop to ctop DISABLE?");
33869         }else if(cbot > sbot){
33870             //Roo.log("set scrolltop to cbot-ch");
33871             c.scrollTop = cbot-ch;
33872         }
33873         
33874         if(hscroll !== false){
33875             if(cleft < sleft){
33876                 c.scrollLeft = cleft;
33877             }else if(cright > sright){
33878                 c.scrollLeft = cright-c.clientWidth;
33879             }
33880         }
33881          
33882         return el;
33883     },
33884
33885     updateColumns : function(){
33886         this.grid.stopEditing();
33887         var cm = this.grid.colModel, colIds = this.getColumnIds();
33888         //var totalWidth = cm.getTotalWidth();
33889         var pos = 0;
33890         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33891             //if(cm.isHidden(i)) continue;
33892             var w = cm.getColumnWidth(i);
33893             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33894             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33895         }
33896         this.updateSplitters();
33897     },
33898
33899     generateRules : function(cm){
33900         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33901         Roo.util.CSS.removeStyleSheet(rulesId);
33902         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33903             var cid = cm.getColumnId(i);
33904             var align = '';
33905             if(cm.config[i].align){
33906                 align = 'text-align:'+cm.config[i].align+';';
33907             }
33908             var hidden = '';
33909             if(cm.isHidden(i)){
33910                 hidden = 'display:none;';
33911             }
33912             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33913             ruleBuf.push(
33914                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33915                     this.hdSelector, cid, " {\n", align, width, "}\n",
33916                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33917                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33918         }
33919         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33920     },
33921
33922     updateSplitters : function(){
33923         var cm = this.cm, s = this.getSplitters();
33924         if(s){ // splitters not created yet
33925             var pos = 0, locked = true;
33926             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33927                 if(cm.isHidden(i)) {
33928                     continue;
33929                 }
33930                 var w = cm.getColumnWidth(i); // make sure it's a number
33931                 if(!cm.isLocked(i) && locked){
33932                     pos = 0;
33933                     locked = false;
33934                 }
33935                 pos += w;
33936                 s[i].style.left = (pos-this.splitOffset) + "px";
33937             }
33938         }
33939     },
33940
33941     handleHiddenChange : function(colModel, colIndex, hidden){
33942         if(hidden){
33943             this.hideColumn(colIndex);
33944         }else{
33945             this.unhideColumn(colIndex);
33946         }
33947     },
33948
33949     hideColumn : function(colIndex){
33950         var cid = this.getColumnId(colIndex);
33951         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33952         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33953         if(Roo.isSafari){
33954             this.updateHeaders();
33955         }
33956         this.updateSplitters();
33957         this.layout();
33958     },
33959
33960     unhideColumn : function(colIndex){
33961         var cid = this.getColumnId(colIndex);
33962         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33963         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33964
33965         if(Roo.isSafari){
33966             this.updateHeaders();
33967         }
33968         this.updateSplitters();
33969         this.layout();
33970     },
33971
33972     insertRows : function(dm, firstRow, lastRow, isUpdate){
33973         if(firstRow == 0 && lastRow == dm.getCount()-1){
33974             this.refresh();
33975         }else{
33976             if(!isUpdate){
33977                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33978             }
33979             var s = this.getScrollState();
33980             var markup = this.renderRows(firstRow, lastRow);
33981             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33982             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33983             this.restoreScroll(s);
33984             if(!isUpdate){
33985                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33986                 this.syncRowHeights(firstRow, lastRow);
33987                 this.stripeRows(firstRow);
33988                 this.layout();
33989             }
33990         }
33991     },
33992
33993     bufferRows : function(markup, target, index){
33994         var before = null, trows = target.rows, tbody = target.tBodies[0];
33995         if(index < trows.length){
33996             before = trows[index];
33997         }
33998         var b = document.createElement("div");
33999         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34000         var rows = b.firstChild.rows;
34001         for(var i = 0, len = rows.length; i < len; i++){
34002             if(before){
34003                 tbody.insertBefore(rows[0], before);
34004             }else{
34005                 tbody.appendChild(rows[0]);
34006             }
34007         }
34008         b.innerHTML = "";
34009         b = null;
34010     },
34011
34012     deleteRows : function(dm, firstRow, lastRow){
34013         if(dm.getRowCount()<1){
34014             this.fireEvent("beforerefresh", this);
34015             this.mainBody.update("");
34016             this.lockedBody.update("");
34017             this.fireEvent("refresh", this);
34018         }else{
34019             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34020             var bt = this.getBodyTable();
34021             var tbody = bt.firstChild;
34022             var rows = bt.rows;
34023             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34024                 tbody.removeChild(rows[firstRow]);
34025             }
34026             this.stripeRows(firstRow);
34027             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34028         }
34029     },
34030
34031     updateRows : function(dataSource, firstRow, lastRow){
34032         var s = this.getScrollState();
34033         this.refresh();
34034         this.restoreScroll(s);
34035     },
34036
34037     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34038         if(!noRefresh){
34039            this.refresh();
34040         }
34041         this.updateHeaderSortState();
34042     },
34043
34044     getScrollState : function(){
34045         
34046         var sb = this.scroller.dom;
34047         return {left: sb.scrollLeft, top: sb.scrollTop};
34048     },
34049
34050     stripeRows : function(startRow){
34051         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34052             return;
34053         }
34054         startRow = startRow || 0;
34055         var rows = this.getBodyTable().rows;
34056         var lrows = this.getLockedTable().rows;
34057         var cls = ' x-grid-row-alt ';
34058         for(var i = startRow, len = rows.length; i < len; i++){
34059             var row = rows[i], lrow = lrows[i];
34060             var isAlt = ((i+1) % 2 == 0);
34061             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34062             if(isAlt == hasAlt){
34063                 continue;
34064             }
34065             if(isAlt){
34066                 row.className += " x-grid-row-alt";
34067             }else{
34068                 row.className = row.className.replace("x-grid-row-alt", "");
34069             }
34070             if(lrow){
34071                 lrow.className = row.className;
34072             }
34073         }
34074     },
34075
34076     restoreScroll : function(state){
34077         //Roo.log('GridView.restoreScroll');
34078         var sb = this.scroller.dom;
34079         sb.scrollLeft = state.left;
34080         sb.scrollTop = state.top;
34081         this.syncScroll();
34082     },
34083
34084     syncScroll : function(){
34085         //Roo.log('GridView.syncScroll');
34086         var sb = this.scroller.dom;
34087         var sh = this.mainHd.dom;
34088         var bs = this.mainBody.dom;
34089         var lv = this.lockedBody.dom;
34090         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34091         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34092     },
34093
34094     handleScroll : function(e){
34095         this.syncScroll();
34096         var sb = this.scroller.dom;
34097         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34098         e.stopEvent();
34099     },
34100
34101     handleWheel : function(e){
34102         var d = e.getWheelDelta();
34103         this.scroller.dom.scrollTop -= d*22;
34104         // set this here to prevent jumpy scrolling on large tables
34105         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34106         e.stopEvent();
34107     },
34108
34109     renderRows : function(startRow, endRow){
34110         // pull in all the crap needed to render rows
34111         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34112         var colCount = cm.getColumnCount();
34113
34114         if(ds.getCount() < 1){
34115             return ["", ""];
34116         }
34117
34118         // build a map for all the columns
34119         var cs = [];
34120         for(var i = 0; i < colCount; i++){
34121             var name = cm.getDataIndex(i);
34122             cs[i] = {
34123                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34124                 renderer : cm.getRenderer(i),
34125                 id : cm.getColumnId(i),
34126                 locked : cm.isLocked(i),
34127                 has_editor : cm.isCellEditable(i)
34128             };
34129         }
34130
34131         startRow = startRow || 0;
34132         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34133
34134         // records to render
34135         var rs = ds.getRange(startRow, endRow);
34136
34137         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34138     },
34139
34140     // As much as I hate to duplicate code, this was branched because FireFox really hates
34141     // [].join("") on strings. The performance difference was substantial enough to
34142     // branch this function
34143     doRender : Roo.isGecko ?
34144             function(cs, rs, ds, startRow, colCount, stripe){
34145                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34146                 // buffers
34147                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34148                 
34149                 var hasListener = this.grid.hasListener('rowclass');
34150                 var rowcfg = {};
34151                 for(var j = 0, len = rs.length; j < len; j++){
34152                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34153                     for(var i = 0; i < colCount; i++){
34154                         c = cs[i];
34155                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34156                         p.id = c.id;
34157                         p.css = p.attr = "";
34158                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34159                         if(p.value == undefined || p.value === "") {
34160                             p.value = "&#160;";
34161                         }
34162                         if(c.has_editor){
34163                             p.css += ' x-grid-editable-cell';
34164                         }
34165                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34166                             p.css +=  ' x-grid-dirty-cell';
34167                         }
34168                         var markup = ct.apply(p);
34169                         if(!c.locked){
34170                             cb+= markup;
34171                         }else{
34172                             lcb+= markup;
34173                         }
34174                     }
34175                     var alt = [];
34176                     if(stripe && ((rowIndex+1) % 2 == 0)){
34177                         alt.push("x-grid-row-alt")
34178                     }
34179                     if(r.dirty){
34180                         alt.push(  " x-grid-dirty-row");
34181                     }
34182                     rp.cells = lcb;
34183                     if(this.getRowClass){
34184                         alt.push(this.getRowClass(r, rowIndex));
34185                     }
34186                     if (hasListener) {
34187                         rowcfg = {
34188                              
34189                             record: r,
34190                             rowIndex : rowIndex,
34191                             rowClass : ''
34192                         };
34193                         this.grid.fireEvent('rowclass', this, rowcfg);
34194                         alt.push(rowcfg.rowClass);
34195                     }
34196                     rp.alt = alt.join(" ");
34197                     lbuf+= rt.apply(rp);
34198                     rp.cells = cb;
34199                     buf+=  rt.apply(rp);
34200                 }
34201                 return [lbuf, buf];
34202             } :
34203             function(cs, rs, ds, startRow, colCount, stripe){
34204                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34205                 // buffers
34206                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34207                 var hasListener = this.grid.hasListener('rowclass');
34208  
34209                 var rowcfg = {};
34210                 for(var j = 0, len = rs.length; j < len; j++){
34211                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34212                     for(var i = 0; i < colCount; i++){
34213                         c = cs[i];
34214                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34215                         p.id = c.id;
34216                         p.css = p.attr = "";
34217                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34218                         if(p.value == undefined || p.value === "") {
34219                             p.value = "&#160;";
34220                         }
34221                         //Roo.log(c);
34222                          if(c.has_editor){
34223                             p.css += ' x-grid-editable-cell';
34224                         }
34225                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34226                             p.css += ' x-grid-dirty-cell' 
34227                         }
34228                         
34229                         var markup = ct.apply(p);
34230                         if(!c.locked){
34231                             cb[cb.length] = markup;
34232                         }else{
34233                             lcb[lcb.length] = markup;
34234                         }
34235                     }
34236                     var alt = [];
34237                     if(stripe && ((rowIndex+1) % 2 == 0)){
34238                         alt.push( "x-grid-row-alt");
34239                     }
34240                     if(r.dirty){
34241                         alt.push(" x-grid-dirty-row");
34242                     }
34243                     rp.cells = lcb;
34244                     if(this.getRowClass){
34245                         alt.push( this.getRowClass(r, rowIndex));
34246                     }
34247                     if (hasListener) {
34248                         rowcfg = {
34249                              
34250                             record: r,
34251                             rowIndex : rowIndex,
34252                             rowClass : ''
34253                         };
34254                         this.grid.fireEvent('rowclass', this, rowcfg);
34255                         alt.push(rowcfg.rowClass);
34256                     }
34257                     
34258                     rp.alt = alt.join(" ");
34259                     rp.cells = lcb.join("");
34260                     lbuf[lbuf.length] = rt.apply(rp);
34261                     rp.cells = cb.join("");
34262                     buf[buf.length] =  rt.apply(rp);
34263                 }
34264                 return [lbuf.join(""), buf.join("")];
34265             },
34266
34267     renderBody : function(){
34268         var markup = this.renderRows();
34269         var bt = this.templates.body;
34270         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34271     },
34272
34273     /**
34274      * Refreshes the grid
34275      * @param {Boolean} headersToo
34276      */
34277     refresh : function(headersToo){
34278         this.fireEvent("beforerefresh", this);
34279         this.grid.stopEditing();
34280         var result = this.renderBody();
34281         this.lockedBody.update(result[0]);
34282         this.mainBody.update(result[1]);
34283         if(headersToo === true){
34284             this.updateHeaders();
34285             this.updateColumns();
34286             this.updateSplitters();
34287             this.updateHeaderSortState();
34288         }
34289         this.syncRowHeights();
34290         this.layout();
34291         this.fireEvent("refresh", this);
34292     },
34293
34294     handleColumnMove : function(cm, oldIndex, newIndex){
34295         this.indexMap = null;
34296         var s = this.getScrollState();
34297         this.refresh(true);
34298         this.restoreScroll(s);
34299         this.afterMove(newIndex);
34300     },
34301
34302     afterMove : function(colIndex){
34303         if(this.enableMoveAnim && Roo.enableFx){
34304             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34305         }
34306         // if multisort - fix sortOrder, and reload..
34307         if (this.grid.dataSource.multiSort) {
34308             // the we can call sort again..
34309             var dm = this.grid.dataSource;
34310             var cm = this.grid.colModel;
34311             var so = [];
34312             for(var i = 0; i < cm.config.length; i++ ) {
34313                 
34314                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34315                     continue; // dont' bother, it's not in sort list or being set.
34316                 }
34317                 
34318                 so.push(cm.config[i].dataIndex);
34319             };
34320             dm.sortOrder = so;
34321             dm.load(dm.lastOptions);
34322             
34323             
34324         }
34325         
34326     },
34327
34328     updateCell : function(dm, rowIndex, dataIndex){
34329         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34330         if(typeof colIndex == "undefined"){ // not present in grid
34331             return;
34332         }
34333         var cm = this.grid.colModel;
34334         var cell = this.getCell(rowIndex, colIndex);
34335         var cellText = this.getCellText(rowIndex, colIndex);
34336
34337         var p = {
34338             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34339             id : cm.getColumnId(colIndex),
34340             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34341         };
34342         var renderer = cm.getRenderer(colIndex);
34343         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34344         if(typeof val == "undefined" || val === "") {
34345             val = "&#160;";
34346         }
34347         cellText.innerHTML = val;
34348         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34349         this.syncRowHeights(rowIndex, rowIndex);
34350     },
34351
34352     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34353         var maxWidth = 0;
34354         if(this.grid.autoSizeHeaders){
34355             var h = this.getHeaderCellMeasure(colIndex);
34356             maxWidth = Math.max(maxWidth, h.scrollWidth);
34357         }
34358         var tb, index;
34359         if(this.cm.isLocked(colIndex)){
34360             tb = this.getLockedTable();
34361             index = colIndex;
34362         }else{
34363             tb = this.getBodyTable();
34364             index = colIndex - this.cm.getLockedCount();
34365         }
34366         if(tb && tb.rows){
34367             var rows = tb.rows;
34368             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34369             for(var i = 0; i < stopIndex; i++){
34370                 var cell = rows[i].childNodes[index].firstChild;
34371                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34372             }
34373         }
34374         return maxWidth + /*margin for error in IE*/ 5;
34375     },
34376     /**
34377      * Autofit a column to its content.
34378      * @param {Number} colIndex
34379      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34380      */
34381      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34382          if(this.cm.isHidden(colIndex)){
34383              return; // can't calc a hidden column
34384          }
34385         if(forceMinSize){
34386             var cid = this.cm.getColumnId(colIndex);
34387             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34388            if(this.grid.autoSizeHeaders){
34389                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34390            }
34391         }
34392         var newWidth = this.calcColumnWidth(colIndex);
34393         this.cm.setColumnWidth(colIndex,
34394             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34395         if(!suppressEvent){
34396             this.grid.fireEvent("columnresize", colIndex, newWidth);
34397         }
34398     },
34399
34400     /**
34401      * Autofits all columns to their content and then expands to fit any extra space in the grid
34402      */
34403      autoSizeColumns : function(){
34404         var cm = this.grid.colModel;
34405         var colCount = cm.getColumnCount();
34406         for(var i = 0; i < colCount; i++){
34407             this.autoSizeColumn(i, true, true);
34408         }
34409         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34410             this.fitColumns();
34411         }else{
34412             this.updateColumns();
34413             this.layout();
34414         }
34415     },
34416
34417     /**
34418      * Autofits all columns to the grid's width proportionate with their current size
34419      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34420      */
34421     fitColumns : function(reserveScrollSpace){
34422         var cm = this.grid.colModel;
34423         var colCount = cm.getColumnCount();
34424         var cols = [];
34425         var width = 0;
34426         var i, w;
34427         for (i = 0; i < colCount; i++){
34428             if(!cm.isHidden(i) && !cm.isFixed(i)){
34429                 w = cm.getColumnWidth(i);
34430                 cols.push(i);
34431                 cols.push(w);
34432                 width += w;
34433             }
34434         }
34435         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34436         if(reserveScrollSpace){
34437             avail -= 17;
34438         }
34439         var frac = (avail - cm.getTotalWidth())/width;
34440         while (cols.length){
34441             w = cols.pop();
34442             i = cols.pop();
34443             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34444         }
34445         this.updateColumns();
34446         this.layout();
34447     },
34448
34449     onRowSelect : function(rowIndex){
34450         var row = this.getRowComposite(rowIndex);
34451         row.addClass("x-grid-row-selected");
34452     },
34453
34454     onRowDeselect : function(rowIndex){
34455         var row = this.getRowComposite(rowIndex);
34456         row.removeClass("x-grid-row-selected");
34457     },
34458
34459     onCellSelect : function(row, col){
34460         var cell = this.getCell(row, col);
34461         if(cell){
34462             Roo.fly(cell).addClass("x-grid-cell-selected");
34463         }
34464     },
34465
34466     onCellDeselect : function(row, col){
34467         var cell = this.getCell(row, col);
34468         if(cell){
34469             Roo.fly(cell).removeClass("x-grid-cell-selected");
34470         }
34471     },
34472
34473     updateHeaderSortState : function(){
34474         
34475         // sort state can be single { field: xxx, direction : yyy}
34476         // or   { xxx=>ASC , yyy : DESC ..... }
34477         
34478         var mstate = {};
34479         if (!this.ds.multiSort) { 
34480             var state = this.ds.getSortState();
34481             if(!state){
34482                 return;
34483             }
34484             mstate[state.field] = state.direction;
34485             // FIXME... - this is not used here.. but might be elsewhere..
34486             this.sortState = state;
34487             
34488         } else {
34489             mstate = this.ds.sortToggle;
34490         }
34491         //remove existing sort classes..
34492         
34493         var sc = this.sortClasses;
34494         var hds = this.el.select(this.headerSelector).removeClass(sc);
34495         
34496         for(var f in mstate) {
34497         
34498             var sortColumn = this.cm.findColumnIndex(f);
34499             
34500             if(sortColumn != -1){
34501                 var sortDir = mstate[f];        
34502                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34503             }
34504         }
34505         
34506          
34507         
34508     },
34509
34510
34511     handleHeaderClick : function(g, index,e){
34512         
34513         Roo.log("header click");
34514         
34515         if (Roo.isTouch) {
34516             // touch events on header are handled by context
34517             this.handleHdCtx(g,index,e);
34518             return;
34519         }
34520         
34521         
34522         if(this.headersDisabled){
34523             return;
34524         }
34525         var dm = g.dataSource, cm = g.colModel;
34526         if(!cm.isSortable(index)){
34527             return;
34528         }
34529         g.stopEditing();
34530         
34531         if (dm.multiSort) {
34532             // update the sortOrder
34533             var so = [];
34534             for(var i = 0; i < cm.config.length; i++ ) {
34535                 
34536                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34537                     continue; // dont' bother, it's not in sort list or being set.
34538                 }
34539                 
34540                 so.push(cm.config[i].dataIndex);
34541             };
34542             dm.sortOrder = so;
34543         }
34544         
34545         
34546         dm.sort(cm.getDataIndex(index));
34547     },
34548
34549
34550     destroy : function(){
34551         if(this.colMenu){
34552             this.colMenu.removeAll();
34553             Roo.menu.MenuMgr.unregister(this.colMenu);
34554             this.colMenu.getEl().remove();
34555             delete this.colMenu;
34556         }
34557         if(this.hmenu){
34558             this.hmenu.removeAll();
34559             Roo.menu.MenuMgr.unregister(this.hmenu);
34560             this.hmenu.getEl().remove();
34561             delete this.hmenu;
34562         }
34563         if(this.grid.enableColumnMove){
34564             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34565             if(dds){
34566                 for(var dd in dds){
34567                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34568                         var elid = dds[dd].dragElId;
34569                         dds[dd].unreg();
34570                         Roo.get(elid).remove();
34571                     } else if(dds[dd].config.isTarget){
34572                         dds[dd].proxyTop.remove();
34573                         dds[dd].proxyBottom.remove();
34574                         dds[dd].unreg();
34575                     }
34576                     if(Roo.dd.DDM.locationCache[dd]){
34577                         delete Roo.dd.DDM.locationCache[dd];
34578                     }
34579                 }
34580                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34581             }
34582         }
34583         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34584         this.bind(null, null);
34585         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34586     },
34587
34588     handleLockChange : function(){
34589         this.refresh(true);
34590     },
34591
34592     onDenyColumnLock : function(){
34593
34594     },
34595
34596     onDenyColumnHide : function(){
34597
34598     },
34599
34600     handleHdMenuClick : function(item){
34601         var index = this.hdCtxIndex;
34602         var cm = this.cm, ds = this.ds;
34603         switch(item.id){
34604             case "asc":
34605                 ds.sort(cm.getDataIndex(index), "ASC");
34606                 break;
34607             case "desc":
34608                 ds.sort(cm.getDataIndex(index), "DESC");
34609                 break;
34610             case "lock":
34611                 var lc = cm.getLockedCount();
34612                 if(cm.getColumnCount(true) <= lc+1){
34613                     this.onDenyColumnLock();
34614                     return;
34615                 }
34616                 if(lc != index){
34617                     cm.setLocked(index, true, true);
34618                     cm.moveColumn(index, lc);
34619                     this.grid.fireEvent("columnmove", index, lc);
34620                 }else{
34621                     cm.setLocked(index, true);
34622                 }
34623             break;
34624             case "unlock":
34625                 var lc = cm.getLockedCount();
34626                 if((lc-1) != index){
34627                     cm.setLocked(index, false, true);
34628                     cm.moveColumn(index, lc-1);
34629                     this.grid.fireEvent("columnmove", index, lc-1);
34630                 }else{
34631                     cm.setLocked(index, false);
34632                 }
34633             break;
34634             case 'wider': // used to expand cols on touch..
34635             case 'narrow':
34636                 var cw = cm.getColumnWidth(index);
34637                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34638                 cw = Math.max(0, cw);
34639                 cw = Math.min(cw,4000);
34640                 cm.setColumnWidth(index, cw);
34641                 break;
34642                 
34643             default:
34644                 index = cm.getIndexById(item.id.substr(4));
34645                 if(index != -1){
34646                     if(item.checked && cm.getColumnCount(true) <= 1){
34647                         this.onDenyColumnHide();
34648                         return false;
34649                     }
34650                     cm.setHidden(index, item.checked);
34651                 }
34652         }
34653         return true;
34654     },
34655
34656     beforeColMenuShow : function(){
34657         var cm = this.cm,  colCount = cm.getColumnCount();
34658         this.colMenu.removeAll();
34659         for(var i = 0; i < colCount; i++){
34660             this.colMenu.add(new Roo.menu.CheckItem({
34661                 id: "col-"+cm.getColumnId(i),
34662                 text: cm.getColumnHeader(i),
34663                 checked: !cm.isHidden(i),
34664                 hideOnClick:false
34665             }));
34666         }
34667     },
34668
34669     handleHdCtx : function(g, index, e){
34670         e.stopEvent();
34671         var hd = this.getHeaderCell(index);
34672         this.hdCtxIndex = index;
34673         var ms = this.hmenu.items, cm = this.cm;
34674         ms.get("asc").setDisabled(!cm.isSortable(index));
34675         ms.get("desc").setDisabled(!cm.isSortable(index));
34676         if(this.grid.enableColLock !== false){
34677             ms.get("lock").setDisabled(cm.isLocked(index));
34678             ms.get("unlock").setDisabled(!cm.isLocked(index));
34679         }
34680         this.hmenu.show(hd, "tl-bl");
34681     },
34682
34683     handleHdOver : function(e){
34684         var hd = this.findHeaderCell(e.getTarget());
34685         if(hd && !this.headersDisabled){
34686             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34687                this.fly(hd).addClass("x-grid-hd-over");
34688             }
34689         }
34690     },
34691
34692     handleHdOut : function(e){
34693         var hd = this.findHeaderCell(e.getTarget());
34694         if(hd){
34695             this.fly(hd).removeClass("x-grid-hd-over");
34696         }
34697     },
34698
34699     handleSplitDblClick : function(e, t){
34700         var i = this.getCellIndex(t);
34701         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34702             this.autoSizeColumn(i, true);
34703             this.layout();
34704         }
34705     },
34706
34707     render : function(){
34708
34709         var cm = this.cm;
34710         var colCount = cm.getColumnCount();
34711
34712         if(this.grid.monitorWindowResize === true){
34713             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34714         }
34715         var header = this.renderHeaders();
34716         var body = this.templates.body.apply({rows:""});
34717         var html = this.templates.master.apply({
34718             lockedBody: body,
34719             body: body,
34720             lockedHeader: header[0],
34721             header: header[1]
34722         });
34723
34724         //this.updateColumns();
34725
34726         this.grid.getGridEl().dom.innerHTML = html;
34727
34728         this.initElements();
34729         
34730         // a kludge to fix the random scolling effect in webkit
34731         this.el.on("scroll", function() {
34732             this.el.dom.scrollTop=0; // hopefully not recursive..
34733         },this);
34734
34735         this.scroller.on("scroll", this.handleScroll, this);
34736         this.lockedBody.on("mousewheel", this.handleWheel, this);
34737         this.mainBody.on("mousewheel", this.handleWheel, this);
34738
34739         this.mainHd.on("mouseover", this.handleHdOver, this);
34740         this.mainHd.on("mouseout", this.handleHdOut, this);
34741         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34742                 {delegate: "."+this.splitClass});
34743
34744         this.lockedHd.on("mouseover", this.handleHdOver, this);
34745         this.lockedHd.on("mouseout", this.handleHdOut, this);
34746         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34747                 {delegate: "."+this.splitClass});
34748
34749         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34750             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34751         }
34752
34753         this.updateSplitters();
34754
34755         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34756             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34757             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34758         }
34759
34760         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34761             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34762             this.hmenu.add(
34763                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34764                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34765             );
34766             if(this.grid.enableColLock !== false){
34767                 this.hmenu.add('-',
34768                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34769                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34770                 );
34771             }
34772             if (Roo.isTouch) {
34773                  this.hmenu.add('-',
34774                     {id:"wider", text: this.columnsWiderText},
34775                     {id:"narrow", text: this.columnsNarrowText }
34776                 );
34777                 
34778                  
34779             }
34780             
34781             if(this.grid.enableColumnHide !== false){
34782
34783                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34784                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34785                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34786
34787                 this.hmenu.add('-',
34788                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34789                 );
34790             }
34791             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34792
34793             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34794         }
34795
34796         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34797             this.dd = new Roo.grid.GridDragZone(this.grid, {
34798                 ddGroup : this.grid.ddGroup || 'GridDD'
34799             });
34800             
34801         }
34802
34803         /*
34804         for(var i = 0; i < colCount; i++){
34805             if(cm.isHidden(i)){
34806                 this.hideColumn(i);
34807             }
34808             if(cm.config[i].align){
34809                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34810                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34811             }
34812         }*/
34813         
34814         this.updateHeaderSortState();
34815
34816         this.beforeInitialResize();
34817         this.layout(true);
34818
34819         // two part rendering gives faster view to the user
34820         this.renderPhase2.defer(1, this);
34821     },
34822
34823     renderPhase2 : function(){
34824         // render the rows now
34825         this.refresh();
34826         if(this.grid.autoSizeColumns){
34827             this.autoSizeColumns();
34828         }
34829     },
34830
34831     beforeInitialResize : function(){
34832
34833     },
34834
34835     onColumnSplitterMoved : function(i, w){
34836         this.userResized = true;
34837         var cm = this.grid.colModel;
34838         cm.setColumnWidth(i, w, true);
34839         var cid = cm.getColumnId(i);
34840         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34841         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34842         this.updateSplitters();
34843         this.layout();
34844         this.grid.fireEvent("columnresize", i, w);
34845     },
34846
34847     syncRowHeights : function(startIndex, endIndex){
34848         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34849             startIndex = startIndex || 0;
34850             var mrows = this.getBodyTable().rows;
34851             var lrows = this.getLockedTable().rows;
34852             var len = mrows.length-1;
34853             endIndex = Math.min(endIndex || len, len);
34854             for(var i = startIndex; i <= endIndex; i++){
34855                 var m = mrows[i], l = lrows[i];
34856                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34857                 m.style.height = l.style.height = h + "px";
34858             }
34859         }
34860     },
34861
34862     layout : function(initialRender, is2ndPass)
34863     {
34864         var g = this.grid;
34865         var auto = g.autoHeight;
34866         var scrollOffset = 16;
34867         var c = g.getGridEl(), cm = this.cm,
34868                 expandCol = g.autoExpandColumn,
34869                 gv = this;
34870         //c.beginMeasure();
34871
34872         if(!c.dom.offsetWidth){ // display:none?
34873             if(initialRender){
34874                 this.lockedWrap.show();
34875                 this.mainWrap.show();
34876             }
34877             return;
34878         }
34879
34880         var hasLock = this.cm.isLocked(0);
34881
34882         var tbh = this.headerPanel.getHeight();
34883         var bbh = this.footerPanel.getHeight();
34884
34885         if(auto){
34886             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34887             var newHeight = ch + c.getBorderWidth("tb");
34888             if(g.maxHeight){
34889                 newHeight = Math.min(g.maxHeight, newHeight);
34890             }
34891             c.setHeight(newHeight);
34892         }
34893
34894         if(g.autoWidth){
34895             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34896         }
34897
34898         var s = this.scroller;
34899
34900         var csize = c.getSize(true);
34901
34902         this.el.setSize(csize.width, csize.height);
34903
34904         this.headerPanel.setWidth(csize.width);
34905         this.footerPanel.setWidth(csize.width);
34906
34907         var hdHeight = this.mainHd.getHeight();
34908         var vw = csize.width;
34909         var vh = csize.height - (tbh + bbh);
34910
34911         s.setSize(vw, vh);
34912
34913         var bt = this.getBodyTable();
34914         
34915         if(cm.getLockedCount() == cm.config.length){
34916             bt = this.getLockedTable();
34917         }
34918         
34919         var ltWidth = hasLock ?
34920                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34921
34922         var scrollHeight = bt.offsetHeight;
34923         var scrollWidth = ltWidth + bt.offsetWidth;
34924         var vscroll = false, hscroll = false;
34925
34926         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34927
34928         var lw = this.lockedWrap, mw = this.mainWrap;
34929         var lb = this.lockedBody, mb = this.mainBody;
34930
34931         setTimeout(function(){
34932             var t = s.dom.offsetTop;
34933             var w = s.dom.clientWidth,
34934                 h = s.dom.clientHeight;
34935
34936             lw.setTop(t);
34937             lw.setSize(ltWidth, h);
34938
34939             mw.setLeftTop(ltWidth, t);
34940             mw.setSize(w-ltWidth, h);
34941
34942             lb.setHeight(h-hdHeight);
34943             mb.setHeight(h-hdHeight);
34944
34945             if(is2ndPass !== true && !gv.userResized && expandCol){
34946                 // high speed resize without full column calculation
34947                 
34948                 var ci = cm.getIndexById(expandCol);
34949                 if (ci < 0) {
34950                     ci = cm.findColumnIndex(expandCol);
34951                 }
34952                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34953                 var expandId = cm.getColumnId(ci);
34954                 var  tw = cm.getTotalWidth(false);
34955                 var currentWidth = cm.getColumnWidth(ci);
34956                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34957                 if(currentWidth != cw){
34958                     cm.setColumnWidth(ci, cw, true);
34959                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34960                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34961                     gv.updateSplitters();
34962                     gv.layout(false, true);
34963                 }
34964             }
34965
34966             if(initialRender){
34967                 lw.show();
34968                 mw.show();
34969             }
34970             //c.endMeasure();
34971         }, 10);
34972     },
34973
34974     onWindowResize : function(){
34975         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34976             return;
34977         }
34978         this.layout();
34979     },
34980
34981     appendFooter : function(parentEl){
34982         return null;
34983     },
34984
34985     sortAscText : "Sort Ascending",
34986     sortDescText : "Sort Descending",
34987     lockText : "Lock Column",
34988     unlockText : "Unlock Column",
34989     columnsText : "Columns",
34990  
34991     columnsWiderText : "Wider",
34992     columnsNarrowText : "Thinner"
34993 });
34994
34995
34996 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34997     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34998     this.proxy.el.addClass('x-grid3-col-dd');
34999 };
35000
35001 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35002     handleMouseDown : function(e){
35003
35004     },
35005
35006     callHandleMouseDown : function(e){
35007         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35008     }
35009 });
35010 /*
35011  * Based on:
35012  * Ext JS Library 1.1.1
35013  * Copyright(c) 2006-2007, Ext JS, LLC.
35014  *
35015  * Originally Released Under LGPL - original licence link has changed is not relivant.
35016  *
35017  * Fork - LGPL
35018  * <script type="text/javascript">
35019  */
35020  /**
35021  * @extends Roo.dd.DDProxy
35022  * @class Roo.grid.SplitDragZone
35023  * Support for Column Header resizing
35024  * @constructor
35025  * @param {Object} config
35026  */
35027 // private
35028 // This is a support class used internally by the Grid components
35029 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35030     this.grid = grid;
35031     this.view = grid.getView();
35032     this.proxy = this.view.resizeProxy;
35033     Roo.grid.SplitDragZone.superclass.constructor.call(
35034         this,
35035         hd, // ID
35036         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
35037         {  // CONFIG
35038             dragElId : Roo.id(this.proxy.dom),
35039             resizeFrame:false
35040         }
35041     );
35042     
35043     this.setHandleElId(Roo.id(hd));
35044     if (hd2 !== false) {
35045         this.setOuterHandleElId(Roo.id(hd2));
35046     }
35047     
35048     this.scroll = false;
35049 };
35050 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35051     fly: Roo.Element.fly,
35052
35053     b4StartDrag : function(x, y){
35054         this.view.headersDisabled = true;
35055         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
35056                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
35057         );
35058         this.proxy.setHeight(h);
35059         
35060         // for old system colWidth really stored the actual width?
35061         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
35062         // which in reality did not work.. - it worked only for fixed sizes
35063         // for resizable we need to use actual sizes.
35064         var w = this.cm.getColumnWidth(this.cellIndex);
35065         if (!this.view.mainWrap) {
35066             // bootstrap.
35067             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
35068         }
35069         
35070         
35071         
35072         // this was w-this.grid.minColumnWidth;
35073         // doesnt really make sense? - w = thie curren width or the rendered one?
35074         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35075         this.resetConstraints();
35076         this.setXConstraint(minw, 1000);
35077         this.setYConstraint(0, 0);
35078         this.minX = x - minw;
35079         this.maxX = x + 1000;
35080         this.startPos = x;
35081         if (!this.view.mainWrap) { // this is Bootstrap code..
35082             this.getDragEl().style.display='block';
35083         }
35084         
35085         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35086     },
35087
35088
35089     handleMouseDown : function(e){
35090         ev = Roo.EventObject.setEvent(e);
35091         var t = this.fly(ev.getTarget());
35092         if(t.hasClass("x-grid-split")){
35093             this.cellIndex = this.view.getCellIndex(t.dom);
35094             this.split = t.dom;
35095             this.cm = this.grid.colModel;
35096             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35097                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35098             }
35099         }
35100     },
35101
35102     endDrag : function(e){
35103         this.view.headersDisabled = false;
35104         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35105         var diff = endX - this.startPos;
35106         // 
35107         var w = this.cm.getColumnWidth(this.cellIndex);
35108         if (!this.view.mainWrap) {
35109             w = 0;
35110         }
35111         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
35112     },
35113
35114     autoOffset : function(){
35115         this.setDelta(0,0);
35116     }
35117 });/*
35118  * Based on:
35119  * Ext JS Library 1.1.1
35120  * Copyright(c) 2006-2007, Ext JS, LLC.
35121  *
35122  * Originally Released Under LGPL - original licence link has changed is not relivant.
35123  *
35124  * Fork - LGPL
35125  * <script type="text/javascript">
35126  */
35127  
35128 // private
35129 // This is a support class used internally by the Grid components
35130 Roo.grid.GridDragZone = function(grid, config){
35131     this.view = grid.getView();
35132     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35133     if(this.view.lockedBody){
35134         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35135         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35136     }
35137     this.scroll = false;
35138     this.grid = grid;
35139     this.ddel = document.createElement('div');
35140     this.ddel.className = 'x-grid-dd-wrap';
35141 };
35142
35143 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35144     ddGroup : "GridDD",
35145
35146     getDragData : function(e){
35147         var t = Roo.lib.Event.getTarget(e);
35148         var rowIndex = this.view.findRowIndex(t);
35149         var sm = this.grid.selModel;
35150             
35151         //Roo.log(rowIndex);
35152         
35153         if (sm.getSelectedCell) {
35154             // cell selection..
35155             if (!sm.getSelectedCell()) {
35156                 return false;
35157             }
35158             if (rowIndex != sm.getSelectedCell()[0]) {
35159                 return false;
35160             }
35161         
35162         }
35163         if (sm.getSelections && sm.getSelections().length < 1) {
35164             return false;
35165         }
35166         
35167         
35168         // before it used to all dragging of unseleted... - now we dont do that.
35169         if(rowIndex !== false){
35170             
35171             // if editorgrid.. 
35172             
35173             
35174             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35175                
35176             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35177               //  
35178             //}
35179             if (e.hasModifier()){
35180                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35181             }
35182             
35183             Roo.log("getDragData");
35184             
35185             return {
35186                 grid: this.grid,
35187                 ddel: this.ddel,
35188                 rowIndex: rowIndex,
35189                 selections: sm.getSelections ? sm.getSelections() : (
35190                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
35191             };
35192         }
35193         return false;
35194     },
35195     
35196     
35197     onInitDrag : function(e){
35198         var data = this.dragData;
35199         this.ddel.innerHTML = this.grid.getDragDropText();
35200         this.proxy.update(this.ddel);
35201         // fire start drag?
35202     },
35203
35204     afterRepair : function(){
35205         this.dragging = false;
35206     },
35207
35208     getRepairXY : function(e, data){
35209         return false;
35210     },
35211
35212     onEndDrag : function(data, e){
35213         // fire end drag?
35214     },
35215
35216     onValidDrop : function(dd, e, id){
35217         // fire drag drop?
35218         this.hideProxy();
35219     },
35220
35221     beforeInvalidDrop : function(e, id){
35222
35223     }
35224 });/*
35225  * Based on:
35226  * Ext JS Library 1.1.1
35227  * Copyright(c) 2006-2007, Ext JS, LLC.
35228  *
35229  * Originally Released Under LGPL - original licence link has changed is not relivant.
35230  *
35231  * Fork - LGPL
35232  * <script type="text/javascript">
35233  */
35234  
35235
35236 /**
35237  * @class Roo.grid.ColumnModel
35238  * @extends Roo.util.Observable
35239  * This is the default implementation of a ColumnModel used by the Grid. It defines
35240  * the columns in the grid.
35241  * <br>Usage:<br>
35242  <pre><code>
35243  var colModel = new Roo.grid.ColumnModel([
35244         {header: "Ticker", width: 60, sortable: true, locked: true},
35245         {header: "Company Name", width: 150, sortable: true},
35246         {header: "Market Cap.", width: 100, sortable: true},
35247         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35248         {header: "Employees", width: 100, sortable: true, resizable: false}
35249  ]);
35250  </code></pre>
35251  * <p>
35252  
35253  * The config options listed for this class are options which may appear in each
35254  * individual column definition.
35255  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35256  * @constructor
35257  * @param {Object} config An Array of column config objects. See this class's
35258  * config objects for details.
35259 */
35260 Roo.grid.ColumnModel = function(config){
35261         /**
35262      * The config passed into the constructor
35263      */
35264     this.config = []; //config;
35265     this.lookup = {};
35266
35267     // if no id, create one
35268     // if the column does not have a dataIndex mapping,
35269     // map it to the order it is in the config
35270     for(var i = 0, len = config.length; i < len; i++){
35271         this.addColumn(config[i]);
35272         
35273     }
35274
35275     /**
35276      * The width of columns which have no width specified (defaults to 100)
35277      * @type Number
35278      */
35279     this.defaultWidth = 100;
35280
35281     /**
35282      * Default sortable of columns which have no sortable specified (defaults to false)
35283      * @type Boolean
35284      */
35285     this.defaultSortable = false;
35286
35287     this.addEvents({
35288         /**
35289              * @event widthchange
35290              * Fires when the width of a column changes.
35291              * @param {ColumnModel} this
35292              * @param {Number} columnIndex The column index
35293              * @param {Number} newWidth The new width
35294              */
35295             "widthchange": true,
35296         /**
35297              * @event headerchange
35298              * Fires when the text of a header changes.
35299              * @param {ColumnModel} this
35300              * @param {Number} columnIndex The column index
35301              * @param {Number} newText The new header text
35302              */
35303             "headerchange": true,
35304         /**
35305              * @event hiddenchange
35306              * Fires when a column is hidden or "unhidden".
35307              * @param {ColumnModel} this
35308              * @param {Number} columnIndex The column index
35309              * @param {Boolean} hidden true if hidden, false otherwise
35310              */
35311             "hiddenchange": true,
35312             /**
35313          * @event columnmoved
35314          * Fires when a column is moved.
35315          * @param {ColumnModel} this
35316          * @param {Number} oldIndex
35317          * @param {Number} newIndex
35318          */
35319         "columnmoved" : true,
35320         /**
35321          * @event columlockchange
35322          * Fires when a column's locked state is changed
35323          * @param {ColumnModel} this
35324          * @param {Number} colIndex
35325          * @param {Boolean} locked true if locked
35326          */
35327         "columnlockchange" : true
35328     });
35329     Roo.grid.ColumnModel.superclass.constructor.call(this);
35330 };
35331 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35332     /**
35333      * @cfg {String} header The header text to display in the Grid view.
35334      */
35335         /**
35336      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
35337      */
35338         /**
35339      * @cfg {String} smHeader Header at Bootsrap Small width
35340      */
35341         /**
35342      * @cfg {String} mdHeader Header at Bootsrap Medium width
35343      */
35344         /**
35345      * @cfg {String} lgHeader Header at Bootsrap Large width
35346      */
35347         /**
35348      * @cfg {String} xlHeader Header at Bootsrap extra Large width
35349      */
35350     /**
35351      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35352      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35353      * specified, the column's index is used as an index into the Record's data Array.
35354      */
35355     /**
35356      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35357      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35358      */
35359     /**
35360      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35361      * Defaults to the value of the {@link #defaultSortable} property.
35362      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35363      */
35364     /**
35365      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35366      */
35367     /**
35368      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35369      */
35370     /**
35371      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35372      */
35373     /**
35374      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35375      */
35376     /**
35377      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35378      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35379      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35380      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35381      */
35382        /**
35383      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35384      */
35385     /**
35386      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35387      */
35388     /**
35389      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35390      */
35391     /**
35392      * @cfg {String} cursor (Optional)
35393      */
35394     /**
35395      * @cfg {String} tooltip (Optional)
35396      */
35397     /**
35398      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
35399      */
35400     /**
35401      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
35402      */
35403     /**
35404      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
35405      */
35406     /**
35407      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
35408      */
35409         /**
35410      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
35411      */
35412     /**
35413      * Returns the id of the column at the specified index.
35414      * @param {Number} index The column index
35415      * @return {String} the id
35416      */
35417     getColumnId : function(index){
35418         return this.config[index].id;
35419     },
35420
35421     /**
35422      * Returns the column for a specified id.
35423      * @param {String} id The column id
35424      * @return {Object} the column
35425      */
35426     getColumnById : function(id){
35427         return this.lookup[id];
35428     },
35429
35430     
35431     /**
35432      * Returns the column Object for a specified dataIndex.
35433      * @param {String} dataIndex The column dataIndex
35434      * @return {Object|Boolean} the column or false if not found
35435      */
35436     getColumnByDataIndex: function(dataIndex){
35437         var index = this.findColumnIndex(dataIndex);
35438         return index > -1 ? this.config[index] : false;
35439     },
35440     
35441     /**
35442      * Returns the index for a specified column id.
35443      * @param {String} id The column id
35444      * @return {Number} the index, or -1 if not found
35445      */
35446     getIndexById : function(id){
35447         for(var i = 0, len = this.config.length; i < len; i++){
35448             if(this.config[i].id == id){
35449                 return i;
35450             }
35451         }
35452         return -1;
35453     },
35454     
35455     /**
35456      * Returns the index for a specified column dataIndex.
35457      * @param {String} dataIndex The column dataIndex
35458      * @return {Number} the index, or -1 if not found
35459      */
35460     
35461     findColumnIndex : function(dataIndex){
35462         for(var i = 0, len = this.config.length; i < len; i++){
35463             if(this.config[i].dataIndex == dataIndex){
35464                 return i;
35465             }
35466         }
35467         return -1;
35468     },
35469     
35470     
35471     moveColumn : function(oldIndex, newIndex){
35472         var c = this.config[oldIndex];
35473         this.config.splice(oldIndex, 1);
35474         this.config.splice(newIndex, 0, c);
35475         this.dataMap = null;
35476         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35477     },
35478
35479     isLocked : function(colIndex){
35480         return this.config[colIndex].locked === true;
35481     },
35482
35483     setLocked : function(colIndex, value, suppressEvent){
35484         if(this.isLocked(colIndex) == value){
35485             return;
35486         }
35487         this.config[colIndex].locked = value;
35488         if(!suppressEvent){
35489             this.fireEvent("columnlockchange", this, colIndex, value);
35490         }
35491     },
35492
35493     getTotalLockedWidth : function(){
35494         var totalWidth = 0;
35495         for(var i = 0; i < this.config.length; i++){
35496             if(this.isLocked(i) && !this.isHidden(i)){
35497                 this.totalWidth += this.getColumnWidth(i);
35498             }
35499         }
35500         return totalWidth;
35501     },
35502
35503     getLockedCount : function(){
35504         for(var i = 0, len = this.config.length; i < len; i++){
35505             if(!this.isLocked(i)){
35506                 return i;
35507             }
35508         }
35509         
35510         return this.config.length;
35511     },
35512
35513     /**
35514      * Returns the number of columns.
35515      * @return {Number}
35516      */
35517     getColumnCount : function(visibleOnly){
35518         if(visibleOnly === true){
35519             var c = 0;
35520             for(var i = 0, len = this.config.length; i < len; i++){
35521                 if(!this.isHidden(i)){
35522                     c++;
35523                 }
35524             }
35525             return c;
35526         }
35527         return this.config.length;
35528     },
35529
35530     /**
35531      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35532      * @param {Function} fn
35533      * @param {Object} scope (optional)
35534      * @return {Array} result
35535      */
35536     getColumnsBy : function(fn, scope){
35537         var r = [];
35538         for(var i = 0, len = this.config.length; i < len; i++){
35539             var c = this.config[i];
35540             if(fn.call(scope||this, c, i) === true){
35541                 r[r.length] = c;
35542             }
35543         }
35544         return r;
35545     },
35546
35547     /**
35548      * Returns true if the specified column is sortable.
35549      * @param {Number} col The column index
35550      * @return {Boolean}
35551      */
35552     isSortable : function(col){
35553         if(typeof this.config[col].sortable == "undefined"){
35554             return this.defaultSortable;
35555         }
35556         return this.config[col].sortable;
35557     },
35558
35559     /**
35560      * Returns the rendering (formatting) function defined for the column.
35561      * @param {Number} col The column index.
35562      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35563      */
35564     getRenderer : function(col){
35565         if(!this.config[col].renderer){
35566             return Roo.grid.ColumnModel.defaultRenderer;
35567         }
35568         return this.config[col].renderer;
35569     },
35570
35571     /**
35572      * Sets the rendering (formatting) function for a column.
35573      * @param {Number} col The column index
35574      * @param {Function} fn The function to use to process the cell's raw data
35575      * to return HTML markup for the grid view. The render function is called with
35576      * the following parameters:<ul>
35577      * <li>Data value.</li>
35578      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35579      * <li>css A CSS style string to apply to the table cell.</li>
35580      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35581      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35582      * <li>Row index</li>
35583      * <li>Column index</li>
35584      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35585      */
35586     setRenderer : function(col, fn){
35587         this.config[col].renderer = fn;
35588     },
35589
35590     /**
35591      * Returns the width for the specified column.
35592      * @param {Number} col The column index
35593      * @param (optional) {String} gridSize bootstrap width size.
35594      * @return {Number}
35595      */
35596     getColumnWidth : function(col, gridSize)
35597         {
35598                 var cfg = this.config[col];
35599                 
35600                 if (typeof(gridSize) == 'undefined') {
35601                         return cfg.width * 1 || this.defaultWidth;
35602                 }
35603                 if (gridSize === false) { // if we set it..
35604                         return cfg.width || false;
35605                 }
35606                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
35607                 
35608                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
35609                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
35610                                 continue;
35611                         }
35612                         return cfg[ sizes[i] ];
35613                 }
35614                 return 1;
35615                 
35616     },
35617
35618     /**
35619      * Sets the width for a column.
35620      * @param {Number} col The column index
35621      * @param {Number} width The new width
35622      */
35623     setColumnWidth : function(col, width, suppressEvent){
35624         this.config[col].width = width;
35625         this.totalWidth = null;
35626         if(!suppressEvent){
35627              this.fireEvent("widthchange", this, col, width);
35628         }
35629     },
35630
35631     /**
35632      * Returns the total width of all columns.
35633      * @param {Boolean} includeHidden True to include hidden column widths
35634      * @return {Number}
35635      */
35636     getTotalWidth : function(includeHidden){
35637         if(!this.totalWidth){
35638             this.totalWidth = 0;
35639             for(var i = 0, len = this.config.length; i < len; i++){
35640                 if(includeHidden || !this.isHidden(i)){
35641                     this.totalWidth += this.getColumnWidth(i);
35642                 }
35643             }
35644         }
35645         return this.totalWidth;
35646     },
35647
35648     /**
35649      * Returns the header for the specified column.
35650      * @param {Number} col The column index
35651      * @return {String}
35652      */
35653     getColumnHeader : function(col){
35654         return this.config[col].header;
35655     },
35656
35657     /**
35658      * Sets the header for a column.
35659      * @param {Number} col The column index
35660      * @param {String} header The new header
35661      */
35662     setColumnHeader : function(col, header){
35663         this.config[col].header = header;
35664         this.fireEvent("headerchange", this, col, header);
35665     },
35666
35667     /**
35668      * Returns the tooltip for the specified column.
35669      * @param {Number} col The column index
35670      * @return {String}
35671      */
35672     getColumnTooltip : function(col){
35673             return this.config[col].tooltip;
35674     },
35675     /**
35676      * Sets the tooltip for a column.
35677      * @param {Number} col The column index
35678      * @param {String} tooltip The new tooltip
35679      */
35680     setColumnTooltip : function(col, tooltip){
35681             this.config[col].tooltip = tooltip;
35682     },
35683
35684     /**
35685      * Returns the dataIndex for the specified column.
35686      * @param {Number} col The column index
35687      * @return {Number}
35688      */
35689     getDataIndex : function(col){
35690         return this.config[col].dataIndex;
35691     },
35692
35693     /**
35694      * Sets the dataIndex for a column.
35695      * @param {Number} col The column index
35696      * @param {Number} dataIndex The new dataIndex
35697      */
35698     setDataIndex : function(col, dataIndex){
35699         this.config[col].dataIndex = dataIndex;
35700     },
35701
35702     
35703     
35704     /**
35705      * Returns true if the cell is editable.
35706      * @param {Number} colIndex The column index
35707      * @param {Number} rowIndex The row index - this is nto actually used..?
35708      * @return {Boolean}
35709      */
35710     isCellEditable : function(colIndex, rowIndex){
35711         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35712     },
35713
35714     /**
35715      * Returns the editor defined for the cell/column.
35716      * return false or null to disable editing.
35717      * @param {Number} colIndex The column index
35718      * @param {Number} rowIndex The row index
35719      * @return {Object}
35720      */
35721     getCellEditor : function(colIndex, rowIndex){
35722         return this.config[colIndex].editor;
35723     },
35724
35725     /**
35726      * Sets if a column is editable.
35727      * @param {Number} col The column index
35728      * @param {Boolean} editable True if the column is editable
35729      */
35730     setEditable : function(col, editable){
35731         this.config[col].editable = editable;
35732     },
35733
35734
35735     /**
35736      * Returns true if the column is hidden.
35737      * @param {Number} colIndex The column index
35738      * @return {Boolean}
35739      */
35740     isHidden : function(colIndex){
35741         return this.config[colIndex].hidden;
35742     },
35743
35744
35745     /**
35746      * Returns true if the column width cannot be changed
35747      */
35748     isFixed : function(colIndex){
35749         return this.config[colIndex].fixed;
35750     },
35751
35752     /**
35753      * Returns true if the column can be resized
35754      * @return {Boolean}
35755      */
35756     isResizable : function(colIndex){
35757         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35758     },
35759     /**
35760      * Sets if a column is hidden.
35761      * @param {Number} colIndex The column index
35762      * @param {Boolean} hidden True if the column is hidden
35763      */
35764     setHidden : function(colIndex, hidden){
35765         this.config[colIndex].hidden = hidden;
35766         this.totalWidth = null;
35767         this.fireEvent("hiddenchange", this, colIndex, hidden);
35768     },
35769
35770     /**
35771      * Sets the editor for a column.
35772      * @param {Number} col The column index
35773      * @param {Object} editor The editor object
35774      */
35775     setEditor : function(col, editor){
35776         this.config[col].editor = editor;
35777     },
35778     /**
35779      * Add a column (experimental...) - defaults to adding to the end..
35780      * @param {Object} config 
35781     */
35782     addColumn : function(c)
35783     {
35784     
35785         var i = this.config.length;
35786         this.config[i] = c;
35787         
35788         if(typeof c.dataIndex == "undefined"){
35789             c.dataIndex = i;
35790         }
35791         if(typeof c.renderer == "string"){
35792             c.renderer = Roo.util.Format[c.renderer];
35793         }
35794         if(typeof c.id == "undefined"){
35795             c.id = Roo.id();
35796         }
35797         if(c.editor && c.editor.xtype){
35798             c.editor  = Roo.factory(c.editor, Roo.grid);
35799         }
35800         if(c.editor && c.editor.isFormField){
35801             c.editor = new Roo.grid.GridEditor(c.editor);
35802         }
35803         this.lookup[c.id] = c;
35804     }
35805     
35806 });
35807
35808 Roo.grid.ColumnModel.defaultRenderer = function(value)
35809 {
35810     if(typeof value == "object") {
35811         return value;
35812     }
35813         if(typeof value == "string" && value.length < 1){
35814             return "&#160;";
35815         }
35816     
35817         return String.format("{0}", value);
35818 };
35819
35820 // Alias for backwards compatibility
35821 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35822 /*
35823  * Based on:
35824  * Ext JS Library 1.1.1
35825  * Copyright(c) 2006-2007, Ext JS, LLC.
35826  *
35827  * Originally Released Under LGPL - original licence link has changed is not relivant.
35828  *
35829  * Fork - LGPL
35830  * <script type="text/javascript">
35831  */
35832
35833 /**
35834  * @class Roo.grid.AbstractSelectionModel
35835  * @extends Roo.util.Observable
35836  * @abstract
35837  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35838  * implemented by descendant classes.  This class should not be directly instantiated.
35839  * @constructor
35840  */
35841 Roo.grid.AbstractSelectionModel = function(){
35842     this.locked = false;
35843     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35844 };
35845
35846 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35847     /** @ignore Called by the grid automatically. Do not call directly. */
35848     init : function(grid){
35849         this.grid = grid;
35850         this.initEvents();
35851     },
35852
35853     /**
35854      * Locks the selections.
35855      */
35856     lock : function(){
35857         this.locked = true;
35858     },
35859
35860     /**
35861      * Unlocks the selections.
35862      */
35863     unlock : function(){
35864         this.locked = false;
35865     },
35866
35867     /**
35868      * Returns true if the selections are locked.
35869      * @return {Boolean}
35870      */
35871     isLocked : function(){
35872         return this.locked;
35873     }
35874 });/*
35875  * Based on:
35876  * Ext JS Library 1.1.1
35877  * Copyright(c) 2006-2007, Ext JS, LLC.
35878  *
35879  * Originally Released Under LGPL - original licence link has changed is not relivant.
35880  *
35881  * Fork - LGPL
35882  * <script type="text/javascript">
35883  */
35884 /**
35885  * @extends Roo.grid.AbstractSelectionModel
35886  * @class Roo.grid.RowSelectionModel
35887  * The default SelectionModel used by {@link Roo.grid.Grid}.
35888  * It supports multiple selections and keyboard selection/navigation. 
35889  * @constructor
35890  * @param {Object} config
35891  */
35892 Roo.grid.RowSelectionModel = function(config){
35893     Roo.apply(this, config);
35894     this.selections = new Roo.util.MixedCollection(false, function(o){
35895         return o.id;
35896     });
35897
35898     this.last = false;
35899     this.lastActive = false;
35900
35901     this.addEvents({
35902         /**
35903         * @event selectionchange
35904         * Fires when the selection changes
35905         * @param {SelectionModel} this
35906         */
35907        "selectionchange" : true,
35908        /**
35909         * @event afterselectionchange
35910         * Fires after the selection changes (eg. by key press or clicking)
35911         * @param {SelectionModel} this
35912         */
35913        "afterselectionchange" : true,
35914        /**
35915         * @event beforerowselect
35916         * Fires when a row is selected being selected, return false to cancel.
35917         * @param {SelectionModel} this
35918         * @param {Number} rowIndex The selected index
35919         * @param {Boolean} keepExisting False if other selections will be cleared
35920         */
35921        "beforerowselect" : true,
35922        /**
35923         * @event rowselect
35924         * Fires when a row is selected.
35925         * @param {SelectionModel} this
35926         * @param {Number} rowIndex The selected index
35927         * @param {Roo.data.Record} r The record
35928         */
35929        "rowselect" : true,
35930        /**
35931         * @event rowdeselect
35932         * Fires when a row is deselected.
35933         * @param {SelectionModel} this
35934         * @param {Number} rowIndex The selected index
35935         */
35936         "rowdeselect" : true
35937     });
35938     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35939     this.locked = false;
35940 };
35941
35942 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35943     /**
35944      * @cfg {Boolean} singleSelect
35945      * True to allow selection of only one row at a time (defaults to false)
35946      */
35947     singleSelect : false,
35948
35949     // private
35950     initEvents : function(){
35951
35952         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35953             this.grid.on("mousedown", this.handleMouseDown, this);
35954         }else{ // allow click to work like normal
35955             this.grid.on("rowclick", this.handleDragableRowClick, this);
35956         }
35957         // bootstrap does not have a view..
35958         var view = this.grid.view ? this.grid.view : this.grid;
35959         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35960             "up" : function(e){
35961                 if(!e.shiftKey){
35962                     this.selectPrevious(e.shiftKey);
35963                 }else if(this.last !== false && this.lastActive !== false){
35964                     var last = this.last;
35965                     this.selectRange(this.last,  this.lastActive-1);
35966                     view.focusRow(this.lastActive);
35967                     if(last !== false){
35968                         this.last = last;
35969                     }
35970                 }else{
35971                     this.selectFirstRow();
35972                 }
35973                 this.fireEvent("afterselectionchange", this);
35974             },
35975             "down" : function(e){
35976                 if(!e.shiftKey){
35977                     this.selectNext(e.shiftKey);
35978                 }else if(this.last !== false && this.lastActive !== false){
35979                     var last = this.last;
35980                     this.selectRange(this.last,  this.lastActive+1);
35981                     view.focusRow(this.lastActive);
35982                     if(last !== false){
35983                         this.last = last;
35984                     }
35985                 }else{
35986                     this.selectFirstRow();
35987                 }
35988                 this.fireEvent("afterselectionchange", this);
35989             },
35990             scope: this
35991         });
35992
35993          
35994         view.on("refresh", this.onRefresh, this);
35995         view.on("rowupdated", this.onRowUpdated, this);
35996         view.on("rowremoved", this.onRemove, this);
35997     },
35998
35999     // private
36000     onRefresh : function(){
36001         var ds = this.grid.ds, i, v = this.grid.view;
36002         var s = this.selections;
36003         s.each(function(r){
36004             if((i = ds.indexOfId(r.id)) != -1){
36005                 v.onRowSelect(i);
36006                 s.add(ds.getAt(i)); // updating the selection relate data
36007             }else{
36008                 s.remove(r);
36009             }
36010         });
36011     },
36012
36013     // private
36014     onRemove : function(v, index, r){
36015         this.selections.remove(r);
36016     },
36017
36018     // private
36019     onRowUpdated : function(v, index, r){
36020         if(this.isSelected(r)){
36021             v.onRowSelect(index);
36022         }
36023     },
36024
36025     /**
36026      * Select records.
36027      * @param {Array} records The records to select
36028      * @param {Boolean} keepExisting (optional) True to keep existing selections
36029      */
36030     selectRecords : function(records, keepExisting){
36031         if(!keepExisting){
36032             this.clearSelections();
36033         }
36034         var ds = this.grid.ds;
36035         for(var i = 0, len = records.length; i < len; i++){
36036             this.selectRow(ds.indexOf(records[i]), true);
36037         }
36038     },
36039
36040     /**
36041      * Gets the number of selected rows.
36042      * @return {Number}
36043      */
36044     getCount : function(){
36045         return this.selections.length;
36046     },
36047
36048     /**
36049      * Selects the first row in the grid.
36050      */
36051     selectFirstRow : function(){
36052         this.selectRow(0);
36053     },
36054
36055     /**
36056      * Select the last row.
36057      * @param {Boolean} keepExisting (optional) True to keep existing selections
36058      */
36059     selectLastRow : function(keepExisting){
36060         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
36061     },
36062
36063     /**
36064      * Selects the row immediately following the last selected row.
36065      * @param {Boolean} keepExisting (optional) True to keep existing selections
36066      */
36067     selectNext : function(keepExisting){
36068         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
36069             this.selectRow(this.last+1, keepExisting);
36070             var view = this.grid.view ? this.grid.view : this.grid;
36071             view.focusRow(this.last);
36072         }
36073     },
36074
36075     /**
36076      * Selects the row that precedes the last selected row.
36077      * @param {Boolean} keepExisting (optional) True to keep existing selections
36078      */
36079     selectPrevious : function(keepExisting){
36080         if(this.last){
36081             this.selectRow(this.last-1, keepExisting);
36082             var view = this.grid.view ? this.grid.view : this.grid;
36083             view.focusRow(this.last);
36084         }
36085     },
36086
36087     /**
36088      * Returns the selected records
36089      * @return {Array} Array of selected records
36090      */
36091     getSelections : function(){
36092         return [].concat(this.selections.items);
36093     },
36094
36095     /**
36096      * Returns the first selected record.
36097      * @return {Record}
36098      */
36099     getSelected : function(){
36100         return this.selections.itemAt(0);
36101     },
36102
36103
36104     /**
36105      * Clears all selections.
36106      */
36107     clearSelections : function(fast){
36108         if(this.locked) {
36109             return;
36110         }
36111         if(fast !== true){
36112             var ds = this.grid.ds;
36113             var s = this.selections;
36114             s.each(function(r){
36115                 this.deselectRow(ds.indexOfId(r.id));
36116             }, this);
36117             s.clear();
36118         }else{
36119             this.selections.clear();
36120         }
36121         this.last = false;
36122     },
36123
36124
36125     /**
36126      * Selects all rows.
36127      */
36128     selectAll : function(){
36129         if(this.locked) {
36130             return;
36131         }
36132         this.selections.clear();
36133         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
36134             this.selectRow(i, true);
36135         }
36136     },
36137
36138     /**
36139      * Returns True if there is a selection.
36140      * @return {Boolean}
36141      */
36142     hasSelection : function(){
36143         return this.selections.length > 0;
36144     },
36145
36146     /**
36147      * Returns True if the specified row is selected.
36148      * @param {Number/Record} record The record or index of the record to check
36149      * @return {Boolean}
36150      */
36151     isSelected : function(index){
36152         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
36153         return (r && this.selections.key(r.id) ? true : false);
36154     },
36155
36156     /**
36157      * Returns True if the specified record id is selected.
36158      * @param {String} id The id of record to check
36159      * @return {Boolean}
36160      */
36161     isIdSelected : function(id){
36162         return (this.selections.key(id) ? true : false);
36163     },
36164
36165     // private
36166     handleMouseDown : function(e, t)
36167     {
36168         var view = this.grid.view ? this.grid.view : this.grid;
36169         var rowIndex;
36170         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36171             return;
36172         };
36173         if(e.shiftKey && this.last !== false){
36174             var last = this.last;
36175             this.selectRange(last, rowIndex, e.ctrlKey);
36176             this.last = last; // reset the last
36177             view.focusRow(rowIndex);
36178         }else{
36179             var isSelected = this.isSelected(rowIndex);
36180             if(e.button !== 0 && isSelected){
36181                 view.focusRow(rowIndex);
36182             }else if(e.ctrlKey && isSelected){
36183                 this.deselectRow(rowIndex);
36184             }else if(!isSelected){
36185                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36186                 view.focusRow(rowIndex);
36187             }
36188         }
36189         this.fireEvent("afterselectionchange", this);
36190     },
36191     // private
36192     handleDragableRowClick :  function(grid, rowIndex, e) 
36193     {
36194         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36195             this.selectRow(rowIndex, false);
36196             var view = this.grid.view ? this.grid.view : this.grid;
36197             view.focusRow(rowIndex);
36198              this.fireEvent("afterselectionchange", this);
36199         }
36200     },
36201     
36202     /**
36203      * Selects multiple rows.
36204      * @param {Array} rows Array of the indexes of the row to select
36205      * @param {Boolean} keepExisting (optional) True to keep existing selections
36206      */
36207     selectRows : function(rows, keepExisting){
36208         if(!keepExisting){
36209             this.clearSelections();
36210         }
36211         for(var i = 0, len = rows.length; i < len; i++){
36212             this.selectRow(rows[i], true);
36213         }
36214     },
36215
36216     /**
36217      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36218      * @param {Number} startRow The index of the first row in the range
36219      * @param {Number} endRow The index of the last row in the range
36220      * @param {Boolean} keepExisting (optional) True to retain existing selections
36221      */
36222     selectRange : function(startRow, endRow, keepExisting){
36223         if(this.locked) {
36224             return;
36225         }
36226         if(!keepExisting){
36227             this.clearSelections();
36228         }
36229         if(startRow <= endRow){
36230             for(var i = startRow; i <= endRow; i++){
36231                 this.selectRow(i, true);
36232             }
36233         }else{
36234             for(var i = startRow; i >= endRow; i--){
36235                 this.selectRow(i, true);
36236             }
36237         }
36238     },
36239
36240     /**
36241      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36242      * @param {Number} startRow The index of the first row in the range
36243      * @param {Number} endRow The index of the last row in the range
36244      */
36245     deselectRange : function(startRow, endRow, preventViewNotify){
36246         if(this.locked) {
36247             return;
36248         }
36249         for(var i = startRow; i <= endRow; i++){
36250             this.deselectRow(i, preventViewNotify);
36251         }
36252     },
36253
36254     /**
36255      * Selects a row.
36256      * @param {Number} row The index of the row to select
36257      * @param {Boolean} keepExisting (optional) True to keep existing selections
36258      */
36259     selectRow : function(index, keepExisting, preventViewNotify){
36260         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
36261             return;
36262         }
36263         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36264             if(!keepExisting || this.singleSelect){
36265                 this.clearSelections();
36266             }
36267             var r = this.grid.ds.getAt(index);
36268             this.selections.add(r);
36269             this.last = this.lastActive = index;
36270             if(!preventViewNotify){
36271                 var view = this.grid.view ? this.grid.view : this.grid;
36272                 view.onRowSelect(index);
36273             }
36274             this.fireEvent("rowselect", this, index, r);
36275             this.fireEvent("selectionchange", this);
36276         }
36277     },
36278
36279     /**
36280      * Deselects a row.
36281      * @param {Number} row The index of the row to deselect
36282      */
36283     deselectRow : function(index, preventViewNotify){
36284         if(this.locked) {
36285             return;
36286         }
36287         if(this.last == index){
36288             this.last = false;
36289         }
36290         if(this.lastActive == index){
36291             this.lastActive = false;
36292         }
36293         var r = this.grid.ds.getAt(index);
36294         this.selections.remove(r);
36295         if(!preventViewNotify){
36296             var view = this.grid.view ? this.grid.view : this.grid;
36297             view.onRowDeselect(index);
36298         }
36299         this.fireEvent("rowdeselect", this, index);
36300         this.fireEvent("selectionchange", this);
36301     },
36302
36303     // private
36304     restoreLast : function(){
36305         if(this._last){
36306             this.last = this._last;
36307         }
36308     },
36309
36310     // private
36311     acceptsNav : function(row, col, cm){
36312         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36313     },
36314
36315     // private
36316     onEditorKey : function(field, e){
36317         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36318         if(k == e.TAB){
36319             e.stopEvent();
36320             ed.completeEdit();
36321             if(e.shiftKey){
36322                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36323             }else{
36324                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36325             }
36326         }else if(k == e.ENTER && !e.ctrlKey){
36327             e.stopEvent();
36328             ed.completeEdit();
36329             if(e.shiftKey){
36330                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36331             }else{
36332                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36333             }
36334         }else if(k == e.ESC){
36335             ed.cancelEdit();
36336         }
36337         if(newCell){
36338             g.startEditing(newCell[0], newCell[1]);
36339         }
36340     }
36341 });/*
36342  * Based on:
36343  * Ext JS Library 1.1.1
36344  * Copyright(c) 2006-2007, Ext JS, LLC.
36345  *
36346  * Originally Released Under LGPL - original licence link has changed is not relivant.
36347  *
36348  * Fork - LGPL
36349  * <script type="text/javascript">
36350  */
36351 /**
36352  * @class Roo.grid.CellSelectionModel
36353  * @extends Roo.grid.AbstractSelectionModel
36354  * This class provides the basic implementation for cell selection in a grid.
36355  * @constructor
36356  * @param {Object} config The object containing the configuration of this model.
36357  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36358  */
36359 Roo.grid.CellSelectionModel = function(config){
36360     Roo.apply(this, config);
36361
36362     this.selection = null;
36363
36364     this.addEvents({
36365         /**
36366              * @event beforerowselect
36367              * Fires before a cell is selected.
36368              * @param {SelectionModel} this
36369              * @param {Number} rowIndex The selected row index
36370              * @param {Number} colIndex The selected cell index
36371              */
36372             "beforecellselect" : true,
36373         /**
36374              * @event cellselect
36375              * Fires when a cell is selected.
36376              * @param {SelectionModel} this
36377              * @param {Number} rowIndex The selected row index
36378              * @param {Number} colIndex The selected cell index
36379              */
36380             "cellselect" : true,
36381         /**
36382              * @event selectionchange
36383              * Fires when the active selection changes.
36384              * @param {SelectionModel} this
36385              * @param {Object} selection null for no selection or an object (o) with two properties
36386                 <ul>
36387                 <li>o.record: the record object for the row the selection is in</li>
36388                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36389                 </ul>
36390              */
36391             "selectionchange" : true,
36392         /**
36393              * @event tabend
36394              * Fires when the tab (or enter) was pressed on the last editable cell
36395              * You can use this to trigger add new row.
36396              * @param {SelectionModel} this
36397              */
36398             "tabend" : true,
36399          /**
36400              * @event beforeeditnext
36401              * Fires before the next editable sell is made active
36402              * You can use this to skip to another cell or fire the tabend
36403              *    if you set cell to false
36404              * @param {Object} eventdata object : { cell : [ row, col ] } 
36405              */
36406             "beforeeditnext" : true
36407     });
36408     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36409 };
36410
36411 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36412     
36413     enter_is_tab: false,
36414
36415     /** @ignore */
36416     initEvents : function(){
36417         this.grid.on("mousedown", this.handleMouseDown, this);
36418         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36419         var view = this.grid.view;
36420         view.on("refresh", this.onViewChange, this);
36421         view.on("rowupdated", this.onRowUpdated, this);
36422         view.on("beforerowremoved", this.clearSelections, this);
36423         view.on("beforerowsinserted", this.clearSelections, this);
36424         if(this.grid.isEditor){
36425             this.grid.on("beforeedit", this.beforeEdit,  this);
36426         }
36427     },
36428
36429         //private
36430     beforeEdit : function(e){
36431         this.select(e.row, e.column, false, true, e.record);
36432     },
36433
36434         //private
36435     onRowUpdated : function(v, index, r){
36436         if(this.selection && this.selection.record == r){
36437             v.onCellSelect(index, this.selection.cell[1]);
36438         }
36439     },
36440
36441         //private
36442     onViewChange : function(){
36443         this.clearSelections(true);
36444     },
36445
36446         /**
36447          * Returns the currently selected cell,.
36448          * @return {Array} The selected cell (row, column) or null if none selected.
36449          */
36450     getSelectedCell : function(){
36451         return this.selection ? this.selection.cell : null;
36452     },
36453
36454     /**
36455      * Clears all selections.
36456      * @param {Boolean} true to prevent the gridview from being notified about the change.
36457      */
36458     clearSelections : function(preventNotify){
36459         var s = this.selection;
36460         if(s){
36461             if(preventNotify !== true){
36462                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36463             }
36464             this.selection = null;
36465             this.fireEvent("selectionchange", this, null);
36466         }
36467     },
36468
36469     /**
36470      * Returns true if there is a selection.
36471      * @return {Boolean}
36472      */
36473     hasSelection : function(){
36474         return this.selection ? true : false;
36475     },
36476
36477     /** @ignore */
36478     handleMouseDown : function(e, t){
36479         var v = this.grid.getView();
36480         if(this.isLocked()){
36481             return;
36482         };
36483         var row = v.findRowIndex(t);
36484         var cell = v.findCellIndex(t);
36485         if(row !== false && cell !== false){
36486             this.select(row, cell);
36487         }
36488     },
36489
36490     /**
36491      * Selects a cell.
36492      * @param {Number} rowIndex
36493      * @param {Number} collIndex
36494      */
36495     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36496         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36497             this.clearSelections();
36498             r = r || this.grid.dataSource.getAt(rowIndex);
36499             this.selection = {
36500                 record : r,
36501                 cell : [rowIndex, colIndex]
36502             };
36503             if(!preventViewNotify){
36504                 var v = this.grid.getView();
36505                 v.onCellSelect(rowIndex, colIndex);
36506                 if(preventFocus !== true){
36507                     v.focusCell(rowIndex, colIndex);
36508                 }
36509             }
36510             this.fireEvent("cellselect", this, rowIndex, colIndex);
36511             this.fireEvent("selectionchange", this, this.selection);
36512         }
36513     },
36514
36515         //private
36516     isSelectable : function(rowIndex, colIndex, cm){
36517         return !cm.isHidden(colIndex);
36518     },
36519
36520     /** @ignore */
36521     handleKeyDown : function(e){
36522         //Roo.log('Cell Sel Model handleKeyDown');
36523         if(!e.isNavKeyPress()){
36524             return;
36525         }
36526         var g = this.grid, s = this.selection;
36527         if(!s){
36528             e.stopEvent();
36529             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36530             if(cell){
36531                 this.select(cell[0], cell[1]);
36532             }
36533             return;
36534         }
36535         var sm = this;
36536         var walk = function(row, col, step){
36537             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36538         };
36539         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36540         var newCell;
36541
36542       
36543
36544         switch(k){
36545             case e.TAB:
36546                 // handled by onEditorKey
36547                 if (g.isEditor && g.editing) {
36548                     return;
36549                 }
36550                 if(e.shiftKey) {
36551                     newCell = walk(r, c-1, -1);
36552                 } else {
36553                     newCell = walk(r, c+1, 1);
36554                 }
36555                 break;
36556             
36557             case e.DOWN:
36558                newCell = walk(r+1, c, 1);
36559                 break;
36560             
36561             case e.UP:
36562                 newCell = walk(r-1, c, -1);
36563                 break;
36564             
36565             case e.RIGHT:
36566                 newCell = walk(r, c+1, 1);
36567                 break;
36568             
36569             case e.LEFT:
36570                 newCell = walk(r, c-1, -1);
36571                 break;
36572             
36573             case e.ENTER:
36574                 
36575                 if(g.isEditor && !g.editing){
36576                    g.startEditing(r, c);
36577                    e.stopEvent();
36578                    return;
36579                 }
36580                 
36581                 
36582              break;
36583         };
36584         if(newCell){
36585             this.select(newCell[0], newCell[1]);
36586             e.stopEvent();
36587             
36588         }
36589     },
36590
36591     acceptsNav : function(row, col, cm){
36592         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36593     },
36594     /**
36595      * Selects a cell.
36596      * @param {Number} field (not used) - as it's normally used as a listener
36597      * @param {Number} e - event - fake it by using
36598      *
36599      * var e = Roo.EventObjectImpl.prototype;
36600      * e.keyCode = e.TAB
36601      *
36602      * 
36603      */
36604     onEditorKey : function(field, e){
36605         
36606         var k = e.getKey(),
36607             newCell,
36608             g = this.grid,
36609             ed = g.activeEditor,
36610             forward = false;
36611         ///Roo.log('onEditorKey' + k);
36612         
36613         
36614         if (this.enter_is_tab && k == e.ENTER) {
36615             k = e.TAB;
36616         }
36617         
36618         if(k == e.TAB){
36619             if(e.shiftKey){
36620                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36621             }else{
36622                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36623                 forward = true;
36624             }
36625             
36626             e.stopEvent();
36627             
36628         } else if(k == e.ENTER &&  !e.ctrlKey){
36629             ed.completeEdit();
36630             e.stopEvent();
36631             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36632         
36633                 } else if(k == e.ESC){
36634             ed.cancelEdit();
36635         }
36636                 
36637         if (newCell) {
36638             var ecall = { cell : newCell, forward : forward };
36639             this.fireEvent('beforeeditnext', ecall );
36640             newCell = ecall.cell;
36641                         forward = ecall.forward;
36642         }
36643                 
36644         if(newCell){
36645             //Roo.log('next cell after edit');
36646             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36647         } else if (forward) {
36648             // tabbed past last
36649             this.fireEvent.defer(100, this, ['tabend',this]);
36650         }
36651     }
36652 });/*
36653  * Based on:
36654  * Ext JS Library 1.1.1
36655  * Copyright(c) 2006-2007, Ext JS, LLC.
36656  *
36657  * Originally Released Under LGPL - original licence link has changed is not relivant.
36658  *
36659  * Fork - LGPL
36660  * <script type="text/javascript">
36661  */
36662  
36663 /**
36664  * @class Roo.grid.EditorGrid
36665  * @extends Roo.grid.Grid
36666  * Class for creating and editable grid.
36667  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36668  * The container MUST have some type of size defined for the grid to fill. The container will be 
36669  * automatically set to position relative if it isn't already.
36670  * @param {Object} dataSource The data model to bind to
36671  * @param {Object} colModel The column model with info about this grid's columns
36672  */
36673 Roo.grid.EditorGrid = function(container, config){
36674     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36675     this.getGridEl().addClass("xedit-grid");
36676
36677     if(!this.selModel){
36678         this.selModel = new Roo.grid.CellSelectionModel();
36679     }
36680
36681     this.activeEditor = null;
36682
36683         this.addEvents({
36684             /**
36685              * @event beforeedit
36686              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36687              * <ul style="padding:5px;padding-left:16px;">
36688              * <li>grid - This grid</li>
36689              * <li>record - The record being edited</li>
36690              * <li>field - The field name being edited</li>
36691              * <li>value - The value for the field being edited.</li>
36692              * <li>row - The grid row index</li>
36693              * <li>column - The grid column index</li>
36694              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36695              * </ul>
36696              * @param {Object} e An edit event (see above for description)
36697              */
36698             "beforeedit" : true,
36699             /**
36700              * @event afteredit
36701              * Fires after a cell is edited. <br />
36702              * <ul style="padding:5px;padding-left:16px;">
36703              * <li>grid - This grid</li>
36704              * <li>record - The record being edited</li>
36705              * <li>field - The field name being edited</li>
36706              * <li>value - The value being set</li>
36707              * <li>originalValue - The original value for the field, before the edit.</li>
36708              * <li>row - The grid row index</li>
36709              * <li>column - The grid column index</li>
36710              * </ul>
36711              * @param {Object} e An edit event (see above for description)
36712              */
36713             "afteredit" : true,
36714             /**
36715              * @event validateedit
36716              * Fires after a cell is edited, but before the value is set in the record. 
36717          * You can use this to modify the value being set in the field, Return false
36718              * to cancel the change. The edit event object has the following properties <br />
36719              * <ul style="padding:5px;padding-left:16px;">
36720          * <li>editor - This editor</li>
36721              * <li>grid - This grid</li>
36722              * <li>record - The record being edited</li>
36723              * <li>field - The field name being edited</li>
36724              * <li>value - The value being set</li>
36725              * <li>originalValue - The original value for the field, before the edit.</li>
36726              * <li>row - The grid row index</li>
36727              * <li>column - The grid column index</li>
36728              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36729              * </ul>
36730              * @param {Object} e An edit event (see above for description)
36731              */
36732             "validateedit" : true
36733         });
36734     this.on("bodyscroll", this.stopEditing,  this);
36735     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36736 };
36737
36738 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36739     /**
36740      * @cfg {Number} clicksToEdit
36741      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36742      */
36743     clicksToEdit: 2,
36744
36745     // private
36746     isEditor : true,
36747     // private
36748     trackMouseOver: false, // causes very odd FF errors
36749
36750     onCellDblClick : function(g, row, col){
36751         this.startEditing(row, col);
36752     },
36753
36754     onEditComplete : function(ed, value, startValue){
36755         this.editing = false;
36756         this.activeEditor = null;
36757         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36758         var r = ed.record;
36759         var field = this.colModel.getDataIndex(ed.col);
36760         var e = {
36761             grid: this,
36762             record: r,
36763             field: field,
36764             originalValue: startValue,
36765             value: value,
36766             row: ed.row,
36767             column: ed.col,
36768             cancel:false,
36769             editor: ed
36770         };
36771         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36772         cell.show();
36773           
36774         if(String(value) !== String(startValue)){
36775             
36776             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36777                 r.set(field, e.value);
36778                 // if we are dealing with a combo box..
36779                 // then we also set the 'name' colum to be the displayField
36780                 if (ed.field.displayField && ed.field.name) {
36781                     r.set(ed.field.name, ed.field.el.dom.value);
36782                 }
36783                 
36784                 delete e.cancel; //?? why!!!
36785                 this.fireEvent("afteredit", e);
36786             }
36787         } else {
36788             this.fireEvent("afteredit", e); // always fire it!
36789         }
36790         this.view.focusCell(ed.row, ed.col);
36791     },
36792
36793     /**
36794      * Starts editing the specified for the specified row/column
36795      * @param {Number} rowIndex
36796      * @param {Number} colIndex
36797      */
36798     startEditing : function(row, col){
36799         this.stopEditing();
36800         if(this.colModel.isCellEditable(col, row)){
36801             this.view.ensureVisible(row, col, true);
36802           
36803             var r = this.dataSource.getAt(row);
36804             var field = this.colModel.getDataIndex(col);
36805             var cell = Roo.get(this.view.getCell(row,col));
36806             var e = {
36807                 grid: this,
36808                 record: r,
36809                 field: field,
36810                 value: r.data[field],
36811                 row: row,
36812                 column: col,
36813                 cancel:false 
36814             };
36815             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36816                 this.editing = true;
36817                 var ed = this.colModel.getCellEditor(col, row);
36818                 
36819                 if (!ed) {
36820                     return;
36821                 }
36822                 if(!ed.rendered){
36823                     ed.render(ed.parentEl || document.body);
36824                 }
36825                 ed.field.reset();
36826                
36827                 cell.hide();
36828                 
36829                 (function(){ // complex but required for focus issues in safari, ie and opera
36830                     ed.row = row;
36831                     ed.col = col;
36832                     ed.record = r;
36833                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36834                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36835                     this.activeEditor = ed;
36836                     var v = r.data[field];
36837                     ed.startEdit(this.view.getCell(row, col), v);
36838                     // combo's with 'displayField and name set
36839                     if (ed.field.displayField && ed.field.name) {
36840                         ed.field.el.dom.value = r.data[ed.field.name];
36841                     }
36842                     
36843                     
36844                 }).defer(50, this);
36845             }
36846         }
36847     },
36848         
36849     /**
36850      * Stops any active editing
36851      */
36852     stopEditing : function(){
36853         if(this.activeEditor){
36854             this.activeEditor.completeEdit();
36855         }
36856         this.activeEditor = null;
36857     },
36858         
36859          /**
36860      * Called to get grid's drag proxy text, by default returns this.ddText.
36861      * @return {String}
36862      */
36863     getDragDropText : function(){
36864         var count = this.selModel.getSelectedCell() ? 1 : 0;
36865         return String.format(this.ddText, count, count == 1 ? '' : 's');
36866     }
36867         
36868 });/*
36869  * Based on:
36870  * Ext JS Library 1.1.1
36871  * Copyright(c) 2006-2007, Ext JS, LLC.
36872  *
36873  * Originally Released Under LGPL - original licence link has changed is not relivant.
36874  *
36875  * Fork - LGPL
36876  * <script type="text/javascript">
36877  */
36878
36879 // private - not really -- you end up using it !
36880 // This is a support class used internally by the Grid components
36881
36882 /**
36883  * @class Roo.grid.GridEditor
36884  * @extends Roo.Editor
36885  * Class for creating and editable grid elements.
36886  * @param {Object} config any settings (must include field)
36887  */
36888 Roo.grid.GridEditor = function(field, config){
36889     if (!config && field.field) {
36890         config = field;
36891         field = Roo.factory(config.field, Roo.form);
36892     }
36893     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36894     field.monitorTab = false;
36895 };
36896
36897 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36898     
36899     /**
36900      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36901      */
36902     
36903     alignment: "tl-tl",
36904     autoSize: "width",
36905     hideEl : false,
36906     cls: "x-small-editor x-grid-editor",
36907     shim:false,
36908     shadow:"frame"
36909 });/*
36910  * Based on:
36911  * Ext JS Library 1.1.1
36912  * Copyright(c) 2006-2007, Ext JS, LLC.
36913  *
36914  * Originally Released Under LGPL - original licence link has changed is not relivant.
36915  *
36916  * Fork - LGPL
36917  * <script type="text/javascript">
36918  */
36919   
36920
36921   
36922 Roo.grid.PropertyRecord = Roo.data.Record.create([
36923     {name:'name',type:'string'},  'value'
36924 ]);
36925
36926
36927 Roo.grid.PropertyStore = function(grid, source){
36928     this.grid = grid;
36929     this.store = new Roo.data.Store({
36930         recordType : Roo.grid.PropertyRecord
36931     });
36932     this.store.on('update', this.onUpdate,  this);
36933     if(source){
36934         this.setSource(source);
36935     }
36936     Roo.grid.PropertyStore.superclass.constructor.call(this);
36937 };
36938
36939
36940
36941 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36942     setSource : function(o){
36943         this.source = o;
36944         this.store.removeAll();
36945         var data = [];
36946         for(var k in o){
36947             if(this.isEditableValue(o[k])){
36948                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36949             }
36950         }
36951         this.store.loadRecords({records: data}, {}, true);
36952     },
36953
36954     onUpdate : function(ds, record, type){
36955         if(type == Roo.data.Record.EDIT){
36956             var v = record.data['value'];
36957             var oldValue = record.modified['value'];
36958             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36959                 this.source[record.id] = v;
36960                 record.commit();
36961                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36962             }else{
36963                 record.reject();
36964             }
36965         }
36966     },
36967
36968     getProperty : function(row){
36969        return this.store.getAt(row);
36970     },
36971
36972     isEditableValue: function(val){
36973         if(val && val instanceof Date){
36974             return true;
36975         }else if(typeof val == 'object' || typeof val == 'function'){
36976             return false;
36977         }
36978         return true;
36979     },
36980
36981     setValue : function(prop, value){
36982         this.source[prop] = value;
36983         this.store.getById(prop).set('value', value);
36984     },
36985
36986     getSource : function(){
36987         return this.source;
36988     }
36989 });
36990
36991 Roo.grid.PropertyColumnModel = function(grid, store){
36992     this.grid = grid;
36993     var g = Roo.grid;
36994     g.PropertyColumnModel.superclass.constructor.call(this, [
36995         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36996         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36997     ]);
36998     this.store = store;
36999     this.bselect = Roo.DomHelper.append(document.body, {
37000         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37001             {tag: 'option', value: 'true', html: 'true'},
37002             {tag: 'option', value: 'false', html: 'false'}
37003         ]
37004     });
37005     Roo.id(this.bselect);
37006     var f = Roo.form;
37007     this.editors = {
37008         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37009         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37010         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37011         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37012         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37013     };
37014     this.renderCellDelegate = this.renderCell.createDelegate(this);
37015     this.renderPropDelegate = this.renderProp.createDelegate(this);
37016 };
37017
37018 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37019     
37020     
37021     nameText : 'Name',
37022     valueText : 'Value',
37023     
37024     dateFormat : 'm/j/Y',
37025     
37026     
37027     renderDate : function(dateVal){
37028         return dateVal.dateFormat(this.dateFormat);
37029     },
37030
37031     renderBool : function(bVal){
37032         return bVal ? 'true' : 'false';
37033     },
37034
37035     isCellEditable : function(colIndex, rowIndex){
37036         return colIndex == 1;
37037     },
37038
37039     getRenderer : function(col){
37040         return col == 1 ?
37041             this.renderCellDelegate : this.renderPropDelegate;
37042     },
37043
37044     renderProp : function(v){
37045         return this.getPropertyName(v);
37046     },
37047
37048     renderCell : function(val){
37049         var rv = val;
37050         if(val instanceof Date){
37051             rv = this.renderDate(val);
37052         }else if(typeof val == 'boolean'){
37053             rv = this.renderBool(val);
37054         }
37055         return Roo.util.Format.htmlEncode(rv);
37056     },
37057
37058     getPropertyName : function(name){
37059         var pn = this.grid.propertyNames;
37060         return pn && pn[name] ? pn[name] : name;
37061     },
37062
37063     getCellEditor : function(colIndex, rowIndex){
37064         var p = this.store.getProperty(rowIndex);
37065         var n = p.data['name'], val = p.data['value'];
37066         
37067         if(typeof(this.grid.customEditors[n]) == 'string'){
37068             return this.editors[this.grid.customEditors[n]];
37069         }
37070         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37071             return this.grid.customEditors[n];
37072         }
37073         if(val instanceof Date){
37074             return this.editors['date'];
37075         }else if(typeof val == 'number'){
37076             return this.editors['number'];
37077         }else if(typeof val == 'boolean'){
37078             return this.editors['boolean'];
37079         }else{
37080             return this.editors['string'];
37081         }
37082     }
37083 });
37084
37085 /**
37086  * @class Roo.grid.PropertyGrid
37087  * @extends Roo.grid.EditorGrid
37088  * This class represents the  interface of a component based property grid control.
37089  * <br><br>Usage:<pre><code>
37090  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37091       
37092  });
37093  // set any options
37094  grid.render();
37095  * </code></pre>
37096   
37097  * @constructor
37098  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37099  * The container MUST have some type of size defined for the grid to fill. The container will be
37100  * automatically set to position relative if it isn't already.
37101  * @param {Object} config A config object that sets properties on this grid.
37102  */
37103 Roo.grid.PropertyGrid = function(container, config){
37104     config = config || {};
37105     var store = new Roo.grid.PropertyStore(this);
37106     this.store = store;
37107     var cm = new Roo.grid.PropertyColumnModel(this, store);
37108     store.store.sort('name', 'ASC');
37109     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37110         ds: store.store,
37111         cm: cm,
37112         enableColLock:false,
37113         enableColumnMove:false,
37114         stripeRows:false,
37115         trackMouseOver: false,
37116         clicksToEdit:1
37117     }, config));
37118     this.getGridEl().addClass('x-props-grid');
37119     this.lastEditRow = null;
37120     this.on('columnresize', this.onColumnResize, this);
37121     this.addEvents({
37122          /**
37123              * @event beforepropertychange
37124              * Fires before a property changes (return false to stop?)
37125              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37126              * @param {String} id Record Id
37127              * @param {String} newval New Value
37128          * @param {String} oldval Old Value
37129              */
37130         "beforepropertychange": true,
37131         /**
37132              * @event propertychange
37133              * Fires after a property changes
37134              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37135              * @param {String} id Record Id
37136              * @param {String} newval New Value
37137          * @param {String} oldval Old Value
37138              */
37139         "propertychange": true
37140     });
37141     this.customEditors = this.customEditors || {};
37142 };
37143 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37144     
37145      /**
37146      * @cfg {Object} customEditors map of colnames=> custom editors.
37147      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37148      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37149      * false disables editing of the field.
37150          */
37151     
37152       /**
37153      * @cfg {Object} propertyNames map of property Names to their displayed value
37154          */
37155     
37156     render : function(){
37157         Roo.grid.PropertyGrid.superclass.render.call(this);
37158         this.autoSize.defer(100, this);
37159     },
37160
37161     autoSize : function(){
37162         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37163         if(this.view){
37164             this.view.fitColumns();
37165         }
37166     },
37167
37168     onColumnResize : function(){
37169         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37170         this.autoSize();
37171     },
37172     /**
37173      * Sets the data for the Grid
37174      * accepts a Key => Value object of all the elements avaiable.
37175      * @param {Object} data  to appear in grid.
37176      */
37177     setSource : function(source){
37178         this.store.setSource(source);
37179         //this.autoSize();
37180     },
37181     /**
37182      * Gets all the data from the grid.
37183      * @return {Object} data  data stored in grid
37184      */
37185     getSource : function(){
37186         return this.store.getSource();
37187     }
37188 });/*
37189   
37190  * Licence LGPL
37191  
37192  */
37193  
37194 /**
37195  * @class Roo.grid.Calendar
37196  * @extends Roo.util.Grid
37197  * This class extends the Grid to provide a calendar widget
37198  * <br><br>Usage:<pre><code>
37199  var grid = new Roo.grid.Calendar("my-container-id", {
37200      ds: myDataStore,
37201      cm: myColModel,
37202      selModel: mySelectionModel,
37203      autoSizeColumns: true,
37204      monitorWindowResize: false,
37205      trackMouseOver: true
37206      eventstore : real data store..
37207  });
37208  // set any options
37209  grid.render();
37210   
37211   * @constructor
37212  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37213  * The container MUST have some type of size defined for the grid to fill. The container will be
37214  * automatically set to position relative if it isn't already.
37215  * @param {Object} config A config object that sets properties on this grid.
37216  */
37217 Roo.grid.Calendar = function(container, config){
37218         // initialize the container
37219         this.container = Roo.get(container);
37220         this.container.update("");
37221         this.container.setStyle("overflow", "hidden");
37222     this.container.addClass('x-grid-container');
37223
37224     this.id = this.container.id;
37225
37226     Roo.apply(this, config);
37227     // check and correct shorthanded configs
37228     
37229     var rows = [];
37230     var d =1;
37231     for (var r = 0;r < 6;r++) {
37232         
37233         rows[r]=[];
37234         for (var c =0;c < 7;c++) {
37235             rows[r][c]= '';
37236         }
37237     }
37238     if (this.eventStore) {
37239         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37240         this.eventStore.on('load',this.onLoad, this);
37241         this.eventStore.on('beforeload',this.clearEvents, this);
37242          
37243     }
37244     
37245     this.dataSource = new Roo.data.Store({
37246             proxy: new Roo.data.MemoryProxy(rows),
37247             reader: new Roo.data.ArrayReader({}, [
37248                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37249     });
37250
37251     this.dataSource.load();
37252     this.ds = this.dataSource;
37253     this.ds.xmodule = this.xmodule || false;
37254     
37255     
37256     var cellRender = function(v,x,r)
37257     {
37258         return String.format(
37259             '<div class="fc-day  fc-widget-content"><div>' +
37260                 '<div class="fc-event-container"></div>' +
37261                 '<div class="fc-day-number">{0}</div>'+
37262                 
37263                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37264             '</div></div>', v);
37265     
37266     }
37267     
37268     
37269     this.colModel = new Roo.grid.ColumnModel( [
37270         {
37271             xtype: 'ColumnModel',
37272             xns: Roo.grid,
37273             dataIndex : 'weekday0',
37274             header : 'Sunday',
37275             renderer : cellRender
37276         },
37277         {
37278             xtype: 'ColumnModel',
37279             xns: Roo.grid,
37280             dataIndex : 'weekday1',
37281             header : 'Monday',
37282             renderer : cellRender
37283         },
37284         {
37285             xtype: 'ColumnModel',
37286             xns: Roo.grid,
37287             dataIndex : 'weekday2',
37288             header : 'Tuesday',
37289             renderer : cellRender
37290         },
37291         {
37292             xtype: 'ColumnModel',
37293             xns: Roo.grid,
37294             dataIndex : 'weekday3',
37295             header : 'Wednesday',
37296             renderer : cellRender
37297         },
37298         {
37299             xtype: 'ColumnModel',
37300             xns: Roo.grid,
37301             dataIndex : 'weekday4',
37302             header : 'Thursday',
37303             renderer : cellRender
37304         },
37305         {
37306             xtype: 'ColumnModel',
37307             xns: Roo.grid,
37308             dataIndex : 'weekday5',
37309             header : 'Friday',
37310             renderer : cellRender
37311         },
37312         {
37313             xtype: 'ColumnModel',
37314             xns: Roo.grid,
37315             dataIndex : 'weekday6',
37316             header : 'Saturday',
37317             renderer : cellRender
37318         }
37319     ]);
37320     this.cm = this.colModel;
37321     this.cm.xmodule = this.xmodule || false;
37322  
37323         
37324           
37325     //this.selModel = new Roo.grid.CellSelectionModel();
37326     //this.sm = this.selModel;
37327     //this.selModel.init(this);
37328     
37329     
37330     if(this.width){
37331         this.container.setWidth(this.width);
37332     }
37333
37334     if(this.height){
37335         this.container.setHeight(this.height);
37336     }
37337     /** @private */
37338         this.addEvents({
37339         // raw events
37340         /**
37341          * @event click
37342          * The raw click event for the entire grid.
37343          * @param {Roo.EventObject} e
37344          */
37345         "click" : true,
37346         /**
37347          * @event dblclick
37348          * The raw dblclick event for the entire grid.
37349          * @param {Roo.EventObject} e
37350          */
37351         "dblclick" : true,
37352         /**
37353          * @event contextmenu
37354          * The raw contextmenu event for the entire grid.
37355          * @param {Roo.EventObject} e
37356          */
37357         "contextmenu" : true,
37358         /**
37359          * @event mousedown
37360          * The raw mousedown event for the entire grid.
37361          * @param {Roo.EventObject} e
37362          */
37363         "mousedown" : true,
37364         /**
37365          * @event mouseup
37366          * The raw mouseup event for the entire grid.
37367          * @param {Roo.EventObject} e
37368          */
37369         "mouseup" : true,
37370         /**
37371          * @event mouseover
37372          * The raw mouseover event for the entire grid.
37373          * @param {Roo.EventObject} e
37374          */
37375         "mouseover" : true,
37376         /**
37377          * @event mouseout
37378          * The raw mouseout event for the entire grid.
37379          * @param {Roo.EventObject} e
37380          */
37381         "mouseout" : true,
37382         /**
37383          * @event keypress
37384          * The raw keypress event for the entire grid.
37385          * @param {Roo.EventObject} e
37386          */
37387         "keypress" : true,
37388         /**
37389          * @event keydown
37390          * The raw keydown event for the entire grid.
37391          * @param {Roo.EventObject} e
37392          */
37393         "keydown" : true,
37394
37395         // custom events
37396
37397         /**
37398          * @event cellclick
37399          * Fires when a cell is clicked
37400          * @param {Grid} this
37401          * @param {Number} rowIndex
37402          * @param {Number} columnIndex
37403          * @param {Roo.EventObject} e
37404          */
37405         "cellclick" : true,
37406         /**
37407          * @event celldblclick
37408          * Fires when a cell is double clicked
37409          * @param {Grid} this
37410          * @param {Number} rowIndex
37411          * @param {Number} columnIndex
37412          * @param {Roo.EventObject} e
37413          */
37414         "celldblclick" : true,
37415         /**
37416          * @event rowclick
37417          * Fires when a row is clicked
37418          * @param {Grid} this
37419          * @param {Number} rowIndex
37420          * @param {Roo.EventObject} e
37421          */
37422         "rowclick" : true,
37423         /**
37424          * @event rowdblclick
37425          * Fires when a row is double clicked
37426          * @param {Grid} this
37427          * @param {Number} rowIndex
37428          * @param {Roo.EventObject} e
37429          */
37430         "rowdblclick" : true,
37431         /**
37432          * @event headerclick
37433          * Fires when a header is clicked
37434          * @param {Grid} this
37435          * @param {Number} columnIndex
37436          * @param {Roo.EventObject} e
37437          */
37438         "headerclick" : true,
37439         /**
37440          * @event headerdblclick
37441          * Fires when a header cell is double clicked
37442          * @param {Grid} this
37443          * @param {Number} columnIndex
37444          * @param {Roo.EventObject} e
37445          */
37446         "headerdblclick" : true,
37447         /**
37448          * @event rowcontextmenu
37449          * Fires when a row is right clicked
37450          * @param {Grid} this
37451          * @param {Number} rowIndex
37452          * @param {Roo.EventObject} e
37453          */
37454         "rowcontextmenu" : true,
37455         /**
37456          * @event cellcontextmenu
37457          * Fires when a cell is right clicked
37458          * @param {Grid} this
37459          * @param {Number} rowIndex
37460          * @param {Number} cellIndex
37461          * @param {Roo.EventObject} e
37462          */
37463          "cellcontextmenu" : true,
37464         /**
37465          * @event headercontextmenu
37466          * Fires when a header is right clicked
37467          * @param {Grid} this
37468          * @param {Number} columnIndex
37469          * @param {Roo.EventObject} e
37470          */
37471         "headercontextmenu" : true,
37472         /**
37473          * @event bodyscroll
37474          * Fires when the body element is scrolled
37475          * @param {Number} scrollLeft
37476          * @param {Number} scrollTop
37477          */
37478         "bodyscroll" : true,
37479         /**
37480          * @event columnresize
37481          * Fires when the user resizes a column
37482          * @param {Number} columnIndex
37483          * @param {Number} newSize
37484          */
37485         "columnresize" : true,
37486         /**
37487          * @event columnmove
37488          * Fires when the user moves a column
37489          * @param {Number} oldIndex
37490          * @param {Number} newIndex
37491          */
37492         "columnmove" : true,
37493         /**
37494          * @event startdrag
37495          * Fires when row(s) start being dragged
37496          * @param {Grid} this
37497          * @param {Roo.GridDD} dd The drag drop object
37498          * @param {event} e The raw browser event
37499          */
37500         "startdrag" : true,
37501         /**
37502          * @event enddrag
37503          * Fires when a drag operation is complete
37504          * @param {Grid} this
37505          * @param {Roo.GridDD} dd The drag drop object
37506          * @param {event} e The raw browser event
37507          */
37508         "enddrag" : true,
37509         /**
37510          * @event dragdrop
37511          * Fires when dragged row(s) are dropped on a valid DD target
37512          * @param {Grid} this
37513          * @param {Roo.GridDD} dd The drag drop object
37514          * @param {String} targetId The target drag drop object
37515          * @param {event} e The raw browser event
37516          */
37517         "dragdrop" : true,
37518         /**
37519          * @event dragover
37520          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37521          * @param {Grid} this
37522          * @param {Roo.GridDD} dd The drag drop object
37523          * @param {String} targetId The target drag drop object
37524          * @param {event} e The raw browser event
37525          */
37526         "dragover" : true,
37527         /**
37528          * @event dragenter
37529          *  Fires when the dragged row(s) first cross another DD target while being dragged
37530          * @param {Grid} this
37531          * @param {Roo.GridDD} dd The drag drop object
37532          * @param {String} targetId The target drag drop object
37533          * @param {event} e The raw browser event
37534          */
37535         "dragenter" : true,
37536         /**
37537          * @event dragout
37538          * Fires when the dragged row(s) leave another DD target while being dragged
37539          * @param {Grid} this
37540          * @param {Roo.GridDD} dd The drag drop object
37541          * @param {String} targetId The target drag drop object
37542          * @param {event} e The raw browser event
37543          */
37544         "dragout" : true,
37545         /**
37546          * @event rowclass
37547          * Fires when a row is rendered, so you can change add a style to it.
37548          * @param {GridView} gridview   The grid view
37549          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37550          */
37551         'rowclass' : true,
37552
37553         /**
37554          * @event render
37555          * Fires when the grid is rendered
37556          * @param {Grid} grid
37557          */
37558         'render' : true,
37559             /**
37560              * @event select
37561              * Fires when a date is selected
37562              * @param {DatePicker} this
37563              * @param {Date} date The selected date
37564              */
37565         'select': true,
37566         /**
37567              * @event monthchange
37568              * Fires when the displayed month changes 
37569              * @param {DatePicker} this
37570              * @param {Date} date The selected month
37571              */
37572         'monthchange': true,
37573         /**
37574              * @event evententer
37575              * Fires when mouse over an event
37576              * @param {Calendar} this
37577              * @param {event} Event
37578              */
37579         'evententer': true,
37580         /**
37581              * @event eventleave
37582              * Fires when the mouse leaves an
37583              * @param {Calendar} this
37584              * @param {event}
37585              */
37586         'eventleave': true,
37587         /**
37588              * @event eventclick
37589              * Fires when the mouse click an
37590              * @param {Calendar} this
37591              * @param {event}
37592              */
37593         'eventclick': true,
37594         /**
37595              * @event eventrender
37596              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37597              * @param {Calendar} this
37598              * @param {data} data to be modified
37599              */
37600         'eventrender': true
37601         
37602     });
37603
37604     Roo.grid.Grid.superclass.constructor.call(this);
37605     this.on('render', function() {
37606         this.view.el.addClass('x-grid-cal'); 
37607         
37608         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37609
37610     },this);
37611     
37612     if (!Roo.grid.Calendar.style) {
37613         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37614             
37615             
37616             '.x-grid-cal .x-grid-col' :  {
37617                 height: 'auto !important',
37618                 'vertical-align': 'top'
37619             },
37620             '.x-grid-cal  .fc-event-hori' : {
37621                 height: '14px'
37622             }
37623              
37624             
37625         }, Roo.id());
37626     }
37627
37628     
37629     
37630 };
37631 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37632     /**
37633      * @cfg {Store} eventStore The store that loads events.
37634      */
37635     eventStore : 25,
37636
37637      
37638     activeDate : false,
37639     startDay : 0,
37640     autoWidth : true,
37641     monitorWindowResize : false,
37642
37643     
37644     resizeColumns : function() {
37645         var col = (this.view.el.getWidth() / 7) - 3;
37646         // loop through cols, and setWidth
37647         for(var i =0 ; i < 7 ; i++){
37648             this.cm.setColumnWidth(i, col);
37649         }
37650     },
37651      setDate :function(date) {
37652         
37653         Roo.log('setDate?');
37654         
37655         this.resizeColumns();
37656         var vd = this.activeDate;
37657         this.activeDate = date;
37658 //        if(vd && this.el){
37659 //            var t = date.getTime();
37660 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37661 //                Roo.log('using add remove');
37662 //                
37663 //                this.fireEvent('monthchange', this, date);
37664 //                
37665 //                this.cells.removeClass("fc-state-highlight");
37666 //                this.cells.each(function(c){
37667 //                   if(c.dateValue == t){
37668 //                       c.addClass("fc-state-highlight");
37669 //                       setTimeout(function(){
37670 //                            try{c.dom.firstChild.focus();}catch(e){}
37671 //                       }, 50);
37672 //                       return false;
37673 //                   }
37674 //                   return true;
37675 //                });
37676 //                return;
37677 //            }
37678 //        }
37679         
37680         var days = date.getDaysInMonth();
37681         
37682         var firstOfMonth = date.getFirstDateOfMonth();
37683         var startingPos = firstOfMonth.getDay()-this.startDay;
37684         
37685         if(startingPos < this.startDay){
37686             startingPos += 7;
37687         }
37688         
37689         var pm = date.add(Date.MONTH, -1);
37690         var prevStart = pm.getDaysInMonth()-startingPos;
37691 //        
37692         
37693         
37694         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37695         
37696         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37697         //this.cells.addClassOnOver('fc-state-hover');
37698         
37699         var cells = this.cells.elements;
37700         var textEls = this.textNodes;
37701         
37702         //Roo.each(cells, function(cell){
37703         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37704         //});
37705         
37706         days += startingPos;
37707
37708         // convert everything to numbers so it's fast
37709         var day = 86400000;
37710         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37711         //Roo.log(d);
37712         //Roo.log(pm);
37713         //Roo.log(prevStart);
37714         
37715         var today = new Date().clearTime().getTime();
37716         var sel = date.clearTime().getTime();
37717         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37718         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37719         var ddMatch = this.disabledDatesRE;
37720         var ddText = this.disabledDatesText;
37721         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37722         var ddaysText = this.disabledDaysText;
37723         var format = this.format;
37724         
37725         var setCellClass = function(cal, cell){
37726             
37727             //Roo.log('set Cell Class');
37728             cell.title = "";
37729             var t = d.getTime();
37730             
37731             //Roo.log(d);
37732             
37733             
37734             cell.dateValue = t;
37735             if(t == today){
37736                 cell.className += " fc-today";
37737                 cell.className += " fc-state-highlight";
37738                 cell.title = cal.todayText;
37739             }
37740             if(t == sel){
37741                 // disable highlight in other month..
37742                 cell.className += " fc-state-highlight";
37743                 
37744             }
37745             // disabling
37746             if(t < min) {
37747                 //cell.className = " fc-state-disabled";
37748                 cell.title = cal.minText;
37749                 return;
37750             }
37751             if(t > max) {
37752                 //cell.className = " fc-state-disabled";
37753                 cell.title = cal.maxText;
37754                 return;
37755             }
37756             if(ddays){
37757                 if(ddays.indexOf(d.getDay()) != -1){
37758                     // cell.title = ddaysText;
37759                    // cell.className = " fc-state-disabled";
37760                 }
37761             }
37762             if(ddMatch && format){
37763                 var fvalue = d.dateFormat(format);
37764                 if(ddMatch.test(fvalue)){
37765                     cell.title = ddText.replace("%0", fvalue);
37766                    cell.className = " fc-state-disabled";
37767                 }
37768             }
37769             
37770             if (!cell.initialClassName) {
37771                 cell.initialClassName = cell.dom.className;
37772             }
37773             
37774             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37775         };
37776
37777         var i = 0;
37778         
37779         for(; i < startingPos; i++) {
37780             cells[i].dayName =  (++prevStart);
37781             Roo.log(textEls[i]);
37782             d.setDate(d.getDate()+1);
37783             
37784             //cells[i].className = "fc-past fc-other-month";
37785             setCellClass(this, cells[i]);
37786         }
37787         
37788         var intDay = 0;
37789         
37790         for(; i < days; i++){
37791             intDay = i - startingPos + 1;
37792             cells[i].dayName =  (intDay);
37793             d.setDate(d.getDate()+1);
37794             
37795             cells[i].className = ''; // "x-date-active";
37796             setCellClass(this, cells[i]);
37797         }
37798         var extraDays = 0;
37799         
37800         for(; i < 42; i++) {
37801             //textEls[i].innerHTML = (++extraDays);
37802             
37803             d.setDate(d.getDate()+1);
37804             cells[i].dayName = (++extraDays);
37805             cells[i].className = "fc-future fc-other-month";
37806             setCellClass(this, cells[i]);
37807         }
37808         
37809         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37810         
37811         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37812         
37813         // this will cause all the cells to mis
37814         var rows= [];
37815         var i =0;
37816         for (var r = 0;r < 6;r++) {
37817             for (var c =0;c < 7;c++) {
37818                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37819             }    
37820         }
37821         
37822         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37823         for(i=0;i<cells.length;i++) {
37824             
37825             this.cells.elements[i].dayName = cells[i].dayName ;
37826             this.cells.elements[i].className = cells[i].className;
37827             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37828             this.cells.elements[i].title = cells[i].title ;
37829             this.cells.elements[i].dateValue = cells[i].dateValue ;
37830         }
37831         
37832         
37833         
37834         
37835         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37836         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37837         
37838         ////if(totalRows != 6){
37839             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37840            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37841        // }
37842         
37843         this.fireEvent('monthchange', this, date);
37844         
37845         
37846     },
37847  /**
37848      * Returns the grid's SelectionModel.
37849      * @return {SelectionModel}
37850      */
37851     getSelectionModel : function(){
37852         if(!this.selModel){
37853             this.selModel = new Roo.grid.CellSelectionModel();
37854         }
37855         return this.selModel;
37856     },
37857
37858     load: function() {
37859         this.eventStore.load()
37860         
37861         
37862         
37863     },
37864     
37865     findCell : function(dt) {
37866         dt = dt.clearTime().getTime();
37867         var ret = false;
37868         this.cells.each(function(c){
37869             //Roo.log("check " +c.dateValue + '?=' + dt);
37870             if(c.dateValue == dt){
37871                 ret = c;
37872                 return false;
37873             }
37874             return true;
37875         });
37876         
37877         return ret;
37878     },
37879     
37880     findCells : function(rec) {
37881         var s = rec.data.start_dt.clone().clearTime().getTime();
37882        // Roo.log(s);
37883         var e= rec.data.end_dt.clone().clearTime().getTime();
37884        // Roo.log(e);
37885         var ret = [];
37886         this.cells.each(function(c){
37887              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37888             
37889             if(c.dateValue > e){
37890                 return ;
37891             }
37892             if(c.dateValue < s){
37893                 return ;
37894             }
37895             ret.push(c);
37896         });
37897         
37898         return ret;    
37899     },
37900     
37901     findBestRow: function(cells)
37902     {
37903         var ret = 0;
37904         
37905         for (var i =0 ; i < cells.length;i++) {
37906             ret  = Math.max(cells[i].rows || 0,ret);
37907         }
37908         return ret;
37909         
37910     },
37911     
37912     
37913     addItem : function(rec)
37914     {
37915         // look for vertical location slot in
37916         var cells = this.findCells(rec);
37917         
37918         rec.row = this.findBestRow(cells);
37919         
37920         // work out the location.
37921         
37922         var crow = false;
37923         var rows = [];
37924         for(var i =0; i < cells.length; i++) {
37925             if (!crow) {
37926                 crow = {
37927                     start : cells[i],
37928                     end :  cells[i]
37929                 };
37930                 continue;
37931             }
37932             if (crow.start.getY() == cells[i].getY()) {
37933                 // on same row.
37934                 crow.end = cells[i];
37935                 continue;
37936             }
37937             // different row.
37938             rows.push(crow);
37939             crow = {
37940                 start: cells[i],
37941                 end : cells[i]
37942             };
37943             
37944         }
37945         
37946         rows.push(crow);
37947         rec.els = [];
37948         rec.rows = rows;
37949         rec.cells = cells;
37950         for (var i = 0; i < cells.length;i++) {
37951             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37952             
37953         }
37954         
37955         
37956     },
37957     
37958     clearEvents: function() {
37959         
37960         if (!this.eventStore.getCount()) {
37961             return;
37962         }
37963         // reset number of rows in cells.
37964         Roo.each(this.cells.elements, function(c){
37965             c.rows = 0;
37966         });
37967         
37968         this.eventStore.each(function(e) {
37969             this.clearEvent(e);
37970         },this);
37971         
37972     },
37973     
37974     clearEvent : function(ev)
37975     {
37976         if (ev.els) {
37977             Roo.each(ev.els, function(el) {
37978                 el.un('mouseenter' ,this.onEventEnter, this);
37979                 el.un('mouseleave' ,this.onEventLeave, this);
37980                 el.remove();
37981             },this);
37982             ev.els = [];
37983         }
37984     },
37985     
37986     
37987     renderEvent : function(ev,ctr) {
37988         if (!ctr) {
37989              ctr = this.view.el.select('.fc-event-container',true).first();
37990         }
37991         
37992          
37993         this.clearEvent(ev);
37994             //code
37995        
37996         
37997         
37998         ev.els = [];
37999         var cells = ev.cells;
38000         var rows = ev.rows;
38001         this.fireEvent('eventrender', this, ev);
38002         
38003         for(var i =0; i < rows.length; i++) {
38004             
38005             cls = '';
38006             if (i == 0) {
38007                 cls += ' fc-event-start';
38008             }
38009             if ((i+1) == rows.length) {
38010                 cls += ' fc-event-end';
38011             }
38012             
38013             //Roo.log(ev.data);
38014             // how many rows should it span..
38015             var cg = this.eventTmpl.append(ctr,Roo.apply({
38016                 fccls : cls
38017                 
38018             }, ev.data) , true);
38019             
38020             
38021             cg.on('mouseenter' ,this.onEventEnter, this, ev);
38022             cg.on('mouseleave' ,this.onEventLeave, this, ev);
38023             cg.on('click', this.onEventClick, this, ev);
38024             
38025             ev.els.push(cg);
38026             
38027             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38028             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38029             //Roo.log(cg);
38030              
38031             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
38032             cg.setWidth(ebox.right - sbox.x -2);
38033         }
38034     },
38035     
38036     renderEvents: function()
38037     {   
38038         // first make sure there is enough space..
38039         
38040         if (!this.eventTmpl) {
38041             this.eventTmpl = new Roo.Template(
38042                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
38043                     '<div class="fc-event-inner">' +
38044                         '<span class="fc-event-time">{time}</span>' +
38045                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38046                     '</div>' +
38047                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
38048                 '</div>'
38049             );
38050                 
38051         }
38052                
38053         
38054         
38055         this.cells.each(function(c) {
38056             //Roo.log(c.select('.fc-day-content div',true).first());
38057             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38058         });
38059         
38060         var ctr = this.view.el.select('.fc-event-container',true).first();
38061         
38062         var cls;
38063         this.eventStore.each(function(ev){
38064             
38065             this.renderEvent(ev);
38066              
38067              
38068         }, this);
38069         this.view.layout();
38070         
38071     },
38072     
38073     onEventEnter: function (e, el,event,d) {
38074         this.fireEvent('evententer', this, el, event);
38075     },
38076     
38077     onEventLeave: function (e, el,event,d) {
38078         this.fireEvent('eventleave', this, el, event);
38079     },
38080     
38081     onEventClick: function (e, el,event,d) {
38082         this.fireEvent('eventclick', this, el, event);
38083     },
38084     
38085     onMonthChange: function () {
38086         this.store.load();
38087     },
38088     
38089     onLoad: function () {
38090         
38091         //Roo.log('calendar onload');
38092 //         
38093         if(this.eventStore.getCount() > 0){
38094             
38095            
38096             
38097             this.eventStore.each(function(d){
38098                 
38099                 
38100                 // FIXME..
38101                 var add =   d.data;
38102                 if (typeof(add.end_dt) == 'undefined')  {
38103                     Roo.log("Missing End time in calendar data: ");
38104                     Roo.log(d);
38105                     return;
38106                 }
38107                 if (typeof(add.start_dt) == 'undefined')  {
38108                     Roo.log("Missing Start time in calendar data: ");
38109                     Roo.log(d);
38110                     return;
38111                 }
38112                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38113                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38114                 add.id = add.id || d.id;
38115                 add.title = add.title || '??';
38116                 
38117                 this.addItem(d);
38118                 
38119              
38120             },this);
38121         }
38122         
38123         this.renderEvents();
38124     }
38125     
38126
38127 });
38128 /*
38129  grid : {
38130                 xtype: 'Grid',
38131                 xns: Roo.grid,
38132                 listeners : {
38133                     render : function ()
38134                     {
38135                         _this.grid = this;
38136                         
38137                         if (!this.view.el.hasClass('course-timesheet')) {
38138                             this.view.el.addClass('course-timesheet');
38139                         }
38140                         if (this.tsStyle) {
38141                             this.ds.load({});
38142                             return; 
38143                         }
38144                         Roo.log('width');
38145                         Roo.log(_this.grid.view.el.getWidth());
38146                         
38147                         
38148                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38149                             '.course-timesheet .x-grid-row' : {
38150                                 height: '80px'
38151                             },
38152                             '.x-grid-row td' : {
38153                                 'vertical-align' : 0
38154                             },
38155                             '.course-edit-link' : {
38156                                 'color' : 'blue',
38157                                 'text-overflow' : 'ellipsis',
38158                                 'overflow' : 'hidden',
38159                                 'white-space' : 'nowrap',
38160                                 'cursor' : 'pointer'
38161                             },
38162                             '.sub-link' : {
38163                                 'color' : 'green'
38164                             },
38165                             '.de-act-sup-link' : {
38166                                 'color' : 'purple',
38167                                 'text-decoration' : 'line-through'
38168                             },
38169                             '.de-act-link' : {
38170                                 'color' : 'red',
38171                                 'text-decoration' : 'line-through'
38172                             },
38173                             '.course-timesheet .course-highlight' : {
38174                                 'border-top-style': 'dashed !important',
38175                                 'border-bottom-bottom': 'dashed !important'
38176                             },
38177                             '.course-timesheet .course-item' : {
38178                                 'font-family'   : 'tahoma, arial, helvetica',
38179                                 'font-size'     : '11px',
38180                                 'overflow'      : 'hidden',
38181                                 'padding-left'  : '10px',
38182                                 'padding-right' : '10px',
38183                                 'padding-top' : '10px' 
38184                             }
38185                             
38186                         }, Roo.id());
38187                                 this.ds.load({});
38188                     }
38189                 },
38190                 autoWidth : true,
38191                 monitorWindowResize : false,
38192                 cellrenderer : function(v,x,r)
38193                 {
38194                     return v;
38195                 },
38196                 sm : {
38197                     xtype: 'CellSelectionModel',
38198                     xns: Roo.grid
38199                 },
38200                 dataSource : {
38201                     xtype: 'Store',
38202                     xns: Roo.data,
38203                     listeners : {
38204                         beforeload : function (_self, options)
38205                         {
38206                             options.params = options.params || {};
38207                             options.params._month = _this.monthField.getValue();
38208                             options.params.limit = 9999;
38209                             options.params['sort'] = 'when_dt';    
38210                             options.params['dir'] = 'ASC';    
38211                             this.proxy.loadResponse = this.loadResponse;
38212                             Roo.log("load?");
38213                             //this.addColumns();
38214                         },
38215                         load : function (_self, records, options)
38216                         {
38217                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38218                                 // if you click on the translation.. you can edit it...
38219                                 var el = Roo.get(this);
38220                                 var id = el.dom.getAttribute('data-id');
38221                                 var d = el.dom.getAttribute('data-date');
38222                                 var t = el.dom.getAttribute('data-time');
38223                                 //var id = this.child('span').dom.textContent;
38224                                 
38225                                 //Roo.log(this);
38226                                 Pman.Dialog.CourseCalendar.show({
38227                                     id : id,
38228                                     when_d : d,
38229                                     when_t : t,
38230                                     productitem_active : id ? 1 : 0
38231                                 }, function() {
38232                                     _this.grid.ds.load({});
38233                                 });
38234                            
38235                            });
38236                            
38237                            _this.panel.fireEvent('resize', [ '', '' ]);
38238                         }
38239                     },
38240                     loadResponse : function(o, success, response){
38241                             // this is overridden on before load..
38242                             
38243                             Roo.log("our code?");       
38244                             //Roo.log(success);
38245                             //Roo.log(response)
38246                             delete this.activeRequest;
38247                             if(!success){
38248                                 this.fireEvent("loadexception", this, o, response);
38249                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38250                                 return;
38251                             }
38252                             var result;
38253                             try {
38254                                 result = o.reader.read(response);
38255                             }catch(e){
38256                                 Roo.log("load exception?");
38257                                 this.fireEvent("loadexception", this, o, response, e);
38258                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38259                                 return;
38260                             }
38261                             Roo.log("ready...");        
38262                             // loop through result.records;
38263                             // and set this.tdate[date] = [] << array of records..
38264                             _this.tdata  = {};
38265                             Roo.each(result.records, function(r){
38266                                 //Roo.log(r.data);
38267                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38268                                     _this.tdata[r.data.when_dt.format('j')] = [];
38269                                 }
38270                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38271                             });
38272                             
38273                             //Roo.log(_this.tdata);
38274                             
38275                             result.records = [];
38276                             result.totalRecords = 6;
38277                     
38278                             // let's generate some duumy records for the rows.
38279                             //var st = _this.dateField.getValue();
38280                             
38281                             // work out monday..
38282                             //st = st.add(Date.DAY, -1 * st.format('w'));
38283                             
38284                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38285                             
38286                             var firstOfMonth = date.getFirstDayOfMonth();
38287                             var days = date.getDaysInMonth();
38288                             var d = 1;
38289                             var firstAdded = false;
38290                             for (var i = 0; i < result.totalRecords ; i++) {
38291                                 //var d= st.add(Date.DAY, i);
38292                                 var row = {};
38293                                 var added = 0;
38294                                 for(var w = 0 ; w < 7 ; w++){
38295                                     if(!firstAdded && firstOfMonth != w){
38296                                         continue;
38297                                     }
38298                                     if(d > days){
38299                                         continue;
38300                                     }
38301                                     firstAdded = true;
38302                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38303                                     row['weekday'+w] = String.format(
38304                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38305                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38306                                                     d,
38307                                                     date.format('Y-m-')+dd
38308                                                 );
38309                                     added++;
38310                                     if(typeof(_this.tdata[d]) != 'undefined'){
38311                                         Roo.each(_this.tdata[d], function(r){
38312                                             var is_sub = '';
38313                                             var deactive = '';
38314                                             var id = r.id;
38315                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38316                                             if(r.parent_id*1>0){
38317                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38318                                                 id = r.parent_id;
38319                                             }
38320                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38321                                                 deactive = 'de-act-link';
38322                                             }
38323                                             
38324                                             row['weekday'+w] += String.format(
38325                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38326                                                     id, //0
38327                                                     r.product_id_name, //1
38328                                                     r.when_dt.format('h:ia'), //2
38329                                                     is_sub, //3
38330                                                     deactive, //4
38331                                                     desc // 5
38332                                             );
38333                                         });
38334                                     }
38335                                     d++;
38336                                 }
38337                                 
38338                                 // only do this if something added..
38339                                 if(added > 0){ 
38340                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38341                                 }
38342                                 
38343                                 
38344                                 // push it twice. (second one with an hour..
38345                                 
38346                             }
38347                             //Roo.log(result);
38348                             this.fireEvent("load", this, o, o.request.arg);
38349                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38350                         },
38351                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38352                     proxy : {
38353                         xtype: 'HttpProxy',
38354                         xns: Roo.data,
38355                         method : 'GET',
38356                         url : baseURL + '/Roo/Shop_course.php'
38357                     },
38358                     reader : {
38359                         xtype: 'JsonReader',
38360                         xns: Roo.data,
38361                         id : 'id',
38362                         fields : [
38363                             {
38364                                 'name': 'id',
38365                                 'type': 'int'
38366                             },
38367                             {
38368                                 'name': 'when_dt',
38369                                 'type': 'string'
38370                             },
38371                             {
38372                                 'name': 'end_dt',
38373                                 'type': 'string'
38374                             },
38375                             {
38376                                 'name': 'parent_id',
38377                                 'type': 'int'
38378                             },
38379                             {
38380                                 'name': 'product_id',
38381                                 'type': 'int'
38382                             },
38383                             {
38384                                 'name': 'productitem_id',
38385                                 'type': 'int'
38386                             },
38387                             {
38388                                 'name': 'guid',
38389                                 'type': 'int'
38390                             }
38391                         ]
38392                     }
38393                 },
38394                 toolbar : {
38395                     xtype: 'Toolbar',
38396                     xns: Roo,
38397                     items : [
38398                         {
38399                             xtype: 'Button',
38400                             xns: Roo.Toolbar,
38401                             listeners : {
38402                                 click : function (_self, e)
38403                                 {
38404                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38405                                     sd.setMonth(sd.getMonth()-1);
38406                                     _this.monthField.setValue(sd.format('Y-m-d'));
38407                                     _this.grid.ds.load({});
38408                                 }
38409                             },
38410                             text : "Back"
38411                         },
38412                         {
38413                             xtype: 'Separator',
38414                             xns: Roo.Toolbar
38415                         },
38416                         {
38417                             xtype: 'MonthField',
38418                             xns: Roo.form,
38419                             listeners : {
38420                                 render : function (_self)
38421                                 {
38422                                     _this.monthField = _self;
38423                                    // _this.monthField.set  today
38424                                 },
38425                                 select : function (combo, date)
38426                                 {
38427                                     _this.grid.ds.load({});
38428                                 }
38429                             },
38430                             value : (function() { return new Date(); })()
38431                         },
38432                         {
38433                             xtype: 'Separator',
38434                             xns: Roo.Toolbar
38435                         },
38436                         {
38437                             xtype: 'TextItem',
38438                             xns: Roo.Toolbar,
38439                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38440                         },
38441                         {
38442                             xtype: 'Fill',
38443                             xns: Roo.Toolbar
38444                         },
38445                         {
38446                             xtype: 'Button',
38447                             xns: Roo.Toolbar,
38448                             listeners : {
38449                                 click : function (_self, e)
38450                                 {
38451                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38452                                     sd.setMonth(sd.getMonth()+1);
38453                                     _this.monthField.setValue(sd.format('Y-m-d'));
38454                                     _this.grid.ds.load({});
38455                                 }
38456                             },
38457                             text : "Next"
38458                         }
38459                     ]
38460                 },
38461                  
38462             }
38463         };
38464         
38465         *//*
38466  * Based on:
38467  * Ext JS Library 1.1.1
38468  * Copyright(c) 2006-2007, Ext JS, LLC.
38469  *
38470  * Originally Released Under LGPL - original licence link has changed is not relivant.
38471  *
38472  * Fork - LGPL
38473  * <script type="text/javascript">
38474  */
38475  
38476 /**
38477  * @class Roo.LoadMask
38478  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38479  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38480  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38481  * element's UpdateManager load indicator and will be destroyed after the initial load.
38482  * @constructor
38483  * Create a new LoadMask
38484  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38485  * @param {Object} config The config object
38486  */
38487 Roo.LoadMask = function(el, config){
38488     this.el = Roo.get(el);
38489     Roo.apply(this, config);
38490     if(this.store){
38491         this.store.on('beforeload', this.onBeforeLoad, this);
38492         this.store.on('load', this.onLoad, this);
38493         this.store.on('loadexception', this.onLoadException, this);
38494         this.removeMask = false;
38495     }else{
38496         var um = this.el.getUpdateManager();
38497         um.showLoadIndicator = false; // disable the default indicator
38498         um.on('beforeupdate', this.onBeforeLoad, this);
38499         um.on('update', this.onLoad, this);
38500         um.on('failure', this.onLoad, this);
38501         this.removeMask = true;
38502     }
38503 };
38504
38505 Roo.LoadMask.prototype = {
38506     /**
38507      * @cfg {Boolean} removeMask
38508      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38509      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38510      */
38511     removeMask : false,
38512     /**
38513      * @cfg {String} msg
38514      * The text to display in a centered loading message box (defaults to 'Loading...')
38515      */
38516     msg : 'Loading...',
38517     /**
38518      * @cfg {String} msgCls
38519      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38520      */
38521     msgCls : 'x-mask-loading',
38522
38523     /**
38524      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38525      * @type Boolean
38526      */
38527     disabled: false,
38528
38529     /**
38530      * Disables the mask to prevent it from being displayed
38531      */
38532     disable : function(){
38533        this.disabled = true;
38534     },
38535
38536     /**
38537      * Enables the mask so that it can be displayed
38538      */
38539     enable : function(){
38540         this.disabled = false;
38541     },
38542     
38543     onLoadException : function()
38544     {
38545         Roo.log(arguments);
38546         
38547         if (typeof(arguments[3]) != 'undefined') {
38548             Roo.MessageBox.alert("Error loading",arguments[3]);
38549         } 
38550         /*
38551         try {
38552             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38553                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38554             }   
38555         } catch(e) {
38556             
38557         }
38558         */
38559     
38560         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38561     },
38562     // private
38563     onLoad : function()
38564     {
38565         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38566     },
38567
38568     // private
38569     onBeforeLoad : function(){
38570         if(!this.disabled){
38571             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38572         }
38573     },
38574
38575     // private
38576     destroy : function(){
38577         if(this.store){
38578             this.store.un('beforeload', this.onBeforeLoad, this);
38579             this.store.un('load', this.onLoad, this);
38580             this.store.un('loadexception', this.onLoadException, this);
38581         }else{
38582             var um = this.el.getUpdateManager();
38583             um.un('beforeupdate', this.onBeforeLoad, this);
38584             um.un('update', this.onLoad, this);
38585             um.un('failure', this.onLoad, this);
38586         }
38587     }
38588 };/*
38589  * Based on:
38590  * Ext JS Library 1.1.1
38591  * Copyright(c) 2006-2007, Ext JS, LLC.
38592  *
38593  * Originally Released Under LGPL - original licence link has changed is not relivant.
38594  *
38595  * Fork - LGPL
38596  * <script type="text/javascript">
38597  */
38598
38599
38600 /**
38601  * @class Roo.XTemplate
38602  * @extends Roo.Template
38603  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38604 <pre><code>
38605 var t = new Roo.XTemplate(
38606         '&lt;select name="{name}"&gt;',
38607                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38608         '&lt;/select&gt;'
38609 );
38610  
38611 // then append, applying the master template values
38612  </code></pre>
38613  *
38614  * Supported features:
38615  *
38616  *  Tags:
38617
38618 <pre><code>
38619       {a_variable} - output encoded.
38620       {a_variable.format:("Y-m-d")} - call a method on the variable
38621       {a_variable:raw} - unencoded output
38622       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38623       {a_variable:this.method_on_template(...)} - call a method on the template object.
38624  
38625 </code></pre>
38626  *  The tpl tag:
38627 <pre><code>
38628         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38629         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38630         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38631         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38632   
38633         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38634         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38635 </code></pre>
38636  *      
38637  */
38638 Roo.XTemplate = function()
38639 {
38640     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38641     if (this.html) {
38642         this.compile();
38643     }
38644 };
38645
38646
38647 Roo.extend(Roo.XTemplate, Roo.Template, {
38648
38649     /**
38650      * The various sub templates
38651      */
38652     tpls : false,
38653     /**
38654      *
38655      * basic tag replacing syntax
38656      * WORD:WORD()
38657      *
38658      * // you can fake an object call by doing this
38659      *  x.t:(test,tesT) 
38660      * 
38661      */
38662     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38663
38664     /**
38665      * compile the template
38666      *
38667      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38668      *
38669      */
38670     compile: function()
38671     {
38672         var s = this.html;
38673      
38674         s = ['<tpl>', s, '</tpl>'].join('');
38675     
38676         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38677             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38678             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38679             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38680             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38681             m,
38682             id     = 0,
38683             tpls   = [];
38684     
38685         while(true == !!(m = s.match(re))){
38686             var forMatch   = m[0].match(nameRe),
38687                 ifMatch   = m[0].match(ifRe),
38688                 execMatch   = m[0].match(execRe),
38689                 namedMatch   = m[0].match(namedRe),
38690                 
38691                 exp  = null, 
38692                 fn   = null,
38693                 exec = null,
38694                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38695                 
38696             if (ifMatch) {
38697                 // if - puts fn into test..
38698                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38699                 if(exp){
38700                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38701                 }
38702             }
38703             
38704             if (execMatch) {
38705                 // exec - calls a function... returns empty if true is  returned.
38706                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38707                 if(exp){
38708                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38709                 }
38710             }
38711             
38712             
38713             if (name) {
38714                 // for = 
38715                 switch(name){
38716                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38717                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38718                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38719                 }
38720             }
38721             var uid = namedMatch ? namedMatch[1] : id;
38722             
38723             
38724             tpls.push({
38725                 id:     namedMatch ? namedMatch[1] : id,
38726                 target: name,
38727                 exec:   exec,
38728                 test:   fn,
38729                 body:   m[1] || ''
38730             });
38731             if (namedMatch) {
38732                 s = s.replace(m[0], '');
38733             } else { 
38734                 s = s.replace(m[0], '{xtpl'+ id + '}');
38735             }
38736             ++id;
38737         }
38738         this.tpls = [];
38739         for(var i = tpls.length-1; i >= 0; --i){
38740             this.compileTpl(tpls[i]);
38741             this.tpls[tpls[i].id] = tpls[i];
38742         }
38743         this.master = tpls[tpls.length-1];
38744         return this;
38745     },
38746     /**
38747      * same as applyTemplate, except it's done to one of the subTemplates
38748      * when using named templates, you can do:
38749      *
38750      * var str = pl.applySubTemplate('your-name', values);
38751      *
38752      * 
38753      * @param {Number} id of the template
38754      * @param {Object} values to apply to template
38755      * @param {Object} parent (normaly the instance of this object)
38756      */
38757     applySubTemplate : function(id, values, parent)
38758     {
38759         
38760         
38761         var t = this.tpls[id];
38762         
38763         
38764         try { 
38765             if(t.test && !t.test.call(this, values, parent)){
38766                 return '';
38767             }
38768         } catch(e) {
38769             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38770             Roo.log(e.toString());
38771             Roo.log(t.test);
38772             return ''
38773         }
38774         try { 
38775             
38776             if(t.exec && t.exec.call(this, values, parent)){
38777                 return '';
38778             }
38779         } catch(e) {
38780             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38781             Roo.log(e.toString());
38782             Roo.log(t.exec);
38783             return ''
38784         }
38785         try {
38786             var vs = t.target ? t.target.call(this, values, parent) : values;
38787             parent = t.target ? values : parent;
38788             if(t.target && vs instanceof Array){
38789                 var buf = [];
38790                 for(var i = 0, len = vs.length; i < len; i++){
38791                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38792                 }
38793                 return buf.join('');
38794             }
38795             return t.compiled.call(this, vs, parent);
38796         } catch (e) {
38797             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38798             Roo.log(e.toString());
38799             Roo.log(t.compiled);
38800             return '';
38801         }
38802     },
38803
38804     compileTpl : function(tpl)
38805     {
38806         var fm = Roo.util.Format;
38807         var useF = this.disableFormats !== true;
38808         var sep = Roo.isGecko ? "+" : ",";
38809         var undef = function(str) {
38810             Roo.log("Property not found :"  + str);
38811             return '';
38812         };
38813         
38814         var fn = function(m, name, format, args)
38815         {
38816             //Roo.log(arguments);
38817             args = args ? args.replace(/\\'/g,"'") : args;
38818             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38819             if (typeof(format) == 'undefined') {
38820                 format= 'htmlEncode';
38821             }
38822             if (format == 'raw' ) {
38823                 format = false;
38824             }
38825             
38826             if(name.substr(0, 4) == 'xtpl'){
38827                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38828             }
38829             
38830             // build an array of options to determine if value is undefined..
38831             
38832             // basically get 'xxxx.yyyy' then do
38833             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38834             //    (function () { Roo.log("Property not found"); return ''; })() :
38835             //    ......
38836             
38837             var udef_ar = [];
38838             var lookfor = '';
38839             Roo.each(name.split('.'), function(st) {
38840                 lookfor += (lookfor.length ? '.': '') + st;
38841                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38842             });
38843             
38844             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38845             
38846             
38847             if(format && useF){
38848                 
38849                 args = args ? ',' + args : "";
38850                  
38851                 if(format.substr(0, 5) != "this."){
38852                     format = "fm." + format + '(';
38853                 }else{
38854                     format = 'this.call("'+ format.substr(5) + '", ';
38855                     args = ", values";
38856                 }
38857                 
38858                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38859             }
38860              
38861             if (args.length) {
38862                 // called with xxyx.yuu:(test,test)
38863                 // change to ()
38864                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38865             }
38866             // raw.. - :raw modifier..
38867             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38868             
38869         };
38870         var body;
38871         // branched to use + in gecko and [].join() in others
38872         if(Roo.isGecko){
38873             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38874                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38875                     "';};};";
38876         }else{
38877             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38878             body.push(tpl.body.replace(/(\r\n|\n)/g,
38879                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38880             body.push("'].join('');};};");
38881             body = body.join('');
38882         }
38883         
38884         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38885        
38886         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38887         eval(body);
38888         
38889         return this;
38890     },
38891
38892     applyTemplate : function(values){
38893         return this.master.compiled.call(this, values, {});
38894         //var s = this.subs;
38895     },
38896
38897     apply : function(){
38898         return this.applyTemplate.apply(this, arguments);
38899     }
38900
38901  });
38902
38903 Roo.XTemplate.from = function(el){
38904     el = Roo.getDom(el);
38905     return new Roo.XTemplate(el.value || el.innerHTML);
38906 };